1 /* Copyright (c) 2011, 2021, Oracle and/or its affiliates.
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
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22    02110-1301 USA */
23 
24 #include "rpl_gtid.h"
25 
26 #include "mysqld_error.h"    // ER_*
27 
28 
29 PSI_memory_key key_memory_Sid_map_Node;
30 
Sid_map(Checkable_rwlock * _sid_lock)31 Sid_map::Sid_map(Checkable_rwlock *_sid_lock)
32   : sid_lock(_sid_lock),
33     _sidno_to_sid(key_memory_Sid_map_Node), _sorted(key_memory_Sid_map_Node)
34 {
35   DBUG_ENTER("Sid_map::Sid_map");
36   my_hash_init(&_sid_to_sidno, &my_charset_bin, 20,
37                offsetof(Node, sid.bytes), binary_log::Uuid::BYTE_LENGTH, NULL,
38                my_free, 0,
39                key_memory_Sid_map_Node);
40   DBUG_VOID_RETURN;
41 }
42 
43 
~Sid_map()44 Sid_map::~Sid_map()
45 {
46   DBUG_ENTER("Sid_map::~Sid_map");
47   my_hash_free(&_sid_to_sidno);
48   DBUG_VOID_RETURN;
49 }
50 
51 
52 /*
53   This code is not being used but we will keep it as it may be
54   useful to optimize gtids by avoiding sharing mappings from
55   sid to sidno. For instance, the IO Thread and the SQL Thread
56   may have different mappings in the future.
57 */
58 #ifdef NON_DISABLED_GTID
clear()59 enum_return_status Sid_map::clear()
60 {
61   DBUG_ENTER("Sid_map::clear");
62   my_hash_free(&_sid_to_sidno);
63   my_hash_init(&_sid_to_sidno, &my_charset_bin, 20,
64                offsetof(Node, sid.bytes), binary_log::Uuid::BYTE_LENGTH, NULL,
65                my_free, 0);
66   _sidno_to_sid.clear();
67   _sorted.clear();
68   RETURN_OK;
69 }
70 #endif
71 
add_sid(const rpl_sid & sid)72 rpl_sidno Sid_map::add_sid(const rpl_sid &sid)
73 {
74   DBUG_ENTER("Sid_map::add_sid(const rpl_sid *)");
75 #ifndef NDEBUG
76   char buf[binary_log::Uuid::TEXT_LENGTH + 1];
77   sid.to_string(buf);
78   DBUG_PRINT("info", ("SID=%s", buf));
79 #endif
80   if (sid_lock)
81     sid_lock->assert_some_lock();
82   Node *node= (Node *)my_hash_search(&_sid_to_sidno, sid.bytes,
83                                      binary_log::Uuid::BYTE_LENGTH);
84   if (node != NULL)
85   {
86     DBUG_PRINT("info", ("existed as sidno=%d", node->sidno));
87     DBUG_RETURN(node->sidno);
88   }
89 
90   bool is_wrlock= false;
91   if (sid_lock)
92   {
93     is_wrlock= sid_lock->is_wrlock();
94     if (!is_wrlock)
95     {
96       sid_lock->unlock();
97       sid_lock->wrlock();
98     }
99   }
100   DBUG_PRINT("info", ("is_wrlock=%d sid_lock=%p", is_wrlock, sid_lock));
101   rpl_sidno sidno;
102   node= (Node *)my_hash_search(&_sid_to_sidno, sid.bytes,
103                                binary_log::Uuid::BYTE_LENGTH);
104   if (node != NULL)
105     sidno= node->sidno;
106   else
107   {
108     sidno= get_max_sidno() + 1;
109     if (add_node(sidno, sid) != RETURN_STATUS_OK)
110       sidno= -1;
111   }
112 
113   if (sid_lock)
114   {
115     if (!is_wrlock)
116     {
117       sid_lock->unlock();
118       sid_lock->rdlock();
119     }
120   }
121   DBUG_RETURN(sidno);
122 }
123 
add_node(rpl_sidno sidno,const rpl_sid & sid)124 enum_return_status Sid_map::add_node(rpl_sidno sidno, const rpl_sid &sid)
125 {
126   DBUG_ENTER("Sid_map::add_node(rpl_sidno, const rpl_sid *)");
127   if (sid_lock)
128     sid_lock->assert_some_wrlock();
129   Node *node= (Node *)my_malloc(key_memory_Sid_map_Node,
130                                 sizeof(Node), MYF(MY_WME));
131   if (node == NULL)
132     RETURN_REPORTED_ERROR;
133 
134   node->sidno= sidno;
135   node->sid= sid;
136   if (!_sidno_to_sid.push_back(node))
137   {
138     if (!_sorted.push_back(sidno))
139     {
140       if (my_hash_insert(&_sid_to_sidno, (uchar *)node) == 0)
141       {
142 #ifdef MYSQL_SERVER
143         /*
144           If this is the global_sid_map, we take the opportunity to
145           resize all arrays in gtid_state while holding the wrlock.
146         */
147         if (this != global_sid_map ||
148             gtid_state->ensure_sidno() == RETURN_STATUS_OK)
149 #endif
150         {
151           // We have added one element to the end of _sorted.  Now we
152           // bubble it down to the sorted position.
153           int sorted_i= sidno - 1;
154           rpl_sidno *prev_sorted_p= &_sorted[sorted_i];
155           sorted_i--;
156           while (sorted_i >= 0)
157           {
158             rpl_sidno *sorted_p= &_sorted[sorted_i];
159             const rpl_sid &other_sid= sidno_to_sid(*sorted_p);
160             if (memcmp(sid.bytes, other_sid.bytes,
161                        binary_log::Uuid::BYTE_LENGTH) >= 0)
162               break;
163             memcpy(prev_sorted_p, sorted_p, sizeof(rpl_sidno));
164             sorted_i--;
165             prev_sorted_p= sorted_p;
166           }
167           memcpy(prev_sorted_p, &sidno, sizeof(rpl_sidno));
168           RETURN_OK;
169         }
170       }
171       _sorted.pop_back();
172     }
173     _sidno_to_sid.pop_back();
174   }
175   my_free(node);
176 
177   BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
178   RETURN_REPORTED_ERROR;
179 }
180 
181 
copy(Sid_map * dest)182 enum_return_status Sid_map::copy(Sid_map *dest)
183 {
184   DBUG_ENTER("Sid_map::copy(Sid_map)");
185   enum_return_status return_status= RETURN_STATUS_OK;
186 
187   rpl_sidno max_sidno= get_max_sidno();
188   for (rpl_sidno sidno= 1;
189        sidno <= max_sidno && return_status == RETURN_STATUS_OK;
190        sidno++)
191   {
192     rpl_sid sid;
193     sid.copy_from(sidno_to_sid(sidno));
194     return_status= dest->add_node(sidno, sid);
195   }
196 
197   DBUG_RETURN(return_status);
198 }
199