From 04961a8f072b3151c6ca6580b43912884461c9d1 Mon Sep 17 00:00:00 2001 From: Carlos Salguero Date: Tue, 16 May 2017 09:14:50 -0300 Subject: [PATCH 1/5] Updated Percona::Toolkit version to 3.0.3 --- bin/pt-archiver | 2 +- bin/pt-config-diff | 2 +- bin/pt-deadlock-logger | 2 +- bin/pt-diskstats | 2 +- bin/pt-duplicate-key-checker | 2 +- bin/pt-find | 2 +- bin/pt-fk-error-logger | 2 +- bin/pt-heartbeat | 2 +- bin/pt-index-usage | 2 +- bin/pt-kill | 2 +- bin/pt-online-schema-change | 2 +- bin/pt-query-digest | 2 +- bin/pt-slave-delay | 2 +- bin/pt-slave-restart | 2 +- bin/pt-table-checksum | 2 +- bin/pt-table-sync | 2 +- bin/pt-upgrade | 2 +- bin/pt-variable-advisor | 2 +- lib/Percona/Toolkit.pm | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bin/pt-archiver b/bin/pt-archiver index 136c324e..aa4595d1 100755 --- a/bin/pt-archiver +++ b/bin/pt-archiver @@ -45,7 +45,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-config-diff b/bin/pt-config-diff index 18f3837c..a24d7e55 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -43,7 +43,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-deadlock-logger b/bin/pt-deadlock-logger index 3420a70e..94a54603 100755 --- a/bin/pt-deadlock-logger +++ b/bin/pt-deadlock-logger @@ -42,7 +42,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-diskstats b/bin/pt-diskstats index 74518844..3e0e2e76 100755 --- a/bin/pt-diskstats +++ b/bin/pt-diskstats @@ -38,7 +38,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-duplicate-key-checker b/bin/pt-duplicate-key-checker index abf05d5b..8b592eb1 100755 --- a/bin/pt-duplicate-key-checker +++ b/bin/pt-duplicate-key-checker @@ -39,7 +39,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-find b/bin/pt-find index e84b3516..4f300a53 100755 --- a/bin/pt-find +++ b/bin/pt-find @@ -35,7 +35,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-fk-error-logger b/bin/pt-fk-error-logger index a9c68bcb..e94fd70b 100755 --- a/bin/pt-fk-error-logger +++ b/bin/pt-fk-error-logger @@ -37,7 +37,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-heartbeat b/bin/pt-heartbeat index 151f405d..0877cd6f 100755 --- a/bin/pt-heartbeat +++ b/bin/pt-heartbeat @@ -44,7 +44,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-index-usage b/bin/pt-index-usage index 52be0734..b01ceff5 100755 --- a/bin/pt-index-usage +++ b/bin/pt-index-usage @@ -45,7 +45,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-kill b/bin/pt-kill index 539eedfa..10d64934 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -47,7 +47,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 95d5cc38..2c334109 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -55,7 +55,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-query-digest b/bin/pt-query-digest index 2fad690b..c3b174a8 100755 --- a/bin/pt-query-digest +++ b/bin/pt-query-digest @@ -64,7 +64,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-slave-delay b/bin/pt-slave-delay index f1464cfc..56032d14 100755 --- a/bin/pt-slave-delay +++ b/bin/pt-slave-delay @@ -40,7 +40,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-slave-restart b/bin/pt-slave-restart index 52893fde..4ffb76ee 100755 --- a/bin/pt-slave-restart +++ b/bin/pt-slave-restart @@ -41,7 +41,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 338c1bee..2c51cdc6 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -57,7 +57,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-table-sync b/bin/pt-table-sync index 197cb31f..9fee34a9 100755 --- a/bin/pt-table-sync +++ b/bin/pt-table-sync @@ -55,7 +55,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-upgrade b/bin/pt-upgrade index 1c6045e0..7abcb191 100755 --- a/bin/pt-upgrade +++ b/bin/pt-upgrade @@ -61,7 +61,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/bin/pt-variable-advisor b/bin/pt-variable-advisor index 6247ee69..ae0ffc9a 100755 --- a/bin/pt-variable-advisor +++ b/bin/pt-variable-advisor @@ -44,7 +44,7 @@ BEGIN { { package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; diff --git a/lib/Percona/Toolkit.pm b/lib/Percona/Toolkit.pm index bf3a76dc..ff8109bb 100644 --- a/lib/Percona/Toolkit.pm +++ b/lib/Percona/Toolkit.pm @@ -18,7 +18,7 @@ # ########################################################################### package Percona::Toolkit; -our $VERSION = '3.0.2'; +our $VERSION = '3.0.3'; use strict; use warnings FATAL => 'all'; From 109af35a49fb0f24e9353fe849c25b2d50b8b7be Mon Sep 17 00:00:00 2001 From: Carlos Salguero Date: Tue, 16 May 2017 13:53:49 -0300 Subject: [PATCH 2/5] PT-126 Recognize comments having quotes --- bin/pt-online-schema-change | 3 ++- t/pt-online-schema-change/bugs.t | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 2c334109..93c913dc 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -10053,7 +10053,8 @@ sub find_renamed_cols { /x; my $table_ident = qr/$unquoted_ident|`$quoted_ident`|"$ansi_quotes_ident"/; - my $alter_change_col_re = qr/\bCHANGE \s+ (?:COLUMN \s+)? (?:COMMENT\s+[^\]['"].*?[^\]['"])? + my $alter_change_col_re = qr/\bCHANGE \s+ (?:COLUMN \s+)? (?:COMMENT\s+[^\]['].*?[^\]['])? + (?:COMMENT\s+[^\]["].*?[^\]["])? ($table_ident) \s+ ($table_ident)/ix; my %renames; diff --git a/t/pt-online-schema-change/bugs.t b/t/pt-online-schema-change/bugs.t index 61025eaa..ae3db4cb 100644 --- a/t/pt-online-schema-change/bugs.t +++ b/t/pt-online-schema-change/bugs.t @@ -610,7 +610,7 @@ $sb->load_file('master', "$sample/bug-1613915.sql"); $output = output( sub { pt_online_schema_change::main(@args, "$master_dsn,D=test,t=o1", '--execute', - '--alter', "ADD COLUMN c INT COMMENT 'change plus more than one word'", + '--alter', "ADD COLUMN c INT COMMENT 'change \"plus\" more than one word'", '--chunk-size', '10', '--no-check-alter', ), }, @@ -633,7 +633,7 @@ is( $rows = $master_dbh->selectrow_arrayref("SHOW CREATE TABLE test.o1"); like( $rows->[1], - qr/COMMENT 'change plus more than one word'/, + qr/COMMENT 'change "plus" more than one word'/, "recognize comments", ); From 43b50f8a8cfd58b9686ac741dd60900261861def Mon Sep 17 00:00:00 2001 From: Carlos Salguero Date: Wed, 17 May 2017 09:38:19 -0300 Subject: [PATCH 3/5] PT-126 Fix remove comments regex --- bin/pt-online-schema-change | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 93c913dc..ccd4e7cb 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -10053,9 +10053,13 @@ sub find_renamed_cols { /x; my $table_ident = qr/$unquoted_ident|`$quoted_ident`|"$ansi_quotes_ident"/; - my $alter_change_col_re = qr/\bCHANGE \s+ (?:COLUMN \s+)? (?:COMMENT\s+[^\]['].*?[^\]['])? - (?:COMMENT\s+[^\]["].*?[^\]["])? - ($table_ident) \s+ ($table_ident)/ix; + + # remove comments + $alter =~ s/^(.*?)\s+COMMENT\s+'(.*?[^\\]')+(.*)/$1$3/; + $alter =~ s/^(.*?)\s+COMMENT\s+"(.*?[^\\]")+(.*)/$1$3/; + + my $alter_change_col_re = qr/\bCHANGE \s+ (?:COLUMN \s+)? + ($table_ident) \s+ ($table_ident)/ix; my %renames; while ( $alter =~ /$alter_change_col_re/g ) { From 055aef3d8b4d03ba5052773a7c324576511e0b5e Mon Sep 17 00:00:00 2001 From: Carlos Salguero Date: Wed, 17 May 2017 17:20:07 -0300 Subject: [PATCH 4/5] PT-138 Added json otput format to pt-mongodb-summary --- src/go/pt-mongodb-summary/main.go | 123 +++++++++++++++++-------- src/go/pt-mongodb-summary/main_test.go | 29 ++++-- 2 files changed, 106 insertions(+), 46 deletions(-) diff --git a/src/go/pt-mongodb-summary/main.go b/src/go/pt-mongodb-summary/main.go index e272b396..6d90e81c 100644 --- a/src/go/pt-mongodb-summary/main.go +++ b/src/go/pt-mongodb-summary/main.go @@ -1,11 +1,13 @@ package main import ( + "bytes" + "encoding/json" "fmt" + "html/template" "net" "os" "strings" - "text/template" "time" version "github.com/hashicorp/go-version" @@ -33,6 +35,7 @@ const ( DEFAULT_LOGLEVEL = "warn" DEFAULT_RUNNINGOPSINTERVAL = 1000 // milliseconds DEFAULT_RUNNINGOPSSAMPLES = 5 + DEFAULT_OUTPUT_FORMAT = "text" ) var ( @@ -130,12 +133,24 @@ type options struct { Version bool NoVersionCheck bool NoRunningOps bool + OutputFormat string RunningOpsSamples int RunningOpsInterval int SSLCAFile string SSLPEMKeyFile string } +type collectedInfo struct { + BalancerStats *proto.BalancerStats + ClusterWideInfo *clusterwideInfo + OplogInfo []proto.OplogInfo + ReplicaMembers []proto.Members + RunningOps *opCounters + SecuritySettings *security + HostInfo *hostInfo + Errors []string +} + func main() { opts, err := parseFlags() @@ -210,77 +225,105 @@ func main() { defer session.Close() session.SetMode(mgo.Monotonic, true) - hostInfo, err := GetHostinfo(session) + ci := &collectedInfo{} + + ci.HostInfo, err = GetHostinfo(session) if err != nil { message := fmt.Sprintf("Cannot get host info for %q: %s", di.Addrs[0], err.Error()) log.Errorf(message) os.Exit(2) } - if replicaMembers, err := util.GetReplicasetMembers(dialer, di); err != nil { + if ci.ReplicaMembers, err = util.GetReplicasetMembers(dialer, di); err != nil { log.Warnf("[Error] cannot get replicaset members: %v\n", err) os.Exit(2) - } else { - log.Debugf("replicaMembers:\n%+v\n", replicaMembers) - t := template.Must(template.New("replicas").Parse(templates.Replicas)) - t.Execute(os.Stdout, replicaMembers) } - - // Host Info - t := template.Must(template.New("hosttemplateData").Parse(templates.HostInfo)) - t.Execute(os.Stdout, hostInfo) + log.Debugf("replicaMembers:\n%+v\n", ci.ReplicaMembers) if opts.RunningOpsSamples > 0 && opts.RunningOpsInterval > 0 { - if rops, err := GetOpCountersStats(session, opts.RunningOpsSamples, time.Duration(opts.RunningOpsInterval)*time.Millisecond); err != nil { + if ci.RunningOps, err = GetOpCountersStats(session, opts.RunningOpsSamples, time.Duration(opts.RunningOpsInterval)*time.Millisecond); err != nil { log.Printf("[Error] cannot get Opcounters stats: %v\n", err) - } else { - t := template.Must(template.New("runningOps").Parse(templates.RunningOps)) - t.Execute(os.Stdout, rops) } } - if hostInfo != nil { - if security, err := GetSecuritySettings(session, hostInfo.Version); err != nil { + if ci.HostInfo != nil { + if ci.SecuritySettings, err = GetSecuritySettings(session, ci.HostInfo.Version); err != nil { log.Errorf("[Error] cannot get security settings: %v\n", err) - } else { - t := template.Must(template.New("ssl").Parse(templates.Security)) - t.Execute(os.Stdout, security) } } else { log.Warn("Cannot check security settings since host info is not available (permissions?)") } - if oplogInfo, err := oplog.GetOplogInfo(hostnames, di); err != nil { + if ci.OplogInfo, err = oplog.GetOplogInfo(hostnames, di); err != nil { log.Info("Cannot get Oplog info: %v\n", err) } else { - if len(oplogInfo) > 0 { - t := template.Must(template.New("oplogInfo").Parse(templates.Oplog)) - t.Execute(os.Stdout, oplogInfo[0]) - } else { - + if len(ci.OplogInfo) == 0 { log.Info("oplog info is empty. Skipping") + } else { + ci.OplogInfo = ci.OplogInfo[:1] } } // individual servers won't know about this info - if hostInfo.NodeType == "mongos" { - if cwi, err := GetClusterwideInfo(session); err != nil { + if ci.HostInfo.NodeType == "mongos" { + if ci.ClusterWideInfo, err = GetClusterwideInfo(session); err != nil { log.Printf("[Error] cannot get cluster wide info: %v\n", err) - } else { - t := template.Must(template.New("clusterwide").Parse(templates.Clusterwide)) - t.Execute(os.Stdout, cwi) } } - if hostInfo.NodeType == "mongos" { - if bs, err := GetBalancerStats(session); err != nil { + if ci.HostInfo.NodeType == "mongos" { + if ci.BalancerStats, err = GetBalancerStats(session); err != nil { log.Printf("[Error] cannot get balancer stats: %v\n", err) - } else { - t := template.Must(template.New("balancer").Parse(templates.BalancerStats)) - t.Execute(os.Stdout, bs) } } + out, err := formatResults(ci, opts.OutputFormat) + if err != nil { + log.Errorf("Cannot format the results: %s", err.Error()) + os.Exit(1) + } + fmt.Println(string(out)) + +} + +func formatResults(ci *collectedInfo, format string) ([]byte, error) { + var buf *bytes.Buffer + + switch format { + case "json": + b, err := json.MarshalIndent(ci, "", " ") + if err != nil { + return nil, fmt.Errorf("[Error] Cannot convert results to json: %s", err.Error()) + } + buf = bytes.NewBuffer(b) + default: + buf = new(bytes.Buffer) + + t := template.Must(template.New("replicas").Parse(templates.Replicas)) + t.Execute(buf, ci.ReplicaMembers) + + t = template.Must(template.New("hosttemplateData").Parse(templates.HostInfo)) + t.Execute(buf, ci.HostInfo) + + t = template.Must(template.New("runningOps").Parse(templates.RunningOps)) + t.Execute(buf, ci.RunningOps) + + t = template.Must(template.New("ssl").Parse(templates.Security)) + t.Execute(buf, ci.SecuritySettings) + + if ci.OplogInfo != nil && len(ci.OplogInfo) > 0 { + t = template.Must(template.New("oplogInfo").Parse(templates.Oplog)) + t.Execute(buf, ci.OplogInfo[0]) + } + + t = template.Must(template.New("clusterwide").Parse(templates.Clusterwide)) + t.Execute(buf, ci.ClusterWideInfo) + + t = template.Must(template.New("balancer").Parse(templates.BalancerStats)) + t.Execute(buf, ci.BalancerStats) + } + + return buf.Bytes(), nil } func GetHostinfo(session pmgo.SessionManager) (*hostInfo, error) { @@ -472,6 +515,7 @@ func GetSecuritySettings(session pmgo.SessionManager, ver string) (*security, er // Lets try both newSession := session.Clone() defer newSession.Close() + newSession.SetMode(mgo.Strong, true) if s.Users, s.Roles, err = getUserRolesCount(newSession); err != nil { @@ -811,6 +855,7 @@ func parseFlags() (*options, error) { RunningOpsSamples: DEFAULT_RUNNINGOPSSAMPLES, RunningOpsInterval: DEFAULT_RUNNINGOPSINTERVAL, // milliseconds AuthDB: DEFAULT_AUTHDB, + OutputFormat: DEFAULT_OUTPUT_FORMAT, } gop := getopt.New() @@ -821,8 +866,9 @@ func parseFlags() (*options, error) { gop.StringVarLong(&opts.User, "username", 'u', "", "Username to use for optional MongoDB authentication") gop.StringVarLong(&opts.Password, "password", 'p', "", "Password to use for optional MongoDB authentication").SetOptional() gop.StringVarLong(&opts.AuthDB, "authenticationDatabase", 'a', "admin", - "Databaae to use for optional MongoDB authentication. Default: admin") + "Database to use for optional MongoDB authentication. Default: admin") gop.StringVarLong(&opts.LogLevel, "log-level", 'l', "error", "Log level: panic, fatal, error, warn, info, debug. Default: error") + gop.StringVarLong(&opts.OutputFormat, "output-format", 'f', "text", "Output format: text, json. Default: text") gop.IntVarLong(&opts.RunningOpsSamples, "running-ops-samples", 's', fmt.Sprintf("Number of samples to collect for running ops. Default: %d", opts.RunningOpsSamples)) @@ -852,6 +898,9 @@ func parseFlags() (*options, error) { gop.PrintUsage(os.Stdout) return nil, nil } + if opts.OutputFormat != "json" && opts.OutputFormat != "text" { + log.Infof("Invalid output format '%s'. Using text format", opts.OutputFormat) + } return opts, nil } diff --git a/src/go/pt-mongodb-summary/main_test.go b/src/go/pt-mongodb-summary/main_test.go index 1129f06c..4872d786 100644 --- a/src/go/pt-mongodb-summary/main_test.go +++ b/src/go/pt-mongodb-summary/main_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + mgo "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/dbtest" @@ -205,6 +206,9 @@ func TestSecurityOpts(t *testing.T) { session.EXPECT().DB("admin").Return(database) database.EXPECT().Run(bson.D{{"getCmdLineOpts", 1}, {"recordStats", 1}}, gomock.Any()).SetArg(1, cmd) + session.EXPECT().Clone().Return(session) + session.EXPECT().SetMode(mgo.Strong, true) + session.EXPECT().DB("admin").Return(database) database.EXPECT().C("system.users").Return(usersCol) usersCol.EXPECT().Count().Return(1, nil) @@ -212,6 +216,7 @@ func TestSecurityOpts(t *testing.T) { session.EXPECT().DB("admin").Return(database) database.EXPECT().C("system.roles").Return(rolesCol) rolesCol.EXPECT().Count().Return(2, nil) + session.EXPECT().Close().Return() got, err := GetSecuritySettings(session, "3.2") @@ -392,21 +397,25 @@ func TestParseArgs(t *testing.T) { { args: []string{TOOLNAME}, // arg[0] is the command itself want: &options{ - Host: DEFAULT_HOST, - LogLevel: DEFAULT_LOGLEVEL, - AuthDB: DEFAULT_AUTHDB, + Host: DEFAULT_HOST, + LogLevel: DEFAULT_LOGLEVEL, + AuthDB: DEFAULT_AUTHDB, + RunningOpsSamples: DEFAULT_RUNNINGOPSSAMPLES, + RunningOpsInterval: DEFAULT_RUNNINGOPSINTERVAL, + OutputFormat: "text", }, }, { args: []string{TOOLNAME, "zapp.brannigan.net:27018/samples", "--help"}, - want: &options{ - Host: "zapp.brannigan.net:27018/samples", - LogLevel: DEFAULT_LOGLEVEL, - AuthDB: DEFAULT_AUTHDB, - Help: true, - }, + want: nil, }, } + + // Capture stdout to not to show help + old := os.Stdout // keep backup of the real stdout + _, w, _ := os.Pipe() + os.Stdout = w + for i, test := range tests { getopt.Reset() os.Args = test.args @@ -419,4 +428,6 @@ func TestParseArgs(t *testing.T) { } } + os.Stdout = old + } From 33953de0e8234792887f9bf1b8e2ec48bde3f970 Mon Sep 17 00:00:00 2001 From: Carlos Salguero Date: Tue, 23 May 2017 14:19:31 -0300 Subject: [PATCH 5/5] Updated changelog --- Changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog b/Changelog index 300c88d3..8fcaa67b 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,9 @@ Changelog for Percona Toolkit +v3.0.4 + + * Fixed bug PT-138 : Added --output-format option to pt-mongodb-summary + v3.0.3 * Fixed bug PT-133 : Sandbox won't start correctly if autocommit=0 in my.cnf