1 /* Copyright (c) 2005, 2015, 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, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 #ifdef HAVE_REPLICATION
24 #include "rpl_tblmap.h"
25 #ifndef MYSQL_CLIENT
26 #include "table.h" // TABLE
27 #endif
28
29 #ifdef MYSQL_CLIENT
30 #define MAYBE_TABLE_NAME(T) ("")
31 #else
32 #define MAYBE_TABLE_NAME(T) ((T) ? (T)->s->table_name.str : "<>")
33 #endif
34 #define TABLE_ID_HASH_SIZE 32
35 #define TABLE_ID_CHUNK 256
36
table_mapping()37 table_mapping::table_mapping()
38 : m_free(0)
39 {
40 PSI_memory_key psi_key;
41
42 #ifdef MYSQL_CLIENT
43 psi_key= PSI_NOT_INSTRUMENTED;
44 #else
45 psi_key= key_memory_table_mapping_root;
46 #endif
47
48 /*
49 No "free_element" function for entries passed here, as the entries are
50 allocated in a MEM_ROOT (freed as a whole in the destructor), they cannot
51 be freed one by one.
52 Note that below we don't test if my_hash_init() succeeded. This
53 constructor is called at startup only.
54 */
55 (void) my_hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
56 offsetof(entry,table_id),sizeof(ulonglong),
57 0, 0, 0, psi_key);
58 /* We don't preallocate any block, this is consistent with m_free=0 above */
59 init_alloc_root(psi_key,
60 &m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0);
61 }
62
~table_mapping()63 table_mapping::~table_mapping()
64 {
65 #ifdef MYSQL_CLIENT
66 clear_tables();
67 #endif
68 my_hash_free(&m_table_ids);
69 free_root(&m_mem_root, MYF(0));
70 }
71
get_table(ulonglong table_id)72 TABLE* table_mapping::get_table(ulonglong table_id)
73 {
74 DBUG_ENTER("table_mapping::get_table(ulonglong)");
75 DBUG_PRINT("enter", ("table_id: %llu", table_id));
76 entry *e= find_entry(table_id);
77 if (e)
78 {
79 DBUG_PRINT("info", ("tid %llu -> table 0x%lx (%s)",
80 table_id, (long) e->table,
81 MAYBE_TABLE_NAME(e->table)));
82 DBUG_RETURN(e->table);
83 }
84
85 DBUG_PRINT("info", ("tid %llu is not mapped!", table_id));
86 DBUG_RETURN(NULL);
87 }
88
89 /*
90 Called when we are out of table id entries. Creates TABLE_ID_CHUNK
91 new entries, chain them and attach them at the head of the list of free
92 (free for use) entries.
93 */
expand()94 int table_mapping::expand()
95 {
96 /*
97 If we wanted to use "tmp= new (&m_mem_root) entry[TABLE_ID_CHUNK]",
98 we would have to make "entry" derive from Sql_alloc but then it would not
99 be a POD anymore and we want it to be (see rpl_tblmap.h). So we allocate
100 in C.
101 */
102 entry *tmp= (entry *)alloc_root(&m_mem_root, TABLE_ID_CHUNK*sizeof(entry));
103 if (tmp == NULL)
104 return ERR_MEMORY_ALLOCATION; // Memory allocation failed
105
106 /* Find the end of this fresh new array of free entries */
107 entry *e_end= tmp+TABLE_ID_CHUNK-1;
108 for (entry *e= tmp; e < e_end; e++)
109 e->next= e+1;
110 e_end->next= m_free;
111 m_free= tmp;
112 return 0;
113 }
114
set_table(ulonglong table_id,TABLE * table)115 int table_mapping::set_table(ulonglong table_id, TABLE* table)
116 {
117 DBUG_ENTER("table_mapping::set_table(ulong,TABLE*)");
118 DBUG_PRINT("enter", ("table_id: %llu table: 0x%lx (%s)",
119 table_id,
120 (long) table, MAYBE_TABLE_NAME(table)));
121 entry *e= find_entry(table_id);
122 if (e == 0)
123 {
124 if (m_free == 0 && expand())
125 DBUG_RETURN(ERR_MEMORY_ALLOCATION); // Memory allocation failed
126 e= m_free;
127 m_free= m_free->next;
128 }
129 else
130 {
131 #ifdef MYSQL_CLIENT
132 free_table_map_log_event(e->table);
133 #endif
134 my_hash_delete(&m_table_ids,(uchar *)e);
135 }
136 e->table_id= table_id;
137 e->table= table;
138 if (my_hash_insert(&m_table_ids,(uchar *)e))
139 {
140 /* we add this entry to the chain of free (free for use) entries */
141 e->next= m_free;
142 m_free= e;
143 DBUG_RETURN(ERR_MEMORY_ALLOCATION);
144 }
145
146 DBUG_PRINT("info", ("tid %llu -> table 0x%lx (%s)",
147 table_id, (long) e->table,
148 MAYBE_TABLE_NAME(e->table)));
149 DBUG_RETURN(0); // All OK
150 }
151
remove_table(ulonglong table_id)152 int table_mapping::remove_table(ulonglong table_id)
153 {
154 entry *e= find_entry(table_id);
155 if (e)
156 {
157 my_hash_delete(&m_table_ids,(uchar *)e);
158 /* we add this entry to the chain of free (free for use) entries */
159 e->next= m_free;
160 m_free= e;
161 return 0; // All OK
162 }
163 return 1; // No table to remove
164 }
165
166 /*
167 Puts all entries into the list of free-for-use entries (does not free any
168 memory), and empties the hash.
169 */
clear_tables()170 void table_mapping::clear_tables()
171 {
172 DBUG_ENTER("table_mapping::clear_tables()");
173 for (uint i= 0; i < m_table_ids.records; i++)
174 {
175 entry *e= (entry *)my_hash_element(&m_table_ids, i);
176 #ifdef MYSQL_CLIENT
177 free_table_map_log_event(e->table);
178 #endif
179 e->next= m_free;
180 m_free= e;
181 }
182 my_hash_reset(&m_table_ids);
183 DBUG_VOID_RETURN;
184 }
185
186 #endif
187