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