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 
Owned_gtids(Checkable_rwlock * _sid_lock)29 Owned_gtids::Owned_gtids(Checkable_rwlock *_sid_lock)
30   : sid_lock(_sid_lock), sidno_to_hash(key_memory_Owned_gtids_sidno_to_hash)
31 {
32   /*
33   my_hash_init(&gtid_to_owner, &my_charset_bin, 20,
34                offsetof(Node, group), sizeof(Group), NULL,
35                my_free, 0);
36   */
37 }
38 
39 
~Owned_gtids()40 Owned_gtids::~Owned_gtids()
41 {
42   // destructor should only be called when no other thread may access object
43   //sid_lock->assert_no_lock();
44   // need to hold lock before calling get_max_sidno
45   sid_lock->rdlock();
46   rpl_sidno max_sidno= get_max_sidno();
47   for (int sidno= 1; sidno <= max_sidno; sidno++)
48   {
49     HASH *hash= get_hash(sidno);
50     my_hash_free(hash);
51     my_free(hash);
52   }
53   sid_lock->unlock();
54   //sid_lock->assert_no_lock();
55 }
56 
57 
ensure_sidno(rpl_sidno sidno)58 enum_return_status Owned_gtids::ensure_sidno(rpl_sidno sidno)
59 {
60   DBUG_ENTER("Owned_gtids::ensure_sidno");
61   sid_lock->assert_some_wrlock();
62   rpl_sidno max_sidno= get_max_sidno();
63   if (sidno > max_sidno || get_hash(sidno) == NULL)
64   {
65     for (int i= max_sidno; i < sidno; i++)
66     {
67       HASH *hash= (HASH *)my_malloc(key_memory_Owned_gtids_sidno_to_hash,
68                                     sizeof(HASH), MYF(MY_WME));
69       if (hash == NULL)
70         goto error;
71       my_hash_init(hash, &my_charset_bin, 20,
72                    offsetof(Node, gno), sizeof(rpl_gno), NULL,
73                    my_free, 0,
74                    key_memory_Owned_gtids_sidno_to_hash);
75       sidno_to_hash.push_back(hash);
76     }
77   }
78   RETURN_OK;
79 error:
80   BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
81   RETURN_REPORTED_ERROR;
82 }
83 
84 
add_gtid_owner(const Gtid & gtid,my_thread_id owner)85 enum_return_status Owned_gtids::add_gtid_owner(const Gtid &gtid,
86                                                my_thread_id owner)
87 {
88   DBUG_ENTER("Owned_gtids::add_gtid_owner(Gtid, my_thread_id)");
89   assert(gtid.sidno <= get_max_sidno());
90   assert(gtid.gno > 0);
91   assert(gtid.gno < GNO_END);
92   Node *n= (Node *)my_malloc(key_memory_Sid_map_Node,
93                              sizeof(Node), MYF(MY_WME));
94   if (n == NULL)
95     RETURN_REPORTED_ERROR;
96   n->gno= gtid.gno;
97   n->owner= owner;
98   /*
99   printf("Owned_gtids(%p)::add sidno=%d gno=%lld n=%p n->owner=%u\n",
100          this, sidno, gno, n, n?n->owner:0);
101   */
102   if (my_hash_insert(get_hash(gtid.sidno), (const uchar *)n) != 0)
103   {
104     my_free(n);
105     BINLOG_ERROR(("Out of memory."), (ER_OUT_OF_RESOURCES, MYF(0)));
106     RETURN_REPORTED_ERROR;
107   }
108   RETURN_OK;
109 }
110 
111 
remove_gtid(const Gtid & gtid,const my_thread_id owner)112 void Owned_gtids::remove_gtid(const Gtid &gtid, const my_thread_id owner)
113 {
114   DBUG_ENTER("Owned_gtids::remove_gtid(Gtid)");
115   //printf("Owned_gtids::remove(sidno=%d gno=%lld)\n", sidno, gno);
116   //assert(contains_gtid(sidno, gno)); // allow group not owned
117   HASH_SEARCH_STATE state;
118   HASH *hash= get_hash(gtid.sidno);
119   assert(hash != NULL);
120 
121   for (Node *node= (Node *)my_hash_search(hash, (const uchar *)&gtid.gno, sizeof(rpl_gno));
122        node != NULL;
123        node= (Node*) my_hash_next(hash, (const uchar *)&gtid.gno, sizeof(rpl_gno), &state))
124   {
125     if (node->owner == owner)
126     {
127 #ifdef NDEBUG
128       my_hash_delete(hash, (uchar *)node);
129 #else
130       // my_hash_delete returns nonzero if the element does not exist
131       assert(my_hash_delete(hash, (uchar *)node) == 0);
132 #endif
133       break;
134     }
135   }
136   DBUG_VOID_RETURN;
137 }
138 
139 
is_intersection_nonempty(const Gtid_set * other) const140 bool Owned_gtids::is_intersection_nonempty(const Gtid_set *other) const
141 {
142   DBUG_ENTER("Owned_gtids::is_intersection_nonempty(Gtid_set *)");
143   if (sid_lock != NULL)
144     sid_lock->assert_some_wrlock();
145   Gtid_iterator git(this);
146   Gtid g= git.get();
147   while (g.sidno != 0)
148   {
149     if (other->contains_gtid(g.sidno, g.gno))
150       DBUG_RETURN(true);
151     git.next();
152     g= git.get();
153   }
154   DBUG_RETURN(false);
155 }
156 
get_gtids(Gtid_set & gtid_set) const157 void Owned_gtids::get_gtids(Gtid_set &gtid_set) const
158 {
159   DBUG_ENTER("Owned_gtids::get_gtids");
160 
161   if (sid_lock != NULL)
162     sid_lock->assert_some_wrlock();
163 
164   Gtid_iterator git(this);
165   Gtid g= git.get();
166   while (g.sidno != 0)
167   {
168     gtid_set._add_gtid(g);
169     git.next();
170     g= git.get();
171   }
172   DBUG_VOID_RETURN;
173 }
174 
contains_gtid(const Gtid & gtid) const175 bool Owned_gtids::contains_gtid(const Gtid &gtid) const
176 {
177   HASH *hash= get_hash(gtid.sidno);
178   assert(hash != NULL);
179   sid_lock->assert_some_lock();
180 
181   return my_hash_search(hash, (const uchar *)&gtid.gno, sizeof(rpl_gno)) != NULL;
182 }
183 
is_owned_by(const Gtid & gtid,const my_thread_id thd_id) const184 bool Owned_gtids::is_owned_by(const Gtid &gtid, const my_thread_id thd_id) const
185 {
186   HASH_SEARCH_STATE state;
187   HASH *hash= get_hash(gtid.sidno);
188   assert(hash != NULL);
189   Node *node= (Node*) my_hash_first(hash, (const uchar *)&gtid.gno,
190                                     sizeof(rpl_gno), &state);
191   if (thd_id == 0)
192     return node == NULL;
193   while (node)
194   {
195     if (node->owner == thd_id)
196       return true;
197     node= (Node*) my_hash_next(hash, (const uchar *)&gtid.gno,
198                                sizeof(rpl_gno), &state);
199   }
200   return false;
201 }
202