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 Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 #include <gtest/gtest.h>
25 #include <string.h>
26
27 #define FRIEND_OF_GTID_SET class GroupTest_Group_containers_Test
28 #define FRIEND_OF_GROUP_CACHE class GroupTest_Group_containers_Test
29 #define FRIEND_OF_GROUP_LOG_STATE class GroupTest_Group_containers_Test
30 #define NON_DISABLED_UNITTEST_GTID
31
32 #include "sql_class.h"
33 #include "my_thread.h"
34 #include "binlog.h"
35 #include "rpl_gtid.h"
36
37 #define N_SIDS 16
38
39 #define ASSERT_OK(X) ASSERT_EQ(RETURN_STATUS_OK, X)
40 #define EXPECT_OK(X) EXPECT_EQ(RETURN_STATUS_OK, X)
41 #define EXPECT_NOK(X) EXPECT_NE(RETURN_STATUS_OK, X)
42
43
44 class GroupTest : public ::testing::Test
45 {
46 public:
47 static const char *uuids[16];
48 rpl_sid sids[16];
49 unsigned int seed;
50
51
SetUp()52 void SetUp()
53 {
54 seed= (unsigned int)time(NULL);
55 printf("# seed = %u\n", seed);
56 srand(seed);
57 for (int i= 0; i < 16; i++)
58 sids[i].parse(uuids[i]);
59
60 verbose= false;
61 errtext_stack_pos= 0;
62 errtext_stack[0]= 0;
63 append_errtext(__LINE__, "seed=%d", seed);
64 my_delete("sid-map-0", MYF(0));
65 my_delete("sid-map-1", MYF(0));
66 my_delete("sid-map-2", MYF(0));
67 }
68
69
TearDown()70 void TearDown()
71 {
72 my_delete("sid-map-0", MYF(0));
73 my_delete("sid-map-1", MYF(0));
74 my_delete("sid-map-2", MYF(0));
75 }
76
77
78 /*
79 Test that different, equivalent ways to construct a Gtid_set give
80 the same resulting Gtid_set. This is used to test Gtid_set,
81 Sid_map, Group_cache, Group_log_state, and Owned_groups.
82
83 We will generate sets of groups in *stages*. Each stage is
84 divided into a number of *sub-stages* (the number of substages is
85 taken uniformly at random from the set 1, 2, ..., 200). In each
86 sub-stage, we randomly sample one sub-group from a fixed set of
87 groups. The fixed set of groups consists of groups from 16
88 different SIDs. For the Nth SID (1 <= N <= 16), the fixed set of
89 groups contains all GNOS from the closed interval [N, N - 1 + N *
90 N]. The stage consists of the set of groups from all the
91 sub-stages.
92 */
93
94 #define BEGIN_SUBSTAGE_LOOP(group_test, stage, do_errtext) \
95 (group_test)->push_errtext(); \
96 for (int substage_i= 0; substage_i < (stage)->n_substages; substage_i++) { \
97 Substage &substage= (stage)->substages[substage_i]; \
98 if (do_errtext) \
99 (group_test)->append_errtext(__LINE__, \
100 "sidno=%d group=%s substage_i=%d", \
101 substage.sidno, substage.gtid_str, \
102 substage_i);
103 #define END_SUBSTAGE_LOOP(group_test) } group_test->pop_errtext()
104
105 /**
106 A substage, i.e., one of the randomly generated groups.
107 */
108 struct Substage
109 {
110 rpl_sidno sidno;
111 rpl_gno gno;
112 const rpl_sid *sid;
113 char sid_str[binary_log::Uuid::TEXT_LENGTH + 1];
114 char gtid_str[binary_log::Uuid::TEXT_LENGTH + 1 + MAX_GNO_TEXT_LENGTH + 1];
115 bool is_first, is_last, is_auto;
116 #ifndef NO_DBUG
printGroupTest::Substage117 void print() const
118 {
119 printf("%d/%s [first=%d last=%d auto=%d]",
120 sidno, gtid_str, is_first, is_last, is_auto);
121 }
122 #endif
123 };
124
125 /**
126 A stage, i.e., the sequence of randomly generated groups.
127 */
128 struct Stage
129 {
130 class GroupTest *group_test;
131 Sid_map *sid_map;
132
133 // List of groups added in the present stage.
134 static const int MAX_SUBSTAGES= 200;
135 Substage substages[MAX_SUBSTAGES];
136 int n_substages;
137
138 // Set of groups added in the present stage.
139 Gtid_set set;
140 int str_len;
141 char *str;
142
143 // The subset of groups that can be added as automatic groups.
144 Gtid_set automatic_groups;
145 // The subset of groups that cannot be added as automatic groups.
146 Gtid_set non_automatic_groups;
147
StageGroupTest::Stage148 Stage(class GroupTest *gt, Sid_map *sm)
149 : group_test(gt), sid_map(sm),
150 set(sm), str_len(0), str(NULL),
151 automatic_groups(sm), non_automatic_groups(sm)
152 { init(sm); }
153
154
initGroupTest::Stage155 void init(Sid_map *sm)
156 {
157 rpl_sidno max_sidno= sm->get_max_sidno();
158 ASSERT_OK(set.ensure_sidno(max_sidno));
159 ASSERT_OK(automatic_groups.ensure_sidno(max_sidno));
160 ASSERT_OK(non_automatic_groups.ensure_sidno(max_sidno));
161 }
162
~StageGroupTest::Stage163 ~Stage() { free(str); }
164
printGroupTest::Stage165 void print() const
166 {
167 printf("%d substages = {\n", n_substages);
168 for (int i= 0; i < n_substages; i++)
169 {
170 printf(" substage[%d]: ", i);
171 substages[i].print();
172 printf("\n");
173 }
174 printf("\n");
175 }
176
177 /**
178 Generate the the random groups that constitute a stage.
179
180 @param done_groups The set of all groups added in previous
181 stages.
182 @param other_sm Sid_map to which groups should be added.
183 */
new_stageGroupTest::Stage184 void new_stage(const Gtid_set *done_groups, Sid_map *other_sm)
185 {
186 set.clear();
187 automatic_groups.clear();
188 non_automatic_groups.clear();
189
190 n_substages= 1 + (rand() % MAX_SUBSTAGES);
191 BEGIN_SUBSTAGE_LOOP(group_test, this, false)
192 {
193 // generate random GTID
194 substage.sidno= 1 + (rand() % N_SIDS);
195 substage.gno=
196 1 + (rand() % (substage.sidno * substage.sidno));
197 // compute alternative forms
198 substage.sid= sid_map->sidno_to_sid(substage.sidno);
199 ASSERT_NE((rpl_sid *)NULL, substage.sid) << group_test->errtext;
200 substage.sid->to_string(substage.sid_str);
201 substage.sid->to_string(substage.gtid_str);
202 substage.gtid_str[rpl_sid.TEXT_LENGTH]= ':';
203 format_gno(substage.gtid_str + rpl_sid.TEXT_LENGTH + 1, substage.gno);
204
205 ASSERT_LE(1, other_sm->add_permanent(substage.sid))
206 << group_test->errtext;
207
208 // check if this group could be added as an 'automatic' group
209 Gtid_set::Const_interval_iterator ivit(done_groups, substage.sidno);
210 const Gtid_set::Interval *iv= ivit.get();
211 substage.is_auto=
212 !set.contains_group(substage.sidno, substage.gno) &&
213 ((iv == NULL || iv->start > 1) ? substage.gno == 1 :
214 substage.gno == iv->end);
215
216 // check if this sub-group is the first in its group in this
217 // stage, and add it to the set
218 substage.is_first= !set.contains_group(substage.sidno, substage.gno);
219 if (substage.is_first)
220 ASSERT_OK(set.add(substage.gtid_str));
221 } END_SUBSTAGE_LOOP(group_test);
222
223 // Iterate backwards so that we can detect when a subgroup is
224 // the last subgroup of its group.
225 set.clear();
226 for (int substage_i= n_substages - 1; substage_i >= 0; substage_i--)
227 {
228 Substage &substage= substages[substage_i];
229 substage.is_last= !set.contains_group(substage.sidno, substage.gno);
230 if (substage.is_last)
231 ASSERT_OK(set.add(substage.gtid_str));
232 }
233
234 str_len= set.get_string_length();
235 str= (char *)realloc(str, str_len + 1);
236 set.to_string(str);
237 }
238 };
239
240
241 /*
242 We maintain a text that contains the state of the test. We print
243 this text when a test assertion fails. The text is updated each
244 iteration of each loop, so that we can easier track the exact
245 point in time when an error occurs. Since loops may be nested, we
246 maintain a stack of offsets in the error text: before a new loop
247 is entered, the position of the end of the string is pushed to the
248 stack and the text appended in each iteration is added to that
249 position.
250 */
251 char errtext[1000];
252 int errtext_stack[100];
253 int errtext_stack_pos;
254 bool verbose;
255
append_errtext(int line,const char * fmt,...)256 void append_errtext(int line, const char *fmt, ...)
257 MY_ATTRIBUTE((format(printf, 3, 4)))
258 {
259 va_list argp;
260 va_start(argp, fmt);
261 vsprintf(errtext + errtext_stack[errtext_stack_pos], fmt, argp);
262 if (verbose)
263 printf("@line %d: %s\n", line, errtext);
264 va_end(argp);
265 }
266
push_errtext()267 void push_errtext()
268 {
269 int old_len= errtext_stack[errtext_stack_pos];
270 int len= old_len + strlen(errtext + old_len);
271 strcpy(errtext + len, " | ");
272 errtext_stack[++errtext_stack_pos]= len + 3;
273 }
274
pop_errtext()275 void pop_errtext()
276 {
277 errtext[errtext_stack[errtext_stack_pos--] - 3]= 0;
278 }
279
280
group_subset(Gtid_set * sub,Gtid_set * super,bool outcome,int line,const char * desc)281 void group_subset(Gtid_set *sub, Gtid_set *super, bool outcome,
282 int line, const char *desc)
283 {
284 append_errtext(line, "%s", desc);
285 // check using is_subset
286 EXPECT_EQ(outcome, sub->is_subset(super)) << errtext;
287 // check using set subtraction
288 enum_return_status status;
289 Gtid_set sub_minus_super(sub, &status);
290 ASSERT_OK(status) << errtext;
291 ASSERT_OK(sub_minus_super.remove(super)) << errtext;
292 ASSERT_EQ(outcome, sub_minus_super.is_empty()) << errtext;
293 }
294
295 };
296
297
298 const char *GroupTest::uuids[16]=
299 {
300 "00000000-0000-0000-0000-000000000000",
301 "11111111-1111-1111-1111-111111111111",
302 "22222222-2222-2222-2222-222222222222",
303 "33333333-3333-3333-3333-333333333333",
304 "44444444-4444-4444-4444-444444444444",
305 "55555555-5555-5555-5555-555555555555",
306 "66666666-6666-6666-6666-666666666666",
307 "77777777-7777-7777-7777-777777777777",
308 "88888888-8888-8888-8888-888888888888",
309 "99999999-9999-9999-9999-999999999999",
310 "aaaaAAAA-aaaa-AAAA-aaaa-aAaAaAaAaaaa",
311 "bbbbBBBB-bbbb-BBBB-bbbb-bBbBbBbBbbbb",
312 "ccccCCCC-cccc-CCCC-cccc-cCcCcCcCcccc",
313 "ddddDDDD-dddd-DDDD-dddd-dDdDdDdDdddd",
314 "eeeeEEEE-eeee-EEEE-eeee-eEeEeEeEeeee",
315 "ffffFFFF-ffff-FFFF-ffff-fFfFfFfFffff",
316 };
317
318
TEST_F(GroupTest,Uuid)319 TEST_F(GroupTest, Uuid)
320 {
321 Uuid u;
322 char buf[100];
323
324 // check that we get back the same UUID after parse + print
325 for (int i= 0; i < N_SIDS; i++)
326 {
327 EXPECT_OK(u.parse(uuids[i])) << "i=" << i;
328 u.to_string(buf);
329 EXPECT_STRCASEEQ(uuids[i], buf) << "i=" << i;
330 }
331 // check error cases
332 EXPECT_OK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFFFf"));
333 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFFg"));
334 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFF"));
335 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-fff-fffffffffFFFF"));
336 EXPECT_NOK(u.parse("ffffFFFF-ffff-FFFF-ffff-ffffffffFFF-"));
337 EXPECT_NOK(u.parse(" ffffFFFF-ffff-FFFF-ffff-ffffffffFFFF"));
338 EXPECT_NOK(u.parse("ffffFFFFfffff-FFFF-ffff-ffffffffFFFF"));
339 }
340
341
TEST_F(GroupTest,Sid_map)342 TEST_F(GroupTest, Sid_map)
343 {
344 Checkable_rwlock lock;
345 Sid_map sm(&lock);
346
347 lock.rdlock();
348 ASSERT_OK(sm.open("sid-map-0"));
349
350 // Add a random SID until we have N_SID SIDs in the map.
351 while (sm.get_max_sidno() < N_SIDS)
352 ASSERT_LE(1, sm.add_permanent(&sids[rand() % N_SIDS])) << errtext;
353
354 // Check that all N_SID SIDs are in the map, and that
355 // get_sorted_sidno() has the correct order. This implies that no
356 // SID was added twice.
357 for (int i= 0; i < N_SIDS; i++)
358 {
359 rpl_sidno sidno= sm.get_sorted_sidno(i);
360 const rpl_sid *sid;
361 char buf[100];
362 EXPECT_NE((rpl_sid *)NULL, sid= sm.sidno_to_sid(sidno)) << errtext;
363 const int max_len= binary_log::Uuid::TEXT_LENGTH;
364 EXPECT_EQ(max_len, sid->to_string(buf)) << errtext;
365 EXPECT_STRCASEEQ(uuids[i], buf) << errtext;
366 EXPECT_EQ(sidno, sm.sid_to_sidno(sid)) << errtext;
367 }
368 lock.unlock();
369 lock.assert_no_lock();
370 }
371
372
TEST_F(GroupTest,Group_containers)373 TEST_F(GroupTest, Group_containers)
374 {
375 /*
376 In this test, we maintain 298 Gtid_sets. We add groups to these
377 Gtid_sets in stages, as described above. We add the groups to
378 each of the 298 Gtid_sets in different ways, as described below.
379 At the end of each stage, we check that all the 298 resulting
380 Gtid_sets are mutually equal.
381
382 We add groups in the two ways:
383
384 A. Test Gtid_sets and Sid_maps. We vary two parameters:
385
386 Parameter 1: vary the way that groups are added:
387 0. Add one group at a time, using add(sidno, gno).
388 1. Add one group at a time, using add(text).
389 2. Add all new groups at once, using add(gs_new).
390 3. add all new groups at once, using add(gs_new.to_string()).
391 4. Maintain a string that contains the concatenation of all
392 gs_new.to_string(). in each stage, we set gs[4] to a new
393 Gtid_set created from this string.
394
395 Parameter 2: vary the Sid_map object:
396 0. Use a Sid_map that has all the SIDs in order.
397 1. Use a Sid_map where SIDs are added in the order they appear.
398
399 We vary these parameters in all combinations; thus we construct
400 10 Gtid_sets.
401 */
402 enum enum_sets_method {
403 METHOD_SIDNO_GNO= 0, METHOD_GROUP_TEXT,
404 METHOD_GTID_SET, METHOD_GTID_SET_TEXT, METHOD_ALL_TEXTS_CONCATENATED,
405 MAX_METHOD
406 };
407 enum enum_sets_sid_map {
408 SID_MAP_0= 0, SID_MAP_1, MAX_SID_MAP
409 };
410 const int N_COMBINATIONS_SETS= MAX_METHOD * MAX_SID_MAP;
411 /*
412 B. Test Group_cache, Group_log_state, and Owned_groups. All
413 sub-groups for the stage are added to the Group_cache, the
414 Group_cache is flushed to the Group_log_state, and the
415 Gtid_set is extracted from the Group_log_state. We vary the
416 following parameters.
417
418 Parameter 1: type of statement:
419 0. Transactional replayed statement: add all groups to the
420 transaction group cache (which is flushed to a
421 Group_log_state at the end of the stage). Set
422 GTID_NEXT_LIST to the list of all groups in the stage.
423 1. Non-transactional replayed statement: add all groups to the
424 stmt group cache (which is flushed to the Group_log_state
425 at the end of each sub-stage). Set GTID_NEXT_LIST = NULL.
426 2. Randomize: for each sub-stage, choose 0 or 1 with 50%
427 chance. Set GTID_NEXT_LIST to the list of all groups in
428 the stage.
429 3. Automatic groups: add all groups to the stmt group cache,
430 but make the group automatic if possible, i.e., if the SID
431 and GNO are unlogged and there is no smaller unlogged GNO
432 for this SID. Set GTID_NEXT_LIST = NULL.
433
434 Parameter 2: ended or non-ended sub-groups:
435 0. All sub-groups are unended (except automatic sub-groups).
436 1. For each group, the last sub-group of the group in the
437 stage is ended. Don't add groups that are already ended in the
438 Group_log_state.
439 2. For each group in the stage, choose 0 or 1 with 50% chance.
440
441 Parameter 3: empty or normal sub-group:
442 0. Generate only normal (and possibly automatic) sub-groups.
443 1. Generate only empty (and possibly automatic) sub-groups.
444 2. Generate only empty (and possibly automatic) sub-groups.
445 Add the sub-groups implicitly: do not call
446 add_empty_subgroup(); instead rely on
447 gtid_before_flush_trx_cache() to add empty subgroups.
448 3. Choose 0 or 1 with 33% chance.
449
450 Parameter 4: insert anonymous sub-groups or not:
451 0. Do not generate anonymous sub-groups.
452 1. Generate an anomous sub-group before each sub-group with
453 50% chance and an anonymous group after each sub-group with
454 50% chance.
455
456 We vary these parameters in all combinations; thus we construct
457 4*3*4*2=96 Gtid_sets.
458 */
459 enum enum_caches_type
460 {
461 TYPE_TRX= 0, TYPE_NONTRX, TYPE_RANDOMIZE, TYPE_AUTO, MAX_TYPE
462 };
463 enum enum_caches_end
464 {
465 END_OFF= 0, END_ON, END_RANDOMIZE, MAX_END
466 };
467 enum enum_caches_empty
468 {
469 EMPTY_OFF= 0, EMPTY_ON, EMPTY_IMPLICIT, EMPTY_RANDOMIZE, MAX_EMPTY
470 };
471 enum enum_caches_anon {
472 ANON_OFF= 0, ANON_ON, MAX_ANON
473 };
474 const int N_COMBINATIONS_CACHES=
475 MAX_TYPE * MAX_END * MAX_EMPTY * MAX_ANON;
476 const int N_COMBINATIONS= N_COMBINATIONS_SETS + N_COMBINATIONS_CACHES;
477
478 // Auxiliary macros to loop through all combinations of parameters.
479 #define BEGIN_LOOP_A \
480 push_errtext(); \
481 for (int method_i= 0, combination_i= 0; method_i < MAX_METHOD; method_i++) { \
482 for (int sid_map_i= 0; sid_map_i < MAX_SID_MAP; sid_map_i++, combination_i++) { \
483 Gtid_set >id_set MY_ATTRIBUTE((unused))= \
484 containers[combination_i]->gtid_set; \
485 Sid_map *&sid_map MY_ATTRIBUTE((unused))= \
486 sid_maps[sid_map_i]; \
487 append_errtext(__LINE__, \
488 "sid_map_i=%d method_i=%d combination_i=%d", \
489 sid_map_i, method_i, combination_i);
490
491 #define END_LOOP_A } } pop_errtext()
492
493 #define BEGIN_LOOP_B \
494 push_errtext(); \
495 for (int type_i= 0, combination_i= N_COMBINATIONS_SETS; \
496 type_i < MAX_TYPE; type_i++) { \
497 for (int end_i= 0; end_i < MAX_END; end_i++) { \
498 for (int empty_i= 0; empty_i < MAX_EMPTY; empty_i++) { \
499 for (int anon_i= 0; anon_i < MAX_ANON; anon_i++, combination_i++) { \
500 Gtid_set >id_set MY_ATTRIBUTE((unused))= \
501 containers[combination_i]->gtid_set; \
502 Group_cache &stmt_cache MY_ATTRIBUTE((unused))= \
503 containers[combination_i]->stmt_cache; \
504 Group_cache &trx_cache MY_ATTRIBUTE((unused))= \
505 containers[combination_i]->trx_cache; \
506 Group_log_state &group_log_state MY_ATTRIBUTE((unused))= \
507 containers[combination_i]->group_log_state; \
508 append_errtext(__LINE__, \
509 "type_i=%d end_i=%d empty_i=%d " \
510 "anon_i=%d combination_i=%d", \
511 type_i, end_i, empty_i, \
512 anon_i, combination_i); \
513 //verbose= (combination_i == 108); /*todo*/
514
515 #define END_LOOP_B } } } } pop_errtext()
516
517 // Do not generate warnings (because that causes segfault when done
518 // from a unittest).
519 global_system_variables.log_error_verbosity= 1;
520
521 mysql_bin_log.server_uuid_sidno= 1;
522
523 // Create Sid_maps.
524 Checkable_rwlock &lock= mysql_bin_log.sid_lock;
525 Sid_map **sid_maps= new Sid_map*[2];
526 sid_maps[0]= &mysql_bin_log.sid_map;
527 sid_maps[1]= new Sid_map(&lock);
528
529 lock.rdlock();
530 ASSERT_OK(sid_maps[0]->open("sid-map-1"));
531 ASSERT_OK(sid_maps[1]->open("sid-map-2"));
532 /*
533 Make sid_maps[0] and sid_maps[1] different: sid_maps[0] is
534 generated in order; sid_maps[1] is generated in the order that
535 SIDS are inserted in the Gtid_set.
536 */
537 for (int i= 0; i < N_SIDS; i++)
538 ASSERT_LE(1, sid_maps[0]->add_permanent(&sids[i])) << errtext;
539
540 // Create list of container objects. These are the objects that we
541 // test.
542 struct Containers
543 {
544 Gtid_set gtid_set;
545 Group_cache stmt_cache;
546 Group_cache trx_cache;
547 Group_log_state group_log_state;
548 Containers(Checkable_rwlock *lock, Sid_map *sm)
549 : gtid_set(sm), group_log_state(lock, sm)
550 { init(); }
551 void init() { ASSERT_OK(group_log_state.ensure_sidno()); };
552 };
553 Containers **containers= new Containers*[N_COMBINATIONS];
554 BEGIN_LOOP_A
555 {
556 containers[combination_i]= new Containers(&lock, sid_map);
557 } END_LOOP_A;
558 BEGIN_LOOP_B
559 {
560 containers[combination_i] = new Containers(&lock, sid_maps[0]);
561 } END_LOOP_B;
562
563 /*
564 Construct a Gtid_set that contains the set of all groups from
565 which we sample.
566 */
567 static char all_groups_str[100*100];
568 char *s= all_groups_str;
569 s += sprintf(s, "%s:1", uuids[0]);
570 for (rpl_sidno sidno= 2; sidno <= N_SIDS; sidno++)
571 s += sprintf(s, ",\n%s:1-%d", uuids[sidno - 1], sidno * sidno);
572 enum_return_status status;
573 Gtid_set all_groups(sid_maps[0], all_groups_str, &status);
574 ASSERT_OK(status) << errtext;
575
576 // The set of groups that were added in some previous stage.
577 Gtid_set done_groups(sid_maps[0]);
578 ASSERT_OK(done_groups.ensure_sidno(sid_maps[0]->get_max_sidno()));
579
580 /*
581 Iterate through stages. In each stage, create the "stage group
582 set" by generating up to 200 subgroups. Add this stage group set
583 to each of the group sets in different ways. Stop when the union
584 of all stage group sets is equal to the full set from which we
585 took the samples.
586 */
587 char *done_str= NULL;
588 int done_str_len= 0;
589 Stage stage(this, sid_maps[0]);
590 int stage_i= 0;
591
592 /*
593 We need a THD object only to read THD::variables.gtid_next,
594 THD::variables.gtid_end, THD::variables.gtid_next_list,
595 THD::thread_id, THD::server_status. We don't want to invoke the
596 THD constructor because that would require setting up mutexes,
597 etc. Hence we use malloc instead of new.
598 */
599 THD *thd= (THD *)malloc(sizeof(THD));
600 ASSERT_NE((THD *)NULL, thd) << errtext;
601 Gtid_specification *gtid_next= &thd->variables.gtid_next;
602 thd->set_new_thread_id();
603 gtid_next->type= Gtid_specification::AUTOMATIC;
604 my_bool >id_end= thd->variables.gtid_end;
605 my_bool >id_commit= thd->variables.gtid_commit;
606 thd->server_status= 0;
607 thd->system_thread= NON_SYSTEM_THREAD;
608 thd->variables.gtid_next_list.gtid_set= &stage.set;
609
610 push_errtext();
611 while (!all_groups.equals(&done_groups))
612 {
613 stage_i++;
614 append_errtext(__LINE__, "stage_i=%d", stage_i);
615 stage.new_stage(&done_groups, sid_maps[1]);
616
617 if (verbose)
618 {
619 printf("======== stage %d ========\n", stage_i);
620 stage.print();
621 }
622
623 // Create a string that contains all previous stage.str,
624 // concatenated.
625 done_str= (char *)realloc(done_str,
626 done_str_len + 1 + stage.str_len + 1);
627 ASSERT_NE((char *)NULL, done_str) << errtext;
628 done_str_len += sprintf(done_str + done_str_len, ",%s", stage.str);
629
630 // Add groups to Gtid_sets.
631 BEGIN_LOOP_A
632 {
633 switch (method_i)
634 {
635 case METHOD_SIDNO_GNO:
636 BEGIN_SUBSTAGE_LOOP(this, &stage, true)
637 {
638 rpl_sidno sidno_1= sid_map->sid_to_sidno(substage.sid);
639 ASSERT_LE(1, sidno_1) << errtext;
640 ASSERT_OK(gtid_set.ensure_sidno(sidno_1));
641 ASSERT_OK(gtid_set._add(sidno_1, substage.gno));
642 } END_SUBSTAGE_LOOP(this);
643 break;
644 case METHOD_GROUP_TEXT:
645 BEGIN_SUBSTAGE_LOOP(this, &stage, true)
646 {
647 ASSERT_OK(gtid_set.add(substage.gtid_str));
648 } END_SUBSTAGE_LOOP(this);
649 break;
650 case METHOD_GTID_SET:
651 ASSERT_OK(gtid_set.add(&stage.set)) << errtext;
652 break;
653 case METHOD_GTID_SET_TEXT:
654 ASSERT_OK(gtid_set.add(stage.str)) << errtext;
655 break;
656 case METHOD_ALL_TEXTS_CONCATENATED:
657 gtid_set.clear();
658 ASSERT_OK(gtid_set.add(done_str)) << errtext;
659 case MAX_METHOD:
660 break;
661 }
662 } END_LOOP_A;
663
664 // Add groups to Group_caches.
665 BEGIN_LOOP_B
666 {
667 if (verbose)
668 {
669 printf("======== stage=%d combination=%d ========\n",
670 stage_i, combination_i);
671 #ifndef NDEBUG
672 printf("group log state:\n");
673 group_log_state.print();
674 printf("trx cache:\n");
675 trx_cache.print(sid_maps[0]);
676 printf("stmt cache:\n");
677 stmt_cache.print(sid_maps[0]);
678 #endif // ifdef NDEBUG
679 }
680
681 Gtid_set ended_groups(sid_maps[0]);
682 bool trx_contains_logged_subgroup= false;
683 bool stmt_contains_logged_subgroup= false;
684 BEGIN_SUBSTAGE_LOOP(this, &stage, true)
685 {
686 int type_j;
687 if (type_i == TYPE_RANDOMIZE)
688 type_j= rand() % 2;
689 else if (type_i == TYPE_AUTO && !substage.is_auto)
690 type_j= TYPE_NONTRX;
691 else
692 type_j= type_i;
693 int end_j;
694 if (substage.is_first &&
695 ((end_i == END_RANDOMIZE && (rand() % 2)) ||
696 end_i == END_ON))
697 {
698 ASSERT_OK(ended_groups.ensure_sidno(substage.sidno));
699 ASSERT_OK(ended_groups._add(substage.sidno, substage.gno));
700 }
701 end_j= substage.is_last &&
702 ended_groups.contains_group(substage.sidno, substage.gno);
703
704 /*
705 In EMPTY_RANDOMIZE mode, we have to determine once *per
706 group* (not substage) if we use EMPTY_END or not. So we
707 determine this for the first subgroup of the group, and then
708 we memoize which groups use EMPTY_END using the Gtid_set
709 empty_end.
710 */
711 int empty_j;
712 if (empty_i == EMPTY_RANDOMIZE)
713 empty_j= rand() % 3;
714 else
715 empty_j= empty_i;
716 int anon_j1, anon_j2;
717 if (type_j != TYPE_TRX || anon_i == ANON_OFF)
718 anon_j1= anon_j2= ANON_OFF;
719 else
720 {
721 anon_j1= rand() % 2;
722 anon_j2= rand() % 2;
723 }
724 if (verbose)
725 printf("type_j=%d end_j=%d empty_j=%d anon_j1=%d anon_j2=%d\n",
726 type_j, end_j, empty_j, anon_j1, anon_j2);
727
728 thd->variables.gtid_next_list.is_non_null=
729 (type_i == TYPE_NONTRX || type_i == TYPE_AUTO) ? 0 : 1;
730 gtid_commit=
731 (substage_i == stage.n_substages - 1) ||
732 !thd->variables.gtid_next_list.is_non_null;
733
734 if (type_j == TYPE_AUTO)
735 {
736 gtid_next->type= Gtid_specification::AUTOMATIC;
737 gtid_next->group.sidno= substage.sidno;
738 gtid_next->group.gno= 0;
739 gtid_end= false;
740 lock.unlock();
741 lock.assert_no_lock();
742 gtid_before_statement(thd, &lock, &group_log_state,
743 &stmt_cache, &trx_cache);
744 lock.rdlock();
745 stmt_cache.add_logged_subgroup(thd, 20 + rand() % 100/*binlog_len*/);
746 stmt_contains_logged_subgroup= true;
747 }
748 else
749 {
750 Group_cache &cache= type_j == TYPE_TRX ? trx_cache : stmt_cache;
751
752 if (anon_j1)
753 {
754 gtid_next->type= Gtid_specification::ANONYMOUS;
755 gtid_next->group.sidno= 0;
756 gtid_next->group.gno= 0;
757 gtid_end= false;
758 lock.unlock();
759 lock.assert_no_lock();
760 gtid_before_statement(thd, &lock, &group_log_state,
761 &stmt_cache, &trx_cache);
762 lock.rdlock();
763 cache.add_logged_subgroup(thd, 20 + rand() % 100/*binlog_len*/);
764 trx_contains_logged_subgroup= true;
765 }
766
767 gtid_next->type= Gtid_specification::GTID;
768 gtid_next->group.sidno= substage.sidno;
769 gtid_next->group.gno= substage.gno;
770 gtid_end= (end_j == END_ON) ? true : false;
771 lock.unlock();
772 lock.assert_no_lock();
773 gtid_before_statement(thd, &lock, &group_log_state,
774 &stmt_cache, &trx_cache);
775 lock.rdlock();
776 if (!group_log_state.is_ended(substage.sidno, substage.gno))
777 {
778 switch (empty_j)
779 {
780 case EMPTY_OFF:
781 cache.add_logged_subgroup(thd, 20 + rand() % 100/*binlog_len*/);
782 if (type_j == TYPE_TRX)
783 trx_contains_logged_subgroup= true;
784 else
785 stmt_contains_logged_subgroup= true;
786 break;
787 case EMPTY_ON:
788 cache.add_empty_subgroup(substage.sidno, substage.gno,
789 end_j ? true : false);
790 break;
791 case EMPTY_IMPLICIT:
792 break; // do nothing
793 default:
794 assert(0);
795 }
796 }
797
798 if (anon_j2)
799 {
800 gtid_next->type= Gtid_specification::ANONYMOUS;
801 gtid_next->group.sidno= 0;
802 gtid_next->group.gno= 0;
803 gtid_end= false;
804 lock.unlock();
805 lock.assert_no_lock();
806 gtid_before_statement(thd, &lock, &group_log_state,
807 &stmt_cache, &trx_cache);
808 lock.rdlock();
809 cache.add_logged_subgroup(thd, 20 + rand() % 100/*binlog_len*/);
810 trx_contains_logged_subgroup= true;
811 }
812 }
813
814 #ifndef NDEBUG
815 if (verbose)
816 {
817 printf("stmt_cache:\n");
818 stmt_cache.print(sid_maps[0]);
819 }
820 #endif // ifndef NDEBUG
821 if (!stmt_cache.is_empty())
822 gtid_flush_group_cache(thd, &lock,
823 &group_log_state, NULL/*group log*/,
824 &stmt_cache, &trx_cache,
825 1/*binlog_no*/, 1/*binlog_pos*/,
826 stmt_contains_logged_subgroup ?
827 20 + rand() % 99 : -1
828 /*offset_after_last_statement*/);
829 stmt_contains_logged_subgroup= false;
830 gtid_before_flush_trx_cache(thd, &lock, &group_log_state, &trx_cache);
831 if (gtid_commit)
832 {
833 // simulate gtid_after_flush_trx_cache() but don't
834 // execute a COMMIT statement
835 thd->variables.gtid_has_ongoing_super_group= 0;
836
837 #ifndef NDEBUG
838 if (verbose)
839 {
840 printf("trx_cache:\n");
841 trx_cache.print(sid_maps[0]);
842 printf("trx_cache.is_empty=%d n_subgroups=%d trx_contains_logged_subgroup=%d\n",
843 trx_cache.is_empty(), trx_cache.get_n_subgroups(),
844 trx_contains_logged_subgroup);
845 }
846 #endif // ifndef NDEBUG
847
848 if (!trx_cache.is_empty())
849 gtid_flush_group_cache(thd, &lock,
850 &group_log_state, NULL/*group log*/,
851 &trx_cache, &trx_cache,
852 1/*binlog_no*/, 1/*binlog_pos*/,
853 trx_contains_logged_subgroup ?
854 20 + rand() % 99 : -1
855 /*offset_after_last_statement*/);
856 trx_contains_logged_subgroup= false;
857 }
858 } END_SUBSTAGE_LOOP(this);
859
860 gtid_set.clear();
861 ASSERT_OK(group_log_state.owned_groups.get_partial_groups(>id_set));
862 ASSERT_OK(gtid_set.add(&group_log_state.ended_groups));
863 } END_LOOP_B;
864
865 // add stage.set to done_groups
866 Gtid_set old_done_groups(&done_groups, &status);
867 ASSERT_OK(status);
868 ASSERT_OK(done_groups.add(&stage.set));
869
870 // check the Gtid_set::remove and Gtid_set::is_subset functions
871 Gtid_set diff(&done_groups, &status);
872 ASSERT_OK(status);
873 ASSERT_OK(diff.remove(&old_done_groups));
874 Gtid_set not_new(&stage.set, &status);
875 ASSERT_OK(status);
876 ASSERT_OK(not_new.remove(&diff));
877
878 #define GROUP_SUBSET(gs1, gs2, outcome) \
879 group_subset(&gs1, &gs2, outcome, __LINE__, #gs1 " <= " #gs2);
880 push_errtext();
881 GROUP_SUBSET(not_new, not_new, true);
882 GROUP_SUBSET(not_new, diff, not_new.is_empty());
883 GROUP_SUBSET(not_new, stage.set, true);
884 GROUP_SUBSET(not_new, done_groups, true);
885 GROUP_SUBSET(not_new, old_done_groups, true);
886
887 GROUP_SUBSET(diff, not_new, diff.is_empty());
888 GROUP_SUBSET(diff, diff, true);
889 GROUP_SUBSET(diff, stage.set, true);
890 GROUP_SUBSET(diff, done_groups, true);
891 GROUP_SUBSET(diff, old_done_groups, diff.is_empty());
892
893 GROUP_SUBSET(stage.set, not_new, diff.is_empty());
894 GROUP_SUBSET(stage.set, diff, not_new.is_empty());
895 GROUP_SUBSET(stage.set, stage.set, true);
896 GROUP_SUBSET(stage.set, done_groups, true);
897 GROUP_SUBSET(stage.set, old_done_groups, diff.is_empty());
898
899 //GROUP_SUBSET(done_groups, not_new, ???);
900 GROUP_SUBSET(done_groups, diff, old_done_groups.is_empty());
901 GROUP_SUBSET(done_groups, stage.set, done_groups.equals(&stage.set));
902 GROUP_SUBSET(done_groups, done_groups, true);
903 GROUP_SUBSET(done_groups, old_done_groups, diff.is_empty());
904
905 GROUP_SUBSET(old_done_groups, not_new, old_done_groups.equals(¬_new));
906 GROUP_SUBSET(old_done_groups, diff, old_done_groups.is_empty());
907 //GROUP_SUBSET(old_done_groups, stage.set, ???);
908 GROUP_SUBSET(old_done_groups, done_groups, true);
909 GROUP_SUBSET(old_done_groups, old_done_groups, true);
910 pop_errtext();
911
912 /*
913 Verify that all group sets are equal. We test both a.equals(b)
914 and b.equals(a) and a.equals(a), because we want to verify that
915 Gtid_set::equals is correct too. We compare both the sets
916 using Gtid_set::equals, and the output of to_string() using
917 EXPECT_STREQ.
918 */
919 BEGIN_LOOP_A
920 {
921 char *buf1= new char[gtid_set.get_string_length() + 1];
922 gtid_set.to_string(buf1);
923 for (int i= 0; i < N_COMBINATIONS_SETS; i++)
924 {
925 Gtid_set >id_set_2= containers[i]->gtid_set;
926 if (combination_i < i)
927 {
928 char *buf2= new char[gtid_set_2.get_string_length() + 1];
929 gtid_set_2.to_string(buf2);
930 EXPECT_STREQ(buf1, buf2) << errtext << " i=" << i;
931 delete buf2;
932 }
933 EXPECT_EQ(true, gtid_set.equals(>id_set_2)) << errtext << " i=" << i;
934 }
935 delete buf1;
936 } END_LOOP_A;
937 BEGIN_LOOP_B
938 {
939 EXPECT_EQ(true, containers[combination_i]->gtid_set.equals(&done_groups)) << errtext;
940 } END_LOOP_B;
941 }
942 pop_errtext();
943
944 // Finally, verify that the string representations of
945 // done_groups is as expected
946 static char buf[100*100];
947 done_groups.to_string(buf);
948 EXPECT_STRCASEEQ(all_groups_str, buf) << errtext;
949 lock.unlock();
950 lock.assert_no_lock();
951
952 // Clean up.
953 free(done_str);
954 for (int i= 0; i < N_COMBINATIONS; i++)
955 delete containers[i];
956 delete containers;
957 delete sid_maps[1];
958 delete sid_maps;
959 free(thd);
960
961 mysql_bin_log.sid_lock.assert_no_lock();
962 }
963