Skip to content

Commit 94d1338

Browse files
committed
vttablet: new flag: enable_strict_trans_tables.
Many users are running MySQL with this flag turned off, and it's non-trivial to turn it on due to legacy reasons. This flag allows you to keep going. The associated risk of using this flag is in the description of the command line argument.
1 parent 2a8964b commit 94d1338

File tree

5 files changed

+87
-19
lines changed

5 files changed

+87
-19
lines changed

go/vt/vtgate/executor_dml_test.go

+17-6
Original file line numberDiff line numberDiff line change
@@ -787,11 +787,14 @@ func TestInsertFail(t *testing.T) {
787787
}
788788
}
789789

790-
func TestInsertPartialFail(t *testing.T) {
791-
executor, sbc1, _, sbclookup := createExecutorEnv()
790+
// If a statement gets broken up into two, and the first one fails,
791+
// then an error should be returned normally.
792+
func TestInsertPartialFail1(t *testing.T) {
793+
executor, _, _, sbclookup := createExecutorEnv()
792794

793-
// If the first DML fails, there should be no rollback.
795+
// Make the first DML fail, there should be no rollback.
794796
sbclookup.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
797+
795798
_, err := executor.Execute(
796799
context.Background(),
797800
&vtgatepb.Session{InTransaction: true},
@@ -802,16 +805,24 @@ func TestInsertPartialFail(t *testing.T) {
802805
if err == nil || !strings.HasPrefix(err.Error(), want) {
803806
t.Errorf("insert first DML fail: %v, must start with %s", err, want)
804807
}
808+
}
805809

806-
// If the second DML fails, we should rollback.
810+
// If a statement gets broken up into two, and the second one fails
811+
// after successful execution of the first, then the transaction must
812+
// be rolled back due to partial execution.
813+
func TestInsertPartialFail2(t *testing.T) {
814+
executor, sbc1, _, _ := createExecutorEnv()
815+
816+
// Make the second DML fail, it should result in a rollback.
807817
sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
808-
_, err = executor.Execute(
818+
819+
_, err := executor.Execute(
809820
context.Background(),
810821
&vtgatepb.Session{InTransaction: true},
811822
"insert into user(id, v, name) values (1, 2, 'myname')",
812823
nil,
813824
)
814-
want = "transaction rolled back"
825+
want := "transaction rolled back"
815826
if err == nil || !strings.HasPrefix(err.Error(), want) {
816827
t.Errorf("insert first DML fail: %v, must start with %s", err, want)
817828
}

go/vt/vttablet/tabletserver/connpool/dbconn.go

+13-11
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,20 @@ var (
177177
// VerifyMode is a helper method to verify mysql is running with
178178
// sql_mode = STRICT_TRANS_TABLES and autocommit=ON. It also returns
179179
// the current binlog format.
180-
func (dbc *DBConn) VerifyMode() (BinlogFormat, error) {
181-
qr, err := dbc.conn.ExecuteFetch(getModeSQL, 2, false)
182-
if err != nil {
183-
return 0, fmt.Errorf("could not verify mode: %v", err)
184-
}
185-
if len(qr.Rows) != 1 {
186-
return 0, fmt.Errorf("incorrect rowcount received for %s: %d", getModeSQL, len(qr.Rows))
187-
}
188-
if !strings.Contains(qr.Rows[0][0].String(), "STRICT_TRANS_TABLES") {
189-
return 0, fmt.Errorf("require sql_mode to be STRICT_TRANS_TABLES: got %s", qr.Rows[0][0].String())
180+
func (dbc *DBConn) VerifyMode(strictTransTables bool) (BinlogFormat, error) {
181+
if strictTransTables {
182+
qr, err := dbc.conn.ExecuteFetch(getModeSQL, 2, false)
183+
if err != nil {
184+
return 0, fmt.Errorf("could not verify mode: %v", err)
185+
}
186+
if len(qr.Rows) != 1 {
187+
return 0, fmt.Errorf("incorrect rowcount received for %s: %d", getModeSQL, len(qr.Rows))
188+
}
189+
if !strings.Contains(qr.Rows[0][0].String(), "STRICT_TRANS_TABLES") {
190+
return 0, fmt.Errorf("require sql_mode to be STRICT_TRANS_TABLES: got '%s'", qr.Rows[0][0].String())
191+
}
190192
}
191-
qr, err = dbc.conn.ExecuteFetch(getAutocommit, 2, false)
193+
qr, err := dbc.conn.ExecuteFetch(getAutocommit, 2, false)
192194
if err != nil {
193195
return 0, fmt.Errorf("could not verify mode: %v", err)
194196
}

go/vt/vttablet/tabletserver/query_engine.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ type QueryEngine struct {
132132
// TODO(sougou) There are two acl packages. Need to rename.
133133
exemptACL tacl.ACL
134134

135+
strictTransTables bool
136+
135137
// Loggers
136138
accessCheckerLogger *logutil.ThrottledLogger
137139
}
@@ -173,6 +175,8 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab
173175
qe.strictTableACL = config.StrictTableACL
174176
qe.enableTableACLDryRun = config.EnableTableACLDryRun
175177

178+
qe.strictTransTables = config.EnforceStrictTransTables
179+
176180
if config.TableACLExemptACL != "" {
177181
if f, err := tableacl.GetCurrentAclFactory(); err == nil {
178182
if exemptACL, err := f.New([]string{config.TableACLExemptACL}); err == nil {
@@ -235,7 +239,7 @@ func (qe *QueryEngine) Open(dbconfigs dbconfigs.DBConfigs) error {
235239
qe.conns.Close()
236240
return err
237241
}
238-
qe.binlogFormat, err = conn.VerifyMode()
242+
qe.binlogFormat, err = conn.VerifyMode(qe.strictTransTables)
239243
conn.Recycle()
240244

241245
if err != nil {

go/vt/vttablet/tabletserver/query_engine_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,56 @@ import (
1515

1616
"github.com/youtube/vitess/go/mysqlconn/fakesqldb"
1717
"github.com/youtube/vitess/go/sqltypes"
18+
querypb "github.com/youtube/vitess/go/vt/proto/query"
1819
"github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema"
1920
"github.com/youtube/vitess/go/vt/vttablet/tabletserver/schema/schematest"
2021
"github.com/youtube/vitess/go/vt/vttablet/tabletserver/tabletenv"
2122
)
2223

24+
func TestStrictTransTables(t *testing.T) {
25+
db := fakesqldb.New(t)
26+
defer db.Close()
27+
for query, result := range schematest.Queries() {
28+
db.AddQuery(query, result)
29+
}
30+
testUtils := newTestUtils()
31+
dbconfigs := testUtils.newDBConfigs(db)
32+
33+
// Test default behavior.
34+
config := tabletenv.DefaultQsConfig
35+
// config.EnforceStrictTransTable is true by default.
36+
qe := NewQueryEngine(DummyChecker, schema.NewEngine(DummyChecker, config), config)
37+
qe.se.Open(db.ConnParams())
38+
if err := qe.Open(dbconfigs); err != nil {
39+
t.Error(err)
40+
}
41+
qe.Close()
42+
43+
// Check that we fail if STRICT_TRANS_TABLES is not set.
44+
db.AddQuery(
45+
"select @@global.sql_mode",
46+
&sqltypes.Result{
47+
Fields: []*querypb.Field{{Type: sqltypes.VarChar}},
48+
Rows: [][]sqltypes.Value{{sqltypes.MakeString([]byte(""))}},
49+
},
50+
)
51+
qe = NewQueryEngine(DummyChecker, schema.NewEngine(DummyChecker, config), config)
52+
err := qe.Open(dbconfigs)
53+
wantErr := "require sql_mode to be STRICT_TRANS_TABLES: got ''"
54+
if err == nil || err.Error() != wantErr {
55+
t.Errorf("Open: %v, want %s", err, wantErr)
56+
}
57+
qe.Close()
58+
59+
// Test that we succeed if the enforcement flag is off.
60+
config.EnforceStrictTransTables = false
61+
qe = NewQueryEngine(DummyChecker, schema.NewEngine(DummyChecker, config), config)
62+
if err := qe.Open(dbconfigs); err != nil {
63+
t.Error(err)
64+
}
65+
qe.Close()
66+
}
67+
2368
func TestGetPlanPanicDuetoEmptyQuery(t *testing.T) {
2469
db := fakesqldb.New(t)
2570
defer db.Close()

go/vt/vttablet/tabletserver/tabletenv/config.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func init() {
5454
flag.BoolVar(&Config.TerseErrors, "queryserver-config-terse-errors", DefaultQsConfig.TerseErrors, "prevent bind vars from escaping in returned errors")
5555
flag.StringVar(&Config.PoolNamePrefix, "pool-name-prefix", DefaultQsConfig.PoolNamePrefix, "pool name prefix, vttablet has several pools and each of them has a name. This config specifies the prefix of these pool names")
5656
flag.BoolVar(&Config.WatchReplication, "watch_replication_stream", false, "When enabled, vttablet will stream the MySQL replication stream from the local server, and use it to support the include_event_token ExecuteOptions.")
57-
flag.BoolVar(&Config.EnableAutoCommit, "enable-autocommit", DefaultQsConfig.EnableAutoCommit, "if the flag is on, a DML outsides a transaction will be auto committed.")
57+
flag.BoolVar(&Config.EnableAutoCommit, "enable-autocommit", DefaultQsConfig.EnableAutoCommit, "if the flag is on, a DML outsides a transaction will be auto committed. This flag is deprecated and is unsafe. Instead, use the VTGate provided autocommit feature.")
5858
flag.BoolVar(&Config.TwoPCEnable, "twopc_enable", DefaultQsConfig.TwoPCEnable, "if the flag is on, 2pc is enabled. Other 2pc flags must be supplied.")
5959
flag.StringVar(&Config.TwoPCCoordinatorAddress, "twopc_coordinator_address", DefaultQsConfig.TwoPCCoordinatorAddress, "address of the (VTGate) process(es) that will be used to notify of abandoned transactions.")
6060
flag.Float64Var(&Config.TwoPCAbandonAge, "twopc_abandon_age", DefaultQsConfig.TwoPCAbandonAge, "time in seconds. Any unresolved transaction older than this time will be sent to the coordinator to be resolved.")
@@ -69,6 +69,8 @@ func init() {
6969

7070
flag.BoolVar(&Config.HeartbeatEnable, "heartbeat_enable", DefaultQsConfig.HeartbeatEnable, "If true, vttablet records (if master) or checks (if replica) the current time of a replication heartbeat in the table _vt.heartbeat. The result is used to inform the serving state of the vttablet via healthchecks.")
7171
flag.DurationVar(&Config.HeartbeatInterval, "heartbeat_interval", DefaultQsConfig.HeartbeatInterval, "How frequently to read and write replication heartbeat.")
72+
73+
flag.BoolVar(&Config.EnforceStrictTransTables, "enforce_strict_trans_tables", DefaultQsConfig.EnforceStrictTransTables, "If true, vttablet requires MySQL to run with STRICT_TRANS_TABLES on. It is recommended to not turn this flag off. Otherwise MySQL may alter your supplied values before saving them to the database.")
7274
}
7375

7476
// Init must be called after flag.Parse, and before doing any other operations.
@@ -115,6 +117,8 @@ type TabletConfig struct {
115117

116118
HeartbeatEnable bool
117119
HeartbeatInterval time.Duration
120+
121+
EnforceStrictTransTables bool
118122
}
119123

120124
// DefaultQsConfig is the default value for the query service config.
@@ -162,6 +166,8 @@ var DefaultQsConfig = TabletConfig{
162166

163167
HeartbeatEnable: false,
164168
HeartbeatInterval: 1 * time.Second,
169+
170+
EnforceStrictTransTables: true,
165171
}
166172

167173
// defaultTxThrottlerConfig formats the default throttlerdata.Configuration

0 commit comments

Comments
 (0)