github.com/snowflakedb/gosnowflake@v1.9.0/transaction_test.go (about) 1 // Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved. 2 3 package gosnowflake 4 5 import ( 6 "context" 7 "database/sql" 8 "errors" 9 "fmt" 10 "testing" 11 "time" 12 ) 13 14 func TestTransactionOptions(t *testing.T) { 15 var tx *sql.Tx 16 var err error 17 18 conn := openConn(t) 19 defer conn.Close() 20 21 tx, err = conn.BeginTx(context.Background(), &sql.TxOptions{}) 22 if err != nil { 23 t.Fatal("failed to start transaction.") 24 } 25 if err = tx.Rollback(); err != nil { 26 t.Fatal("failed to rollback") 27 } 28 if _, err = conn.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}); err == nil { 29 t.Fatal("should have failed.") 30 } 31 if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoReadOnlyTransaction { 32 t.Fatalf("should have returned Snowflake Error: %v", errMsgNoReadOnlyTransaction) 33 } 34 if _, err = conn.BeginTx(context.Background(), &sql.TxOptions{Isolation: 100}); err == nil { 35 t.Fatal("should have failed.") 36 } 37 if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoDefaultTransactionIsolationLevel { 38 t.Fatalf("should have returned Snowflake Error: %v", errMsgNoDefaultTransactionIsolationLevel) 39 } 40 } 41 42 // SNOW-823072: Test that transaction uses the context object supplied by BeginTx(), not from the parent connection 43 func TestTransactionContext(t *testing.T) { 44 var tx *sql.Tx 45 var err error 46 47 conn := openConn(t) 48 defer conn.Close() 49 50 ctx := context.Background() 51 52 pingWithRetry := withRetry(PingFunc, 5, 3*time.Second) 53 54 err = pingWithRetry(context.Background(), conn) 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 tx, err = conn.BeginTx(ctx, nil) 60 if err != nil { 61 t.Fatal(err) 62 } 63 defer tx.Rollback() 64 65 _, err = tx.ExecContext(ctx, "SELECT SYSTEM$WAIT(10, 'SECONDS')") 66 if err != nil { 67 t.Fatal(err) 68 } 69 70 err = tx.Commit() 71 if err != nil { 72 t.Fatal(err) 73 } 74 } 75 76 func PingFunc(ctx context.Context, conn *sql.Conn) error { 77 return conn.PingContext(ctx) 78 } 79 80 // Helper function for SNOW-823072 repro 81 func withRetry(fn func(context.Context, *sql.Conn) error, numAttempts int, timeout time.Duration) func(context.Context, *sql.Conn) error { 82 return func(ctx context.Context, db *sql.Conn) error { 83 for currAttempt := 1; currAttempt <= numAttempts; currAttempt++ { 84 ctx, cancel := context.WithTimeout(ctx, timeout) 85 defer cancel() 86 err := fn(ctx, db) 87 if err != nil { 88 if errors.Is(err, context.DeadlineExceeded) { 89 continue 90 } 91 return err 92 } 93 return nil 94 } 95 return fmt.Errorf("context deadline exceeded, failed after [%d] attempts", numAttempts) 96 } 97 } 98 99 func TestTransactionError(t *testing.T) { 100 sr := &snowflakeRestful{ 101 FuncPostQuery: postQueryFail, 102 } 103 104 tx := snowflakeTx{ 105 sc: &snowflakeConn{ 106 cfg: &Config{Params: map[string]*string{}}, 107 rest: sr, 108 }, 109 ctx: context.Background(), 110 } 111 112 // test for post query error when executing the txCommand 113 err := tx.execTxCommand(rollback) 114 assertNotNilF(t, err, "") 115 assertEqualE(t, err.Error(), "failed to get query response") 116 117 // test for invalid txCommand 118 err = tx.execTxCommand(2) 119 assertNotNilF(t, err, "") 120 assertEqualE(t, err.Error(), "unsupported transaction command") 121 122 // test for bad connection error when snowflakeConn is nil 123 tx.sc = nil 124 err = tx.execTxCommand(rollback) 125 assertNotNilF(t, err, "") 126 assertEqualE(t, err.Error(), "driver: bad connection") 127 }