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