github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/tests/integration/database_sql_regression_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package integration 5 6 import ( 7 "context" 8 "database/sql" 9 "errors" 10 "fmt" 11 "math/rand" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/google/uuid" 17 "github.com/stretchr/testify/require" 18 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 19 20 "github.com/ydb-platform/ydb-go-sdk/v3" 21 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 22 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsql/badconn" 23 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest" 24 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 25 "github.com/ydb-platform/ydb-go-sdk/v3/table" 26 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 27 ) 28 29 func TestRegressionCloud109307(t *testing.T) { 30 var ( 31 ctx = xtest.Context(t) 32 scope = newScope(t) 33 db = scope.SQLDriverWithFolder( 34 ydb.WithTablePathPrefix(scope.Folder()), 35 ydb.WithAutoDeclare(), 36 ) 37 ) 38 39 ctx, cancel := context.WithTimeout(ctx, 42*time.Second) 40 defer cancel() 41 42 for i := int64(1); ; i++ { 43 if ctx.Err() != nil { 44 break 45 } 46 47 err := retry.DoTx(ctx, db, func(ctx context.Context, tx *sql.Tx) error { 48 //nolint:gosec 49 if rand.Int31n(3) == 0 { 50 return badconn.Map(xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))) 51 } 52 var rows *sql.Rows 53 rows, err := tx.QueryContext(ctx, `SELECT $i`, sql.Named("i", i)) 54 if err != nil { 55 return err 56 } 57 defer rows.Close() 58 if !rows.Next() { 59 return errors.New("no rows") 60 } 61 var result interface{} 62 if err = rows.Scan(&result); err != nil { 63 return err 64 } 65 if result.(int64)%100 == 0 { 66 t.Logf("result: %+v\n", result) 67 } 68 return rows.Err() 69 }, retry.WithTxOptions(&sql.TxOptions{ 70 Isolation: sql.LevelSnapshot, 71 ReadOnly: true, 72 }), retry.WithIdempotent(true)) 73 if ctx.Err() == nil { 74 require.NoError(t, err) 75 } 76 } 77 } 78 79 func TestRegressionKikimr17104(t *testing.T) { 80 var ( 81 ctx = xtest.Context(t) 82 scope = newScope(t) 83 db = scope.SQLDriverWithFolder( 84 ydb.WithTablePathPrefix(scope.Folder()), 85 ydb.WithAutoDeclare(), 86 ) 87 tableName = t.Name() 88 upsertRowsCount = 100000 89 upsertChecksum uint64 90 ) 91 92 t.Run("data", func(t *testing.T) { 93 t.Run("prepare", func(t *testing.T) { 94 t.Run("scheme", func(t *testing.T) { 95 err := retry.Do(ydb.WithQueryMode(ctx, ydb.SchemeQueryMode), db, 96 func(ctx context.Context, cc *sql.Conn) (err error) { 97 _, err = cc.ExecContext(ctx, 98 fmt.Sprintf("CREATE TABLE %s (val Int32, PRIMARY KEY (val))", tableName), 99 ) 100 if err != nil { 101 return err 102 } 103 return nil 104 }, retry.WithIdempotent(true), 105 ) 106 require.NoError(t, err) 107 }) 108 t.Run("data", func(t *testing.T) { 109 // - upsert data 110 t.Logf("> preparing values to upsert...\n") 111 values := make([]types.Value, 0, upsertRowsCount) 112 for i := 0; i < upsertRowsCount; i++ { 113 upsertChecksum += uint64(i) 114 values = append(values, 115 types.StructValue( 116 types.StructFieldValue("val", types.Int32Value(int32(i))), 117 ), 118 ) 119 } 120 t.Logf("> upsert data\n") 121 err := retry.Do(ydb.WithQueryMode(ctx, ydb.DataQueryMode), db, 122 func(ctx context.Context, cc *sql.Conn) (err error) { 123 _, err = cc.ExecContext(ctx, 124 fmt.Sprintf("UPSERT INTO %s SELECT val FROM AS_TABLE($values);", tableName), 125 table.NewQueryParameters(table.ValueParam("$values", types.ListValue(values...))), 126 ) 127 if err != nil { 128 return err 129 } 130 return nil 131 }, retry.WithIdempotent(true), 132 ) 133 require.NoError(t, err) 134 }) 135 }) 136 t.Run("scan", func(t *testing.T) { 137 t.Run("query", func(t *testing.T) { 138 var ( 139 rowsCount int 140 checkSum uint64 141 ) 142 err := retry.Do(ydb.WithQueryMode(ctx, ydb.ScanQueryMode), db, 143 func(ctx context.Context, cc *sql.Conn) (err error) { 144 var rows *sql.Rows 145 rowsCount = 0 146 checkSum = 0 147 rows, err = cc.QueryContext(ctx, fmt.Sprintf("SELECT val FROM %s", tableName)) 148 if err != nil { 149 return err 150 } 151 for rows.NextResultSet() { 152 for rows.Next() { 153 rowsCount++ 154 var val uint64 155 err = rows.Scan(&val) 156 if err != nil { 157 return err 158 } 159 checkSum += val 160 } 161 } 162 return rows.Err() 163 }, retry.WithIdempotent(true), 164 ) 165 require.NoError(t, err) 166 require.Equal(t, upsertRowsCount, rowsCount) 167 require.Equal(t, upsertChecksum, checkSum) 168 }) 169 }) 170 }) 171 } 172 173 func TestUUIDSerializationDatabaseSQLIssue1501(t *testing.T) { 174 // https://github.com/ydb-platform/ydb-go-sdk/issues/1501 175 // test with special uuid - all bytes are different for check any byte swaps 176 177 t.Run("old-send", func(t *testing.T) { 178 // test old behavior - for test way of safe work with data, written with bagged API version 179 var ( 180 scope = newScope(t) 181 db = scope.SQLDriver() 182 ) 183 184 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 185 id := [16]byte(uuid.MustParse(idString)) 186 row := db.QueryRow(` 187 DECLARE $val AS UUID; 188 189 SELECT CAST($val AS Utf8)`, sql.Named("val", id), 190 ) 191 192 require.ErrorIs(t, row.Err(), types.ErrIssue1501BadUUID) 193 }) 194 t.Run("old-send-with-force-wrapper", func(t *testing.T) { 195 // test old behavior - for test way of safe work with data, written with bagged API version 196 var ( 197 scope = newScope(t) 198 db = scope.SQLDriver() 199 ) 200 201 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 202 expectedResultWithBug := "2d9e498b-b746-9cfb-084d-de4e1cb4736e" 203 id := [16]byte(uuid.MustParse(idString)) 204 row := db.QueryRow(` 205 DECLARE $val AS UUID; 206 207 SELECT CAST($val AS Utf8)`, 208 sql.Named("val", types.NewUUIDBytesWithIssue1501(id)), 209 ) 210 211 require.NoError(t, row.Err()) 212 213 var res string 214 215 err := row.Scan(&res) 216 require.NoError(t, err) 217 require.Equal(t, expectedResultWithBug, res) 218 }) 219 t.Run("old-receive-to-bytes", func(t *testing.T) { 220 // test old behavior - for test way of safe work with data, written with bagged API version 221 var ( 222 scope = newScope(t) 223 db = scope.SQLDriver() 224 ) 225 226 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 227 row := db.QueryRow(` 228 DECLARE $val AS Text; 229 230 SELECT CAST($val AS UUID)`, 231 sql.Named("val", idString), 232 ) 233 234 require.NoError(t, row.Err()) 235 236 var res [16]byte 237 238 err := row.Scan(&res) 239 require.Error(t, err) 240 }) 241 t.Run("old-receive-to-bytes-with-force-wrapper", func(t *testing.T) { 242 // test old behavior - for test way of safe work with data, written with bagged API version 243 var ( 244 scope = newScope(t) 245 db = scope.SQLDriver() 246 ) 247 248 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 249 expectedResultWithBug := "8b499e2d-46b7-fb9c-4d08-4ede6e73b41c" 250 row := db.QueryRow(` 251 DECLARE $val AS Text; 252 253 SELECT CAST($val AS UUID)`, 254 sql.Named("val", idString), 255 ) 256 257 require.NoError(t, row.Err()) 258 259 var res types.UUIDBytesWithIssue1501Type 260 261 err := row.Scan(&res) 262 require.NoError(t, err) 263 264 resUUID := uuid.UUID(res.AsBytesArray()) 265 require.Equal(t, expectedResultWithBug, resUUID.String()) 266 }) 267 268 t.Run("old-receive-to-string", func(t *testing.T) { 269 // test old behavior - for test way of safe work with data, written with bagged API version 270 var ( 271 scope = newScope(t) 272 db = scope.SQLDriver() 273 ) 274 275 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 276 row := db.QueryRow(` 277 DECLARE $val AS Text; 278 279 SELECT CAST($val AS UUID)`, 280 sql.Named("val", idString), 281 ) 282 283 require.NoError(t, row.Err()) 284 285 var res string 286 287 err := row.Scan(&res) 288 require.Error(t, err) 289 }) 290 t.Run("old-receive-to-uuid", func(t *testing.T) { 291 // test old behavior - for test way of safe work with data, written with bagged API version 292 var ( 293 scope = newScope(t) 294 db = scope.SQLDriver() 295 ) 296 297 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 298 row := db.QueryRow(` 299 DECLARE $val AS Text; 300 301 SELECT CAST($val AS UUID)`, 302 sql.Named("val", idString), 303 ) 304 305 require.NoError(t, row.Err()) 306 307 var res uuid.UUID 308 309 err := row.Scan(&res) 310 require.Error(t, err) 311 }) 312 t.Run("old-send-receive", func(t *testing.T) { 313 // test old behavior - for test way of safe work with data, written with bagged API version 314 var ( 315 scope = newScope(t) 316 db = scope.SQLDriver() 317 ) 318 319 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 320 id := uuid.MustParse(idString) 321 idParam := [16]byte(id) 322 row := db.QueryRow(` 323 DECLARE $val AS UUID; 324 325 SELECT $val`, 326 sql.Named("val", idParam), 327 ) 328 329 require.ErrorIs(t, row.Err(), types.ErrIssue1501BadUUID) 330 }) 331 t.Run("old-send-receive-with-force-wrapper", func(t *testing.T) { 332 // test old behavior - for test way of safe work with data, written with bagged API version 333 var ( 334 scope = newScope(t) 335 db = scope.SQLDriver() 336 ) 337 338 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 339 id := uuid.MustParse(idString) 340 row := db.QueryRow(` 341 DECLARE $val AS UUID; 342 343 SELECT $val`, 344 sql.Named("val", types.UUIDWithIssue1501Value(id)), 345 ) 346 347 require.NoError(t, row.Err()) 348 349 var resBytes types.UUIDBytesWithIssue1501Type 350 err := row.Scan(&resBytes) 351 require.NoError(t, err) 352 353 resUUID := uuid.UUID(resBytes.AsBytesArray()) 354 355 require.Equal(t, id, resUUID) 356 }) 357 t.Run("good-send", func(t *testing.T) { 358 var ( 359 scope = newScope(t) 360 db = scope.SQLDriver() 361 ) 362 363 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 364 id := uuid.MustParse(idString) 365 row := db.QueryRow(` 366 DECLARE $val AS Utf8; 367 368 SELECT $val`, 369 sql.Named("val", id), // send as string because uuid implements Value() (driver.Value, error) 370 ) 371 372 require.NoError(t, row.Err()) 373 374 var res string 375 err := row.Scan(&res) 376 require.NoError(t, err) 377 require.Equal(t, id.String(), res) 378 }) 379 t.Run("good-receive", func(t *testing.T) { 380 // test old behavior - for test way of safe work with data, written with bagged API version 381 var ( 382 scope = newScope(t) 383 db = scope.SQLDriver() 384 ) 385 386 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 387 row := db.QueryRow(` 388 DECLARE $val AS Utf8; 389 390 SELECT CAST($val AS UUID)`, 391 sql.Named("val", idString), 392 ) 393 394 require.NoError(t, row.Err()) 395 396 var resFromDB types.UUIDBytesWithIssue1501Type 397 398 err := row.Scan(&resFromDB) 399 require.NoError(t, err) 400 401 resUUID := resFromDB.PublicRevertReorderForIssue1501() 402 403 resString := strings.ToUpper(resUUID.String()) 404 require.Equal(t, idString, resString) 405 }) 406 t.Run("good-send-receive", func(t *testing.T) { 407 var ( 408 scope = newScope(t) 409 db = scope.SQLDriver() 410 ) 411 412 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 413 id := uuid.MustParse(idString) 414 row := db.QueryRow(` 415 DECLARE $val AS Utf8; 416 417 SELECT $val`, 418 sql.Named("val", id), 419 ) 420 421 require.NoError(t, row.Err()) 422 423 var res uuid.UUID 424 err := row.Scan(&res) 425 require.NoError(t, err) 426 427 require.Equal(t, id.String(), res.String()) 428 }) 429 }