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