github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/tests/integration/tx_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package integration 5 6 import ( 7 "database/sql" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/ydb-platform/ydb-go-sdk/v3" 13 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql" 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" 15 "github.com/ydb-platform/ydb-go-sdk/v3/table" 16 "github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed" 17 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 18 ) 19 20 func TestNoEffectsIfForgetCommitTx(t *testing.T) { 21 var ( 22 ctx = xtest.Context(t) 23 scope = newScope(t) 24 nativeDriver = scope.Driver() 25 db = scope.SQLDriver(ydb.WithTablePathPrefix(scope.Folder()), ydb.WithAutoDeclare(), ydb.WithNumericArgs()) 26 tablePath = scope.TablePath() // for auto-create table 27 ) 28 29 t.Run("native", func(t *testing.T) { 30 id := uint64(123) 31 32 // create session 33 s, err := nativeDriver.Table().CreateSession(ctx) //nolint: 34 require.NoError(t, err) 35 36 // tx1 (without commit) 37 tx1, err := s.BeginTransaction(ctx, table.TxSettings(table.WithSerializableReadWrite())) 38 require.NoError(t, err) 39 40 // upsert data inside tx1 41 _, err = tx1.Execute(ctx, ` 42 DECLARE $p1 AS Uint64; 43 DECLARE $p2 AS Text; 44 UPSERT INTO `+"`"+tablePath+"`"+` ( 45 id, val 46 ) VALUES ( 47 $p1, $p2 48 );`, 49 table.NewQueryParameters( 50 table.ValueParam("$p1", types.Uint64Value(id)), 51 table.ValueParam("$p2", types.TextValue("1st tx")), 52 ), 53 ) 54 require.NoError(t, err) 55 56 // check for NO persist data from tx1 57 _, result, err := s.Execute(ctx, table.DefaultTxControl(), ` 58 DECLARE $p1 AS Uint64; 59 SELECT val FROM `+"`"+tablePath+"`"+` 60 WHERE id = $p1;`, 61 table.NewQueryParameters( 62 table.ValueParam("$p1", types.Uint64Value(id)), 63 ), 64 ) 65 require.NoError(t, err) 66 require.NoError(t, result.NextResultSetErr(ctx)) 67 require.False(t, result.NextRow()) 68 69 // tx2 (with commit) 70 tx2, err := s.BeginTransaction(ctx, table.TxSettings(table.WithSerializableReadWrite())) 71 require.NoError(t, err) 72 73 // check for NO data from tx1 74 result, err = tx2.Execute(ctx, ` 75 DECLARE $p1 AS Uint64; 76 SELECT val FROM `+"`"+tablePath+"`"+` 77 WHERE id = $p1;`, 78 table.NewQueryParameters( 79 table.ValueParam("$p1", types.Uint64Value(id)), 80 ), 81 ) 82 require.NoError(t, err) 83 require.NoError(t, result.NextResultSetErr(ctx)) 84 require.False(t, result.NextRow()) 85 86 // upsert data inside tx2 87 _, err = tx2.Execute(ctx, ` 88 DECLARE $p1 AS Uint64; 89 DECLARE $p2 AS Text; 90 UPSERT INTO `+"`"+tablePath+"`"+` ( 91 id, val 92 ) VALUES ( 93 $p1, $p2 94 );`, 95 table.NewQueryParameters( 96 table.ValueParam("$p1", types.Uint64Value(id)), 97 table.ValueParam("$p2", types.TextValue("2nd tx")), 98 ), 99 ) 100 require.NoError(t, err) 101 // commit tx2 102 _, err = tx2.CommitTx(ctx) 103 require.NoError(t, err) 104 105 // check for persist data from tx2 106 _, result, err = s.Execute(ctx, table.DefaultTxControl(), ` 107 DECLARE $p1 AS Uint64; 108 SELECT val FROM `+"`"+tablePath+"`"+` 109 WHERE id = $p1;`, 110 table.NewQueryParameters( 111 table.ValueParam("$p1", types.Uint64Value(id)), 112 ), 113 ) 114 require.NoError(t, err) 115 require.NoError(t, result.NextResultSetErr(ctx)) 116 require.True(t, result.NextRow()) 117 118 var value *string 119 require.NoError(t, result.Scan(indexed.Optional(&value))) 120 require.NoError(t, result.Err()) 121 122 require.NotNil(t, value) 123 require.Equal(t, "2nd tx", *value) 124 }) 125 126 t.Run("database/sql", func(t *testing.T) { 127 id := uint64(456) 128 129 // create connection === YDB table session 130 cc, err := db.Conn(ctx) 131 require.NoError(t, err) 132 133 // first tx with no commit 134 tx1, err := cc.BeginTx(ctx, &sql.TxOptions{}) 135 require.NoError(t, err) 136 _, err = tx1.ExecContext(ctx, `UPSERT INTO table (id, val) VALUES ($1, $2)`, id, "1st tx") 137 require.NoError(t, err) 138 139 // check row for NO write 140 var ( 141 value string 142 connAlreadyHaveTxError *xsql.ConnAlreadyHaveTxError 143 ) 144 err = db.QueryRowContext(ctx, `SELECT val FROM table WHERE id = $1`, id).Scan(&value) 145 require.ErrorIs(t, err, sql.ErrNoRows) 146 147 // second tx on existing conn === session 148 _, err = cc.BeginTx(ctx, &sql.TxOptions{}) 149 require.ErrorAs(t, err, &connAlreadyHaveTxError) 150 }) 151 }