Resplit Begin and BeginEx

This is in preparation for a Begin / Tx interface that will similate
nested transactions with savepoints.

In addition, this passes the TxOptions struct by value and thereby
removes an allocation.
pull/586/head
Jack Christensen 2019-08-17 15:53:55 -05:00
parent f3c703a102
commit c3e41872a8
9 changed files with 44 additions and 35 deletions

View File

@ -463,7 +463,7 @@ func TestTxSendBatch(t *testing.T) {
);` );`
mustExec(t, conn, sql) mustExec(t, conn, sql)
tx, _ := conn.Begin(context.Background(), nil) tx, _ := conn.Begin(context.Background())
batch := &pgx.Batch{} batch := &pgx.Batch{}
batch.Queue("insert into ledger1(description) values($1) returning id", batch.Queue("insert into ledger1(description) values($1) returning id",
[]interface{}{"q1"}, []interface{}{"q1"},
@ -538,7 +538,7 @@ func TestTxSendBatchRollback(t *testing.T) {
);` );`
mustExec(t, conn, sql) mustExec(t, conn, sql)
tx, _ := conn.Begin(context.Background(), nil) tx, _ := conn.Begin(context.Background())
batch := &pgx.Batch{} batch := &pgx.Batch{}
batch.Queue("insert into ledger1(description) values($1) returning id", batch.Queue("insert into ledger1(description) values($1) returning id",
[]interface{}{"q1"}, []interface{}{"q1"},

View File

@ -413,7 +413,7 @@ func benchmarkWriteNRowsViaInsert(b *testing.B, n int) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
src := newBenchmarkWriteTableCopyFromSrc(n) src := newBenchmarkWriteTableCopyFromSrc(n)
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -452,7 +452,7 @@ func multiInsert(conn *pgx.Conn, tableName string, columnNames []string, rowSrc
} }
resetQuery() resetQuery()
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -22,7 +22,7 @@ func TestLargeObjects(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
tx, err := conn.Begin(ctx, nil) tx, err := conn.Begin(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -135,7 +135,7 @@ func TestLargeObjectsMultipleTransactions(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
tx, err := conn.Begin(ctx, nil) tx, err := conn.Begin(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -175,7 +175,7 @@ func TestLargeObjectsMultipleTransactions(t *testing.T) {
rows.Close() rows.Close()
// Start a new transaction // Start a new transaction
tx2, err := conn.Begin(ctx, nil) tx2, err := conn.Begin(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -66,8 +66,12 @@ func (c *Conn) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNam
return c.Conn().CopyFrom(ctx, tableName, columnNames, rowSrc) return c.Conn().CopyFrom(ctx, tableName, columnNames, rowSrc)
} }
func (c *Conn) Begin(ctx context.Context, txOptions *pgx.TxOptions) (*pgx.Tx, error) { func (c *Conn) Begin(ctx context.Context) (*pgx.Tx, error) {
return c.Conn().Begin(ctx, txOptions) return c.Conn().Begin(ctx)
}
func (c *Conn) BeginEx(ctx context.Context, txOptions pgx.TxOptions) (*pgx.Tx, error) {
return c.Conn().BeginEx(ctx, txOptions)
} }
func (c *Conn) Conn() *pgx.Conn { func (c *Conn) Conn() *pgx.Conn {

View File

@ -352,13 +352,16 @@ func (p *Pool) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults {
return &poolBatchResults{br: br, c: c} return &poolBatchResults{br: br, c: c}
} }
func (p *Pool) Begin(ctx context.Context, txOptions *pgx.TxOptions) (*Tx, error) { func (p *Pool) Begin(ctx context.Context) (*Tx, error) {
return p.BeginEx(ctx, pgx.TxOptions{})
}
func (p *Pool) BeginEx(ctx context.Context, txOptions pgx.TxOptions) (*Tx, error) {
c, err := p.Acquire(ctx) c, err := p.Acquire(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t, err := c.Begin(ctx, txOptions) t, err := c.BeginEx(ctx, txOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -16,7 +16,7 @@ func TestTxExec(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer pool.Close() defer pool.Close()
tx, err := pool.Begin(context.Background(), nil) tx, err := pool.Begin(context.Background())
require.NoError(t, err) require.NoError(t, err)
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())
@ -30,7 +30,7 @@ func TestTxQuery(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer pool.Close() defer pool.Close()
tx, err := pool.Begin(context.Background(), nil) tx, err := pool.Begin(context.Background())
require.NoError(t, err) require.NoError(t, err)
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())
@ -44,7 +44,7 @@ func TestTxQueryRow(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer pool.Close() defer pool.Close()
tx, err := pool.Begin(context.Background(), nil) tx, err := pool.Begin(context.Background())
require.NoError(t, err) require.NoError(t, err)
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())
@ -58,7 +58,7 @@ func TestTxSendBatch(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer pool.Close() defer pool.Close()
tx, err := pool.Begin(context.Background(), nil) tx, err := pool.Begin(context.Background())
require.NoError(t, err) require.NoError(t, err)
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())
@ -72,7 +72,7 @@ func TestTxCopyFrom(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer pool.Close() defer pool.Close()
tx, err := pool.Begin(context.Background(), nil) tx, err := pool.Begin(context.Background())
require.NoError(t, err) require.NoError(t, err)
defer tx.Rollback(context.Background()) defer tx.Rollback(context.Background())

View File

@ -209,7 +209,7 @@ func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e
pgxOpts.AccessMode = pgx.ReadOnly pgxOpts.AccessMode = pgx.ReadOnly
} }
tx, err := c.conn.Begin(ctx, &pgxOpts) tx, err := c.conn.BeginEx(ctx, pgxOpts)
if err != nil { if err != nil {
return nil, err return nil, err
} }

18
tx.go
View File

@ -50,11 +50,7 @@ type TxOptions struct {
DeferrableMode TxDeferrableMode DeferrableMode TxDeferrableMode
} }
func (txOptions *TxOptions) beginSQL() string { func (txOptions TxOptions) beginSQL() string {
if txOptions == nil {
return "begin"
}
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteString("begin") buf.WriteString("begin")
if txOptions.IsoLevel != "" { if txOptions.IsoLevel != "" {
@ -78,9 +74,15 @@ var ErrTxInFailure = errors.New("tx failed")
// it is treated as ROLLBACK. // it is treated as ROLLBACK.
var ErrTxCommitRollback = errors.New("commit unexpectedly resulted in rollback") var ErrTxCommitRollback = errors.New("commit unexpectedly resulted in rollback")
// BeginEx starts a transaction with txOptions determining the transaction mode. txOptions can be nil. Unlike // Begin starts a transaction. Unlike database/sql, the context only affects the begin command. i.e. there is no
// database/sql, the context only affects the begin command. i.e. there is no auto-rollback on context cancelation. // auto-rollback on context cancelation.
func (c *Conn) Begin(ctx context.Context, txOptions *TxOptions) (*Tx, error) { func (c *Conn) Begin(ctx context.Context) (*Tx, error) {
return c.BeginEx(ctx, TxOptions{})
}
// BeginEx starts a transaction with txOptions determining the transaction mode. Unlike database/sql, the context only
// affects the begin command. i.e. there is no auto-rollback on context cancelation.
func (c *Conn) BeginEx(ctx context.Context, txOptions TxOptions) (*Tx, error) {
_, err := c.Exec(ctx, txOptions.beginSQL()) _, err := c.Exec(ctx, txOptions.beginSQL())
if err != nil { if err != nil {
// begin should never fail unless there is an underlying connection issue or // begin should never fail unless there is an underlying connection issue or

View File

@ -26,7 +26,7 @@ func TestTransactionSuccessfulCommit(t *testing.T) {
t.Fatalf("Failed to create table: %v", err) t.Fatalf("Failed to create table: %v", err)
} }
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatalf("conn.Begin failed: %v", err) t.Fatalf("conn.Begin failed: %v", err)
} }
@ -68,7 +68,7 @@ func TestTxCommitWhenTxBroken(t *testing.T) {
t.Fatalf("Failed to create table: %v", err) t.Fatalf("Failed to create table: %v", err)
} }
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatalf("conn.Begin failed: %v", err) t.Fatalf("conn.Begin failed: %v", err)
} }
@ -113,13 +113,13 @@ func TestTxCommitSerializationFailure(t *testing.T) {
} }
defer c1.Exec(context.Background(), `drop table tx_serializable_sums`) defer c1.Exec(context.Background(), `drop table tx_serializable_sums`)
tx1, err := c1.Begin(context.Background(), &pgx.TxOptions{IsoLevel: pgx.Serializable}) tx1, err := c1.BeginEx(context.Background(), pgx.TxOptions{IsoLevel: pgx.Serializable})
if err != nil { if err != nil {
t.Fatalf("Begin failed: %v", err) t.Fatalf("Begin failed: %v", err)
} }
defer tx1.Rollback(context.Background()) defer tx1.Rollback(context.Background())
tx2, err := c2.Begin(context.Background(), &pgx.TxOptions{IsoLevel: pgx.Serializable}) tx2, err := c2.BeginEx(context.Background(), pgx.TxOptions{IsoLevel: pgx.Serializable})
if err != nil { if err != nil {
t.Fatalf("Begin failed: %v", err) t.Fatalf("Begin failed: %v", err)
} }
@ -163,7 +163,7 @@ func TestTransactionSuccessfulRollback(t *testing.T) {
t.Fatalf("Failed to create table: %v", err) t.Fatalf("Failed to create table: %v", err)
} }
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatalf("conn.Begin failed: %v", err) t.Fatalf("conn.Begin failed: %v", err)
} }
@ -196,7 +196,7 @@ func TestBeginIsoLevels(t *testing.T) {
isoLevels := []pgx.TxIsoLevel{pgx.Serializable, pgx.RepeatableRead, pgx.ReadCommitted, pgx.ReadUncommitted} isoLevels := []pgx.TxIsoLevel{pgx.Serializable, pgx.RepeatableRead, pgx.ReadCommitted, pgx.ReadUncommitted}
for _, iso := range isoLevels { for _, iso := range isoLevels {
tx, err := conn.Begin(context.Background(), &pgx.TxOptions{IsoLevel: iso}) tx, err := conn.BeginEx(context.Background(), pgx.TxOptions{IsoLevel: iso})
if err != nil { if err != nil {
t.Fatalf("conn.Begin failed: %v", err) t.Fatalf("conn.Begin failed: %v", err)
} }
@ -220,7 +220,7 @@ func TestBeginReadOnly(t *testing.T) {
conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
defer closeConn(t, conn) defer closeConn(t, conn)
tx, err := conn.Begin(context.Background(), &pgx.TxOptions{AccessMode: pgx.ReadOnly}) tx, err := conn.BeginEx(context.Background(), pgx.TxOptions{AccessMode: pgx.ReadOnly})
if err != nil { if err != nil {
t.Fatalf("conn.Begin failed: %v", err) t.Fatalf("conn.Begin failed: %v", err)
} }
@ -238,7 +238,7 @@ func TestTxStatus(t *testing.T) {
conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
defer closeConn(t, conn) defer closeConn(t, conn)
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -262,7 +262,7 @@ func TestTxStatusErrorInTransactions(t *testing.T) {
conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
defer closeConn(t, conn) defer closeConn(t, conn)
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -309,7 +309,7 @@ func TestTxErr(t *testing.T) {
conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE")) conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
defer closeConn(t, conn) defer closeConn(t, conn)
tx, err := conn.Begin(context.Background(), nil) tx, err := conn.Begin(context.Background())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }