Skip to content

Commit 124ead1

Browse files
committed
support READ-ONLY transactions
1 parent 14f4292 commit 124ead1

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

connection.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,21 @@ func (mc *mysqlConn) markBadConn(err error) error {
9292
}
9393

9494
func (mc *mysqlConn) Begin() (driver.Tx, error) {
95+
return mc.begin(false)
96+
}
97+
98+
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
9599
if mc.closed.IsSet() {
96100
errLog.Print(ErrInvalidConn)
97101
return nil, driver.ErrBadConn
98102
}
99-
err := mc.exec("START TRANSACTION")
103+
var q string
104+
if readOnly {
105+
q = "START TRANSACTION READ ONLY"
106+
} else {
107+
q = "START TRANSACTION"
108+
}
109+
err := mc.exec(q)
100110
if err == nil {
101111
return &mysqlTx{mc}, err
102112
}

connection_go18.go

+1-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"context"
1515
"database/sql"
1616
"database/sql/driver"
17-
"errors"
1817
)
1918

2019
// Ping implements driver.Pinger interface
@@ -41,15 +40,9 @@ func (mc *mysqlConn) Ping(ctx context.Context) error {
4140

4241
// BeginTx implements driver.ConnBeginTx interface
4342
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
44-
if opts.ReadOnly {
45-
// TODO: support read-only transactions
46-
return nil, errors.New("mysql: read-only transactions not supported")
47-
}
48-
4943
if err := mc.watchCancel(ctx); err != nil {
5044
return nil, err
5145
}
52-
5346
defer mc.finish()
5447

5548
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
@@ -63,7 +56,7 @@ func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver
6356
}
6457
}
6558

66-
return mc.Begin()
59+
return mc.begin(opts.ReadOnly)
6760
}
6861

6962
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {

driver_go18_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,41 @@ func TestContextBeginIsolationLevel(t *testing.T) {
520520
tx2.Commit()
521521
})
522522
}
523+
524+
func TestContextBeginReadOnly(t *testing.T) {
525+
runTests(t, dsn, func(dbt *DBTest) {
526+
dbt.mustExec("CREATE TABLE test (v INTEGER)")
527+
ctx, cancel := context.WithCancel(context.Background())
528+
defer cancel()
529+
530+
tx, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
531+
ReadOnly: true,
532+
})
533+
if _, ok := err.(*MySQLError); ok {
534+
dbt.Skip("It seems that your MySQL does not support READ ONLY transactions")
535+
return
536+
} else if err != nil {
537+
dbt.Fatal(err)
538+
}
539+
540+
// INSERT queries fail in a READ ONLY transaction.
541+
_, err = tx.ExecContext(ctx, "INSERT INTO test VALUES (1)")
542+
if _, ok := err.(*MySQLError); !ok {
543+
dbt.Errorf("expected MySQLError, got %v", err)
544+
}
545+
546+
// SELECT queries can be executed.
547+
var v int
548+
row := tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
549+
if err := row.Scan(&v); err != nil {
550+
dbt.Fatal(err)
551+
}
552+
if v != 0 {
553+
dbt.Errorf("expected val to be 0, got %d", v)
554+
}
555+
556+
if err := tx.Commit(); err != nil {
557+
dbt.Fatal(err)
558+
}
559+
})
560+
}

0 commit comments

Comments
 (0)