1 /* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
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 St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 #include "mariadb.h"
17 #include "sql_priv.h"
18 
19 #ifdef HAVE_REPLICATION
20 
21 #include "rpl_tblmap.h"
22 #ifndef MYSQL_CLIENT
23 #include "table.h"
24 #endif
25 
26 #ifdef MYSQL_CLIENT
27 #define MAYBE_TABLE_NAME(T) ("")
28 #else
29 #define MAYBE_TABLE_NAME(T) ((T) ? (T)->s->table_name.str : "<>")
30 #endif
31 #define TABLE_ID_HASH_SIZE 32
32 #define TABLE_ID_CHUNK 256
33 
table_mapping()34 table_mapping::table_mapping()
35   : m_free(0)
36 {
37 #ifdef MYSQL_CLIENT
38   PSI_memory_key psi_key= PSI_NOT_INSTRUMENTED;
39 #else
40   PSI_memory_key psi_key= key_memory_table_mapping_root;
41 #endif
42 
43   DBUG_ENTER("table_mapping::table_mapping");
44   /*
45     No "free_element" function for entries passed here, as the entries are
46     allocated in a MEM_ROOT (freed as a whole in the destructor), they cannot
47     be freed one by one.
48     Note that below we don't test if my_hash_init() succeeded. This
49     constructor is called at startup only.
50   */
51   (void) my_hash_init(psi_key, &m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
52                       offsetof(entry,table_id),sizeof(ulonglong),
53                       0,0,0);
54   /* We don't preallocate any block, this is consistent with m_free=0 above */
55   init_alloc_root(psi_key, &m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0, MYF(0));
56   DBUG_VOID_RETURN;
57 }
58 
~table_mapping()59 table_mapping::~table_mapping()
60 {
61 #ifdef MYSQL_CLIENT
62   clear_tables();
63 #endif
64   my_hash_free(&m_table_ids);
65   free_root(&m_mem_root, MYF(0));
66 }
67 
get_table(ulonglong table_id)68 TABLE* table_mapping::get_table(ulonglong table_id)
69 {
70   DBUG_ENTER("table_mapping::get_table(ulong)");
71   DBUG_PRINT("enter", ("table_id: %llu", table_id));
72   entry *e= find_entry(table_id);
73   if (e)
74   {
75     DBUG_PRINT("info", ("tid %llu -> table %p (%s)",
76 			table_id, e->table,
77 			MAYBE_TABLE_NAME(e->table)));
78     DBUG_RETURN(e->table);
79   }
80 
81   DBUG_PRINT("info", ("tid %llu is not mapped!", table_id));
82   DBUG_RETURN(NULL);
83 }
84 
85 /*
86   Called when we are out of table id entries. Creates TABLE_ID_CHUNK
87   new entries, chain them and attach them at the head of the list of free
88   (free for use) entries.
89 */
expand()90 int table_mapping::expand()
91 {
92   /*
93     If we wanted to use "tmp= new (&m_mem_root) entry[TABLE_ID_CHUNK]",
94     we would have to make "entry" derive from Sql_alloc but then it would not
95     be a POD anymore and we want it to be (see rpl_tblmap.h). So we allocate
96     in C.
97   */
98   entry *tmp= (entry *)alloc_root(&m_mem_root, TABLE_ID_CHUNK*sizeof(entry));
99   if (tmp == NULL)
100     return ERR_MEMORY_ALLOCATION; // Memory allocation failed
101 
102   /* Find the end of this fresh new array of free entries */
103   entry *e_end= tmp+TABLE_ID_CHUNK-1;
104   for (entry *e= tmp; e < e_end; e++)
105     e->next= e+1;
106   e_end->next= m_free;
107   m_free= tmp;
108   return 0;
109 }
110 
set_table(ulonglong table_id,TABLE * table)111 int table_mapping::set_table(ulonglong table_id, TABLE* table)
112 {
113   DBUG_ENTER("table_mapping::set_table(ulong,TABLE*)");
114   DBUG_PRINT("enter", ("table_id: %llu  table: %p (%s)",
115 		       table_id,
116 		       table, MAYBE_TABLE_NAME(table)));
117   entry *e= find_entry(table_id);
118   if (e == 0)
119   {
120     if (m_free == 0 && expand())
121       DBUG_RETURN(ERR_MEMORY_ALLOCATION); // Memory allocation failed
122     e= m_free;
123     m_free= m_free->next;
124   }
125   else
126   {
127 #ifdef MYSQL_CLIENT
128     free_table_map_log_event(e->table);
129 #endif
130     my_hash_delete(&m_table_ids,(uchar *)e);
131   }
132   e->table_id= table_id;
133   e->table= table;
134   if (my_hash_insert(&m_table_ids,(uchar *)e))
135   {
136     /* we add this entry to the chain of free (free for use) entries */
137     e->next= m_free;
138     m_free= e;
139     DBUG_RETURN(ERR_MEMORY_ALLOCATION);
140   }
141 
142   DBUG_PRINT("info", ("tid %llu -> table %p (%s)",
143 		      table_id, e->table,
144 		      MAYBE_TABLE_NAME(e->table)));
145   DBUG_RETURN(0);		// All OK
146 }
147 
remove_table(ulonglong table_id)148 int table_mapping::remove_table(ulonglong table_id)
149 {
150   entry *e= find_entry(table_id);
151   if (e)
152   {
153     my_hash_delete(&m_table_ids,(uchar *)e);
154     /* we add this entry to the chain of free (free for use) entries */
155     e->next= m_free;
156     m_free= e;
157     return 0;			// All OK
158   }
159   return 1;			// No table to remove
160 }
161 
162 /*
163   Puts all entries into the list of free-for-use entries (does not free any
164   memory), and empties the hash.
165 */
clear_tables()166 void table_mapping::clear_tables()
167 {
168   DBUG_ENTER("table_mapping::clear_tables()");
169   for (uint i= 0; i < m_table_ids.records; i++)
170   {
171     entry *e= (entry *)my_hash_element(&m_table_ids, i);
172 #ifdef MYSQL_CLIENT
173     free_table_map_log_event(e->table);
174 #endif
175     e->next= m_free;
176     m_free= e;
177   }
178   my_hash_reset(&m_table_ids);
179   DBUG_VOID_RETURN;
180 }
181 
182 #endif
183