From 2bcc380c687851052d9da52e2c0bcf880f9414f2 Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Wed, 17 Oct 2012 15:52:15 -0300 Subject: [PATCH] Fix for customer issue 26211 ($avg_rate might be zero), plus a fix for bug 1058285 (also related to 26211), and an emergent part of 821722: Several sql_modes implicitly turn on ANSI_QUOTES --- bin/pt-online-schema-change | 19 ++- lib/TableParser.pm | 11 +- t/pt-online-schema-change/bugs.t | 59 +++++++++ .../samples/issue-26211.sql | 117 ++++++++++++++++++ 4 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 t/pt-online-schema-change/samples/issue-26211.sql diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 466fae3f..ae636b61 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -2712,9 +2712,15 @@ sub get_create_table { die "I need a tbl parameter" unless $tbl; my $q = $self->{Quoter}; + + my $replace = q{@@SQL_MODE}; + for my $mode ( qw(ANSI_QUOTES ANSI DB2 MAXDB MSSQL ORACLE POSTGRESQL) ) { + $replace = "REPLACE($replace, '$mode', '')" + } + $replace = qq{REPLACE($replace, ',,', ',')}; my $new_sql_mode = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, ' - . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), } + . '@@SQL_MODE := ' . $replace . ', ' . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, ' . '@@SQL_QUOTE_SHOW_CREATE := 1 */'; @@ -3470,7 +3476,9 @@ sub is_cluster_node { PTDEBUG && _d($sql); my $row = $self->{dbh}->selectrow_arrayref($sql); PTDEBUG && _d(defined $row ? @$row : 'undef'); - $self->{is_cluster_node} = $row && $row->[0] ? 1 : 0; + $self->{is_cluster_node} = $row && $row->[1] + ? ($row->[1] eq 'ON' || $row->[1] eq '1') + : 0; return $self->{is_cluster_node}; } @@ -8231,10 +8239,9 @@ sub main { # the user specified --chunk-size=N on the cmd line, in which # case the max child table size is their specified chunk size # times the fudge factor. - my $max_rows - = $o->get('dry-run') ? $o->get('chunk-size') * $limit - : $chunk_time ? $avg_rate * $chunk_time * $limit - : $o->get('chunk-size') * $limit; + my $max_rows = $o->get('dry-run') ? $o->get('chunk-size') * $limit + : $chunk_time && $avg_rate ? $avg_rate * $chunk_time * $limit + : $o->get('chunk-size') * $limit; PTDEBUG && _d('Max allowed child table size:', $max_rows); $alter_fk_method = determine_alter_fk_method( diff --git a/lib/TableParser.pm b/lib/TableParser.pm index 9d90de3b..35c458c6 100644 --- a/lib/TableParser.pm +++ b/lib/TableParser.pm @@ -58,9 +58,18 @@ sub get_create_table { # To ensure a consistent output, we save the current (old) SQL mode, # then set it to the new SQL mode that what we need. When done, even # if an error occurs, we restore the old SQL mode. + + # We don't want ANSI_QUOTES on here, nor any of the modes that + # implicitly turn that on. + my $replace = q{@@SQL_MODE}; + # The others automatically turn on ANSI_QUOTES + for my $mode ( qw(ANSI_QUOTES ANSI DB2 MAXDB MSSQL ORACLE POSTGRESQL) ) { + $replace = "REPLACE($replace, '$mode', '')" + } + $replace = qq{REPLACE($replace, ',,', ',')}; my $new_sql_mode = '/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, ' - . q{@@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), } + . '@@SQL_MODE := ' . $replace . ', ' . '@OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, ' . '@@SQL_QUOTE_SHOW_CREATE := 1 */'; diff --git a/t/pt-online-schema-change/bugs.t b/t/pt-online-schema-change/bugs.t index 296c0844..cd7c6683 100644 --- a/t/pt-online-schema-change/bugs.t +++ b/t/pt-online-schema-change/bugs.t @@ -193,6 +193,65 @@ is_deeply( $master_dbh->do(q{DROP DATABASE IF EXISTS `bug_1041372`}); +# ############################################################################ +# pt-online-schema-change foreign key error +# Customer issue 26211 +# ############################################################################ +$sb->load_file('master', "$sample/issue-26211.sql"); + +my $retval; +# There's two bugs here. First, see that it lives ok: +($output, $retval) = full_output(sub { pt_online_schema_change::main(@args, + '--alter-foreign-keys-method', 'auto', + '--no-check-replication-filters', + '--alter', "ENGINE=InnoDB", + '--execute', "$master_dsn,D=bug_26211,t=prm_inst")}); + +is( + $retval, + 0, + "Issue 26211: Lives ok" +) or diag($output); + +unlike( + $output, + qr/\QI need a max_rows argument/, + "Issue 26211: No error message" +); + +$sb->load_file('master', "$sample/issue-26211.sql"); +# And now check that it works with this SQL mode + +my ($old_mode) = $master_dbh->selectrow_array('select @@sql_mode'); +chomp $old_mode; +diag("Old SQL mode: $old_mode"); +my $new_mode = 'NO_AUTO_VALUE_ON_ZERO,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER'; +diag("New SQL mode: $new_mode"); +$master_dbh->do("SET GLOBAL sql_mode='$new_mode'"); + +# There's two bugs here. First, see that it lives ok: +($output, $retval) = full_output(sub { pt_online_schema_change::main(@args, + '--alter-foreign-keys-method', 'auto', + '--no-check-replication-filters', + '--alter', "ENGINE=InnoDB", + '--execute', "$master_dsn,D=bug_26211,t=prm_inst")}); + +is( + $retval, + 0, + "Issue 26211 part 2: Lives ok" +); + +unlike( + $output, + qr/Error/i, + "Issue 26211 part 2: No error message" +); + +diag("Restoring SQL mode to $old_mode"); +$master_dbh->do("set global sql_mode='$old_mode'"); +$master_dbh->do(q{DROP DATABASE IF EXISTS `bug_26211`}); + # ############################################################################# # Done. # ############################################################################# diff --git a/t/pt-online-schema-change/samples/issue-26211.sql b/t/pt-online-schema-change/samples/issue-26211.sql new file mode 100644 index 00000000..91f8c0a9 --- /dev/null +++ b/t/pt-online-schema-change/samples/issue-26211.sql @@ -0,0 +1,117 @@ +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + + +DROP DATABASE IF EXISTS `bug_26211`; +CREATE DATABASE `bug_26211`; +USE `bug_26211`; + +DROP TABLE IF EXISTS `mref`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `mref` ( + `M_ID` decimal(10,0) NOT NULL , + `PR_M_INST_ID` decimal(10,0) NOT NULL , + KEY `I_M_ID` (`M_ID`), + KEY `I_PR_M_` (`PR_M_INST_ID`), + CONSTRAINT `FK_MREF_REF_PM` FOREIGN KEY (`M_ID`) REFERENCES `pm` (`M_ID`) ON DELETE CASCADE, + CONSTRAINT `FK_MREF_REF_PRMI` FOREIGN KEY (`PR_M_INST_ID`) REFERENCES `prm_inst` (`PR_M_INST_ID`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +DROP TABLE IF EXISTS `pm`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `pm` ( + `M_ID` decimal(10,0) NOT NULL , + `P_MR_NUM` varchar(64) NOT NULL , + `P_NUM` varchar(50) NOT NULL , + `TYPE` varchar(50) DEFAULT NULL , + `VERSION` decimal(10,0) NOT NULL , + `XML` longtext NOT NULL , + PRIMARY KEY (`M_ID`), + UNIQUE KEY `UK_PM` (`P_MR_NUM`,`P_NUM`), + KEY `I_PM_P_NUM` (`P_NUM`), + CONSTRAINT `FK_PM_REF_P` FOREIGN KEY (`P_NUM`) REFERENCES `p` (`P_NUM`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +DROP TABLE IF EXISTS `p`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `p` ( + `P_NUM` varchar(50) NOT NULL , + `VERSION` decimal(10,0) NOT NULL , + `TYPE` varchar(32) NOT NULL , + `PROTECTED` decimal(1,0) NOT NULL , + `DESCRIPTIONS` varchar(4000) DEFAULT NULL , + PRIMARY KEY (`P_NUM`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + + +DROP TABLE IF EXISTS `pr`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `pr` ( + `PR_ID` decimal(10,0) NOT NULL , + `NUM` varchar(64) NOT NULL , + `HUB_RQD` decimal(1,0) NOT NULL , + `TP_RQD` decimal(1,0) NOT NULL , + `TRANS_TYPE_RQD` decimal(1,0) NOT NULL , + `HUB_LABEL` varchar(255) DEFAULT NULL , + `TP_LABEL` varchar(255) DEFAULT NULL , + `TRANS_TYPE_LABEL` varchar(255) DEFAULT NULL , + `TYPE` varchar(32) NOT NULL , + `PR_M_FLAG` decimal(1,0) NOT NULL , + `USER_DEFINED` decimal(1,0) NOT NULL , + `DESCRIPTIONS` varchar(4000) DEFAULT NULL , + `SIGNATURE` longtext , + PRIMARY KEY (`PR_ID`), + KEY `pr_num_index` (`NUM`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +DROP TABLE IF EXISTS `prm`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `prm` ( + `PR_M_ID` decimal(10,0) NOT NULL , + `M_ID` decimal(10,0) NOT NULL , + `PR_ID` decimal(10,0) DEFAULT NULL , + `ACTIVE_VERSION` decimal(10,0) DEFAULT NULL , + `CURRENT_VERSION` decimal(10,0) DEFAULT NULL , + `ENABLED` decimal(1,0) NOT NULL , + `NUM` varchar(64) NOT NULL , + PRIMARY KEY (`PR_M_ID`), + KEY `I_PRM_M_ID` (`M_ID`), + KEY `I_PRM_PR_ID` (`PR_ID`), + KEY `prm_num_indx` (`NUM`), + CONSTRAINT `FK_PMOD_REF_PR` FOREIGN KEY (`PR_ID`) REFERENCES `pr` (`PR_ID`), + CONSTRAINT `FK_PRM_REF_PM` FOREIGN KEY (`M_ID`) REFERENCES `pm` (`M_ID`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +DROP TABLE IF EXISTS `prm_inst`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `prm_inst` ( + `PR_M_INST_ID` decimal(10,0) NOT NULL, + `VERSION` decimal(10,0) NOT NULL, + `PR_M_ID` decimal(10,0) NOT NULL, + PRIMARY KEY (`PR_M_INST_ID`), + UNIQUE KEY `UK_PRM_INST` (`VERSION`,`PR_M_ID`), + KEY `I_PRM_INST_PR_MODE` (`PR_M_ID`), + CONSTRAINT `FK_PRMI_REF_PRM` FOREIGN KEY (`PR_M_ID`) REFERENCES `prm` (`PR_M_ID`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +