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 "rpl_rli.h" // Relay_log_info
27 #include "sql_class.h" // THD
28 #include "sql_parse.h" // stmt_causes_implicit_commit
29
30 #include "pfs_transaction_provider.h"
31 #include "mysql/psi/mysql_transaction.h"
32
33
34
set_gtid_next(THD * thd,const Gtid_specification & spec)35 bool set_gtid_next(THD *thd, const Gtid_specification &spec)
36 {
37 DBUG_ENTER("set_gtid_next");
38
39 spec.dbug_print();
40 global_sid_lock->assert_some_lock();
41 int lock_count= 1;
42 bool ret= true;
43
44 // we may acquire and release locks throughout this function; this
45 // variable tells the error handler how many are left to release
46
47 // Check that we don't own a GTID or ANONYMOUS.
48 if (thd->owned_gtid.sidno > 0 ||
49 thd->owned_gtid.sidno == THD::OWNED_SIDNO_ANONYMOUS)
50 {
51 char buf[Gtid::MAX_TEXT_LENGTH + 1];
52 if (thd->owned_gtid.sidno > 0)
53 {
54 #ifndef NDEBUG
55 global_sid_lock->unlock();
56 global_sid_lock->wrlock();
57 assert(gtid_state->get_owned_gtids()->
58 thread_owns_anything(thd->thread_id()));
59 #endif
60 thd->owned_gtid.to_string(thd->owned_sid, buf);
61 }
62 else
63 {
64 assert(gtid_state->get_anonymous_ownership_count() > 0);
65 strcpy(buf, "ANONYMOUS");
66 }
67 my_error(ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID, MYF(0), buf);
68 goto err;
69 }
70
71 // At this point we should not own any GTID.
72 assert(thd->owned_gtid.is_empty());
73
74 if (spec.type == AUTOMATIC_GROUP)
75 {
76 thd->variables.gtid_next.set_automatic();
77 }
78 else if (spec.type == ANONYMOUS_GROUP)
79 {
80 if (get_gtid_mode(GTID_MODE_LOCK_SID) == GTID_MODE_ON)
81 {
82 my_error(ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON, MYF(0));
83 goto err;
84 }
85
86 thd->variables.gtid_next.set_anonymous();
87 thd->owned_gtid.sidno= THD::OWNED_SIDNO_ANONYMOUS;
88 thd->owned_gtid.gno= 0;
89 gtid_state->acquire_anonymous_ownership();
90 }
91 else
92 {
93 assert(spec.type == GTID_GROUP);
94 assert(spec.gtid.sidno >= 1);
95 assert(spec.gtid.gno >= 1);
96 assert(spec.gtid.gno < GNO_END);
97 while (true)
98 {
99 // loop invariant: we should always hold global_sid_lock.rdlock
100 assert(lock_count == 1);
101 global_sid_lock->assert_some_lock();
102
103 if (get_gtid_mode(GTID_MODE_LOCK_SID) == GTID_MODE_OFF)
104 {
105 my_error(ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF, MYF(0));
106 goto err;
107 }
108
109 // acquire lock before checking conditions
110 gtid_state->lock_sidno(spec.gtid.sidno);
111 lock_count= 2;
112
113 // GTID already logged
114 if (gtid_state->is_executed(spec.gtid))
115 {
116 thd->variables.gtid_next= spec;
117 /*
118 Don't skip the statement here, skip it in
119 gtid_pre_statement_checks.
120 */
121 break;
122 }
123
124 // GTID not owned by anyone: acquire ownership
125 if (!gtid_state->is_owned(spec.gtid))
126 {
127 // acquire_ownership can't fail
128 gtid_state->acquire_ownership(thd, spec.gtid);
129 thd->variables.gtid_next= spec;
130 assert(thd->owned_gtid.sidno >= 1);
131 assert(thd->owned_gtid.gno >= 1);
132 assert(thd->owned_gtid.gno < GNO_END);
133 break;
134 }
135 // GTID owned by someone (other thread)
136 else
137 {
138 // The call below releases the read lock on global_sid_lock and
139 // the mutex lock on SIDNO.
140 gtid_state->wait_for_gtid(thd, spec.gtid);
141
142 // global_sid_lock and mutex are now released
143 lock_count= 0;
144
145 // Check if thread was killed.
146 if (thd->killed || abort_loop)
147 {
148 goto err;
149 }
150 #ifdef HAVE_REPLICATION
151 // If this thread is a slave SQL thread or slave SQL worker
152 // thread, we need this additional condition to determine if it
153 // has been stopped by STOP SLAVE [SQL_THREAD].
154 if ((thd->system_thread &
155 (SYSTEM_THREAD_SLAVE_SQL | SYSTEM_THREAD_SLAVE_WORKER)) != 0)
156 {
157 // TODO: error is *not* reported on cancel
158 assert(thd->rli_slave!= NULL);
159 Relay_log_info *c_rli= thd->rli_slave->get_c_rli();
160 if (c_rli->abort_slave)
161 {
162 goto err;
163 }
164 }
165 #endif // HAVE_REPLICATION
166 global_sid_lock->rdlock();
167 lock_count= 1;
168 }
169 }
170 }
171
172 ret= false;
173
174 err:
175 if (lock_count == 2)
176 gtid_state->unlock_sidno(spec.gtid.sidno);
177
178 if (lock_count >= 1)
179 global_sid_lock->unlock();
180
181 if (!ret)
182 gtid_set_performance_schema_values(thd);
183 thd->owned_gtid.dbug_print(NULL, "Set owned_gtid in set_gtid_next");
184
185 DBUG_RETURN(ret);
186 }
187
188
189 /**
190 Acquire ownership of all groups in a Gtid_set. This is used to
191 begin a commit-sequence when @@SESSION.GTID_NEXT_LIST != NULL.
192 */
193 #ifdef HAVE_GTID_NEXT_LIST
gtid_acquire_ownership_multiple(THD * thd)194 int gtid_acquire_ownership_multiple(THD *thd)
195 {
196 const Gtid_set *gtid_next_list= thd->get_gtid_next_list_const();
197 rpl_sidno greatest_sidno= 0;
198 DBUG_ENTER("gtid_acquire_ownership_multiple");
199 // first check if we need to wait for any group
200 while (true)
201 {
202 Gtid_set::Gtid_iterator git(gtid_next_list);
203 Gtid g= git.get();
204 my_thread_id owner= 0;
205 rpl_sidno last_sidno= 0;
206 global_sid_lock->rdlock();
207 while (g.sidno != 0)
208 {
209 // lock all SIDNOs in order
210 if (g.sidno != last_sidno)
211 gtid_state->lock_sidno(g.sidno);
212 if (!gtid_state->is_executed(g))
213 {
214 owner= gtid_state->get_owner(g);
215 // break the do-loop and wait for the sid to be updated
216 if (owner != 0)
217 {
218 assert(owner != thd->id);
219 break;
220 }
221 }
222 last_sidno= g.sidno;
223 greatest_sidno= g.sidno;
224 git.next();
225 g= git.get();
226 }
227
228 // we don't need to wait for any groups, and all SIDNOs in the
229 // set are locked
230 if (g.sidno == 0)
231 break;
232
233 // unlock all previous sidnos to avoid blocking them
234 // while waiting. keep lock on g.sidno
235 for (rpl_sidno sidno= 1; sidno < g.sidno; sidno++)
236 if (gtid_next_list->contains_sidno(sidno))
237 gtid_state->unlock_sidno(sidno);
238
239 // wait. this call releases the read lock on global_sid_lock and
240 // the mutex lock on SIDNO
241 gtid_state->wait_for_gtid(thd, g);
242
243 // global_sid_lock and mutex are now released
244
245 // at this point, we don't hold any locks. re-acquire the global
246 // read lock that was held when this function was invoked
247 if (thd->killed || abort_loop)
248 DBUG_RETURN(1);
249 #ifdef HAVE_REPLICATION
250 // If this thread is a slave SQL thread or slave SQL worker
251 // thread, we need this additional condition to determine if it
252 // has been stopped by STOP SLAVE [SQL_THREAD].
253 if ((thd->system_thread &
254 (SYSTEM_THREAD_SLAVE_SQL | SYSTEM_THREAD_SLAVE_WORKER)) != 0)
255 {
256 assert(thd->rli_slave != NULL);
257 Relay_log_info *c_rli= thd->rli_slave->get_c_rli();
258 if (c_rli->abort_slave)
259 DBUG_RETURN(1);
260 }
261 #endif // HAVE_REPLICATION
262 }
263
264 // global_sid_lock is now held
265 thd->owned_gtid_set.ensure_sidno(greatest_sidno);
266
267 /*
268 Now the following hold:
269 - None of the GTIDs in GTID_NEXT_LIST is owned by any thread.
270 - We hold a lock on global_sid_lock.
271 - We hold a lock on all SIDNOs in GTID_NEXT_LIST.
272 So we acquire ownership of all groups that we need.
273 */
274 int ret= 0;
275 Gtid_set::Gtid_iterator git(gtid_next_list);
276 Gtid g= git.get();
277 do
278 {
279 if (!gtid_state->is_executed(g))
280 {
281 if (gtid_state->acquire_ownership(thd, g) != RETURN_STATUS_OK)
282 {
283 /// @todo release ownership on error
284 ret= 1;
285 break;
286 }
287 thd->owned_gtid_set._add_gtid(g);
288 }
289 git.next();
290 g= git.get();
291 } while (g.sidno != 0);
292
293 // unlock all sidnos
294 rpl_sidno max_sidno= gtid_next_list->get_max_sidno();
295 for (rpl_sidno sidno= 1; sidno <= max_sidno; sidno++)
296 if (gtid_next_list->contains_sidno(sidno))
297 gtid_state->unlock_sidno(sidno);
298
299 global_sid_lock->unlock();
300
301 /*
302 TODO: If this code is enabled, set the GTID in the Performance Schema,
303 similar to set_gtid_next().
304 */
305
306 DBUG_RETURN(ret);
307 }
308 #endif
309
310
311 /**
312 Check if current transaction should be skipped, that is, if GTID_NEXT
313 was already logged.
314
315 @param thd The calling thread.
316
317 @retval true Transaction was already logged.
318 @retval false Transaction must be executed.
319 */
is_already_logged_transaction(const THD * thd)320 bool is_already_logged_transaction(const THD *thd)
321 {
322 DBUG_ENTER("is_already_logged_transaction");
323
324 const Gtid_specification *gtid_next= &thd->variables.gtid_next;
325 const Gtid_set *gtid_next_list= thd->get_gtid_next_list_const();
326
327 if (gtid_next_list == NULL)
328 {
329 if (gtid_next->type == GTID_GROUP)
330 {
331 if (thd->owned_gtid.sidno == 0)
332 DBUG_RETURN(true);
333 else
334 assert(thd->owned_gtid.equals(gtid_next->gtid));
335 }
336 else
337 assert(thd->owned_gtid.sidno == 0 ||
338 thd->owned_gtid.sidno == THD::OWNED_SIDNO_ANONYMOUS);
339 }
340 else
341 {
342 #ifdef HAVE_GTID_NEXT_LIST
343 if (gtid_next->type == GTID_GROUP)
344 {
345 assert(gtid_next_list->contains_gtid(gtid_next->gtid));
346 if (!thd->owned_gtid_set.contains_gtid(gtid_next->gtid))
347 DBUG_RETURN(true);
348 }
349 #else
350 assert(0);/*NOTREACHED*/
351 #endif
352 }
353
354 DBUG_RETURN(false);
355 }
356
357
358 /**
359 Debug code executed when a transaction is skipped.
360
361 @param thd The calling thread.
362 */
skip_statement(const THD * thd)363 static inline void skip_statement(const THD *thd)
364 {
365 DBUG_ENTER("skip_statement");
366
367 DBUG_PRINT("info", ("skipping statement '%s'. "
368 "gtid_next->type=%d sql_command=%d "
369 "thd->thread_id=%u",
370 thd->query().str,
371 thd->variables.gtid_next.type,
372 thd->lex->sql_command,
373 thd->thread_id()));
374
375 #ifndef NDEBUG
376 const Gtid_set* executed_gtids= gtid_state->get_executed_gtids();
377 global_sid_lock->rdlock();
378 gtid_state->lock_sidno(thd->variables.gtid_next.gtid.sidno);
379 assert(executed_gtids->contains_gtid(thd->variables.gtid_next.gtid));
380 gtid_state->unlock_sidno(thd->variables.gtid_next.gtid.sidno);
381 global_sid_lock->unlock();
382 #endif
383
384 DBUG_VOID_RETURN;
385 }
386
387
gtid_reacquire_ownership_if_anonymous(THD * thd)388 bool gtid_reacquire_ownership_if_anonymous(THD *thd)
389 {
390 DBUG_ENTER("gtid_reacquire_ownership_if_anonymous(THD *)");
391 Gtid_specification *gtid_next= &thd->variables.gtid_next;
392 /*
393 When the slave applier thread executes a
394 Format_description_log_event originating from a master
395 (corresponding to a new master binary log), it sets gtid_next to
396 NOT_YET_DETERMINED_GROUP. This allows any following
397 Gtid_log_event to set the GTID appropriately, but if there is no
398 Gtid_log_event, gtid_next will be converted to ANONYMOUS.
399 */
400 DBUG_PRINT("info", ("gtid_next->type=%d gtid_mode=%s",
401 gtid_next->type,
402 get_gtid_mode_string(GTID_MODE_LOCK_NONE)));
403 if (gtid_next->type == NOT_YET_DETERMINED_GROUP ||
404 (gtid_next->type == ANONYMOUS_GROUP && thd->owned_gtid.sidno == 0))
405 {
406 Gtid_specification spec;
407 spec.set_anonymous();
408 DBUG_PRINT("info", ("acquiring ANONYMOUS ownership"));
409
410 global_sid_lock->rdlock();
411 // set_gtid_next releases global_sid_lock
412 if (set_gtid_next(thd, spec))
413 // this can happen if gtid_mode=on
414 DBUG_RETURN(true);
415
416 #ifdef HAVE_REPLICATION
417 thd->set_currently_executing_gtid_for_slave_thread();
418 #endif
419 }
420 DBUG_RETURN(false);
421 }
422
423
424 /**
425 Return true if the statement does not invoke any stored function,
426 and is one of the following:
427 - SET (except SET PASSWORD)
428 - SHOW
429 - SELECT
430 - DO
431 - An empty statement because of a skipped version comment
432 That means it is guaranteed not to cause any changes in the
433 database.
434 */
is_stmt_innocent(const THD * thd)435 static bool is_stmt_innocent(const THD *thd)
436 {
437 LEX *lex= thd->lex;
438 enum_sql_command sql_command= lex->sql_command;
439 bool is_show=
440 (sql_command_flags[sql_command] & CF_STATUS_COMMAND) &&
441 (sql_command != SQLCOM_BINLOG_BASE64_EVENT);
442 bool is_set=
443 (sql_command == SQLCOM_SET_OPTION) && !lex->is_set_password_sql;
444 bool is_select= (sql_command == SQLCOM_SELECT);
445 bool is_do= (sql_command == SQLCOM_DO);
446 bool is_empty= (sql_command == SQLCOM_EMPTY_QUERY);
447 bool is_use= (sql_command == SQLCOM_CHANGE_DB);
448 return
449 (is_set || is_select || is_do || is_show || is_empty ||
450 is_use) &&
451 !lex->uses_stored_routines();
452 }
453
454
gtid_pre_statement_checks(THD * thd)455 enum_gtid_statement_status gtid_pre_statement_checks(THD *thd)
456 {
457 DBUG_ENTER("gtid_pre_statement_checks");
458
459 Gtid_specification *gtid_next= &thd->variables.gtid_next;
460
461 DBUG_PRINT("info", ("gtid_next->type=%d "
462 "owned_gtid.{sidno,gno}={%d,%lld}",
463 gtid_next->type,
464 thd->owned_gtid.sidno, thd->owned_gtid.gno));
465 assert(gtid_next->type != AUTOMATIC_GROUP ||
466 thd->owned_gtid.is_empty());
467
468 if ((stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_BEGIN) ||
469 thd->lex->sql_command == SQLCOM_BEGIN) &&
470 thd->in_active_multi_stmt_transaction() &&
471 gtid_next->type == GTID_GROUP)
472 {
473 my_error(ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET, MYF(0));
474 DBUG_RETURN(GTID_STATEMENT_CANCEL);
475 }
476
477 /*
478 Always allow:
479 - BEGIN/COMMIT/ROLLBACK;
480 - innocent statements, i.e., SET/SHOW/DO/SELECT which don't invoke
481 stored functions.
482
483 @todo: add flag to sql_command_flags to detect if statement
484 controls transactions instead of listing the commands in the
485 condition below
486
487 @todo: figure out how to handle SQLCOM_XA_*
488 */
489 const enum_sql_command sql_command= thd->lex->sql_command;
490 if (sql_command == SQLCOM_COMMIT || sql_command == SQLCOM_BEGIN ||
491 sql_command == SQLCOM_ROLLBACK || is_stmt_innocent(thd))
492 DBUG_RETURN(GTID_STATEMENT_EXECUTE);
493
494 /*
495 If a transaction updates both non-transactional and transactional
496 table; or if it updates more than one non-transactional tables;
497 then the transaction must be stopped. This is the case when on
498 master all updated tables are transactional but on slave at least
499 one is non-transactional, e.g.:
500
501 On master, tables are transactional:
502 CREATE TABLE t1 (a INT) Engine=InnoDB;
503 CREATE TABLE t2 (a INT) Engine=InnoDB;
504 On slave, one table is non-transactional:
505 CREATE TABLE t1 (a INT) Engine=MyISAM;
506 CREATE TABLE t2 (a INT) Engine=InnoDB;
507 On master, user executes:
508 BEGIN;
509 INSERT INTO t1 VALUES (1);
510 INSERT INTO t2 VALUES (1);
511 COMMIT;
512 On slave, the second statement must error due to a second statement
513 being executed after a statement that updated a non-transactional
514 table.
515 */
516 if (UNDEFINED_GROUP == gtid_next->type)
517 {
518 char buf[Gtid::MAX_TEXT_LENGTH + 1];
519 global_sid_lock->rdlock();
520 gtid_next->to_string(global_sid_map, buf);
521 global_sid_lock->unlock();
522 my_error(ER_GTID_NEXT_TYPE_UNDEFINED_GROUP, MYF(0), buf);
523 DBUG_RETURN(GTID_STATEMENT_CANCEL);
524 }
525
526 const Gtid_set *gtid_next_list= thd->get_gtid_next_list_const();
527
528 DBUG_PRINT("info", ("gtid_next_list=%p gtid_next->type=%d "
529 "thd->owned_gtid.gtid.{sidno,gno}={%d,%lld} "
530 "thd->thread_id=%u",
531 gtid_next_list, gtid_next->type,
532 thd->owned_gtid.sidno,
533 thd->owned_gtid.gno,
534 thd->thread_id()));
535
536 const bool skip_transaction= is_already_logged_transaction(thd);
537 if (gtid_next_list == NULL)
538 {
539 if (skip_transaction)
540 {
541 skip_statement(thd);
542 DBUG_RETURN(GTID_STATEMENT_SKIP);
543 }
544 DBUG_RETURN(GTID_STATEMENT_EXECUTE);
545 }
546 else
547 {
548 #ifdef HAVE_GTID_NEXT_LIST
549 switch (gtid_next->type)
550 {
551 case AUTOMATIC_GROUP:
552 my_error(ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL,
553 MYF(0));
554 DBUG_RETURN(GTID_STATEMENT_CANCEL);
555 case GTID_GROUP:
556 if (skip_transaction)
557 {
558 skip_statement(thd);
559 DBUG_RETURN(GTID_STATEMENT_SKIP);
560 }
561 /*FALLTHROUGH*/
562 case ANONYMOUS_GROUP:
563 DBUG_RETURN(GTID_STATEMENT_EXECUTE);
564 case INVALID_GROUP:
565 assert(0);/*NOTREACHED*/
566 }
567 #else
568 assert(0);/*NOTREACHED*/
569 #endif
570 }
571 assert(0);/*NOTREACHED*/
572 DBUG_RETURN(GTID_STATEMENT_CANCEL);
573 }
574
575
gtid_pre_statement_post_implicit_commit_checks(THD * thd)576 bool gtid_pre_statement_post_implicit_commit_checks(THD *thd)
577 {
578 DBUG_ENTER("gtid_pre_statement_post_implicit_commit_checks");
579
580 /*
581 Ensure that we hold anonymous ownership before executing any
582 statement, if gtid_next=anonymous or not_yet_determined. But do
583 not re-acquire anonymous ownership if the statement is 'innocent'.
584 Innocent commands are those that cannot get written to the binary
585 log and cannot commit any ongoing transaction, i.e., one of the
586 SET/SELECT/DO/SHOW statements, as long as it does not invoke a
587 stored function.
588
589 It is important that we don't try to reacquire ownership for
590 innocent commands: SET could be used to set GTID_NEXT to
591 UUID:NUMBER; if anonymous was acquired before this then it would
592 result in an error. SHOW/SELECT/DO can be useful for testing
593 ownership logic, e.g., to read @@session.gtid_owned or to read
594 warnings using SHOW WARNINGS, and to test this properly it is
595 important to not affect the ownership status.
596 */
597 if (!is_stmt_innocent(thd))
598 if (gtid_reacquire_ownership_if_anonymous(thd))
599 // this can happen if gtid_mode is on
600 DBUG_RETURN(true);
601
602 if (!thd->is_ddl_gtid_compatible())
603 DBUG_RETURN(true);
604
605 DBUG_RETURN(false);
606 }
607
608
gtid_set_performance_schema_values(const THD * thd)609 void gtid_set_performance_schema_values(const THD *thd)
610 {
611 DBUG_ENTER("gtid_set_performance_schema_values");
612 #ifdef HAVE_PSI_TRANSACTION_INTERFACE
613 if (thd->m_transaction_psi != NULL)
614 {
615 Gtid_specification spec;
616
617 // Thread owns GTID.
618 if (thd->owned_gtid.sidno >= 1)
619 {
620 spec.type= GTID_GROUP;
621 spec.gtid= thd->owned_gtid;
622 }
623
624 // Thread owns ANONYMOUS.
625 else if (thd->owned_gtid.sidno == THD::OWNED_SIDNO_ANONYMOUS)
626 {
627 spec.type= ANONYMOUS_GROUP;
628 }
629
630 // Thread does not own anything.
631 else
632 {
633 assert(thd->owned_gtid.sidno == 0);
634 spec.type= AUTOMATIC_GROUP;
635 }
636 MYSQL_SET_TRANSACTION_GTID(thd->m_transaction_psi, &thd->owned_sid, &spec);
637 }
638 #endif
639 DBUG_VOID_RETURN;
640 }
641