github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/tests/integration/table_regression_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package integration 5 6 import ( 7 "context" 8 "fmt" 9 "strconv" 10 "strings" 11 "testing" 12 13 "github.com/google/uuid" 14 "github.com/stretchr/testify/require" 15 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 16 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/scanner" 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 19 "github.com/ydb-platform/ydb-go-sdk/v3/table" 20 "github.com/ydb-platform/ydb-go-sdk/v3/table/result" 21 "github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed" 22 "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named" 23 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 24 ) 25 26 type rawInt64 struct { 27 val int64 28 } 29 30 func (r *rawInt64) UnmarshalYDB(raw types.RawValue) error { 31 raw.Unwrap() 32 r.val = raw.Int64() 33 return nil 34 } 35 36 func TestIssueWideResultWithUnwrap(t *testing.T) { 37 const columnsCount = 200 38 var columns []string 39 for i := 0; i < columnsCount; i++ { 40 column := fmt.Sprintf("CAST(%v AS Int64?) as c%v", i, i) 41 columns = append(columns, column) 42 } 43 44 query := "SELECT " + strings.Join(columns, ", ") 45 t.Run("named", func(t *testing.T) { 46 scope := newScope(t) 47 var res result.Result 48 var err error 49 err = scope.Driver().Table().DoTx(scope.Ctx, func(ctx context.Context, tx table.TransactionActor) error { 50 res, err = tx.Execute(ctx, query, nil) 51 return err 52 }) 53 require.NoError(t, err) 54 55 res.NextResultSet(scope.Ctx) 56 require.NoError(t, res.Err()) 57 res.NextRow() 58 require.NoError(t, res.Err()) 59 60 results := make([]rawInt64, columnsCount) 61 resultsPointers := make([]named.Value, columnsCount) 62 for i := range results { 63 resultsPointers[i] = named.Required("c"+strconv.Itoa(i), &results[i]) 64 } 65 err = res.ScanNamed(resultsPointers...) 66 require.NoError(t, err) 67 68 for i, val := range results { 69 require.Equal(t, int64(i), val.val) 70 } 71 }) 72 t.Run("indexed", func(t *testing.T) { 73 scope := newScope(t) 74 var res result.Result 75 var err error 76 err = scope.Driver().Table().DoTx(scope.Ctx, func(ctx context.Context, tx table.TransactionActor) error { 77 res, err = tx.Execute(ctx, query, nil) 78 return err 79 }) 80 require.NoError(t, err) 81 82 res.NextResultSet(scope.Ctx) 83 require.NoError(t, res.Err()) 84 res.NextRow() 85 require.NoError(t, res.Err()) 86 87 results := make([]rawInt64, columnsCount) 88 resultsPointers := make([]indexed.RequiredOrOptional, columnsCount) 89 for i := range results { 90 resultsPointers[i] = &results[i] 91 } 92 err = res.Scan(resultsPointers...) 93 require.NoError(t, err) 94 95 for i, val := range results { 96 require.Equal(t, int64(i), val.val) 97 } 98 }) 99 } 100 101 // https://github.com/ydb-platform/ydb-go-sdk/issues/1227 102 func TestRegressionIssue1227RetryBadSession(t *testing.T) { 103 var ( 104 scope = newScope(t) 105 cnt = 0 106 ) 107 err := scope.Driver().Table().Do(scope.Ctx, func(ctx context.Context, s table.Session) error { 108 cnt++ 109 if cnt < 100 { 110 return xerrors.WithStackTrace(xerrors.Operation(xerrors.WithStatusCode(Ydb.StatusIds_BAD_SESSION))) 111 } 112 113 return nil 114 }) 115 require.NoError(t, err) 116 require.EqualValues(t, 100, cnt) 117 } 118 119 func TestUUIDSerializationTableServiceServiceIssue1501(t *testing.T) { 120 // https://github.com/ydb-platform/ydb-go-sdk/issues/1501 121 // test with special uuid - all bytes are different for check any byte swaps 122 123 t.Run("old-send-with-force-wrapper", func(t *testing.T) { 124 // test old behavior - for test way of safe work with data, written with bagged API version 125 var ( 126 scope = newScope(t) 127 ctx = scope.Ctx 128 db = scope.Driver() 129 ) 130 131 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 132 expectedResultWithBug := "2d9e498b-b746-9cfb-084d-de4e1cb4736e" 133 id := uuid.MustParse(idString) 134 135 var idFromDB string 136 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 137 res, err := tx.Execute(ctx, ` 138 DECLARE $val AS UUID; 139 140 SELECT CAST($val AS Utf8) 141 `, table.NewQueryParameters(table.ValueParam("$val", types.UUIDWithIssue1501Value(id)))) 142 if err != nil { 143 return err 144 } 145 res.NextResultSet(ctx) 146 res.NextRow() 147 148 err = res.Scan(&idFromDB) 149 return err 150 }) 151 require.NoError(t, err) 152 require.Equal(t, expectedResultWithBug, idFromDB) 153 }) 154 t.Run("old-receive-to-bytes", func(t *testing.T) { 155 // test old behavior - for test way of safe work with data, written with bagged API version 156 var ( 157 scope = newScope(t) 158 ctx = scope.Ctx 159 db = scope.Driver() 160 ) 161 162 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 163 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 164 res, err := tx.Execute(ctx, ` 165 DECLARE $val AS Text; 166 167 SELECT CAST($val AS UUID) 168 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 169 if err != nil { 170 return err 171 } 172 173 res.NextResultSet(ctx) 174 res.NextRow() 175 176 var resBytes [16]byte 177 err = res.Scan(&resBytes) 178 if err != nil { 179 return err 180 } 181 182 return nil 183 }) 184 185 require.ErrorIs(t, err, types.ErrIssue1501BadUUID) 186 }) 187 t.Run("old-receive-to-bytes-with-force-wrapper", func(t *testing.T) { 188 // test old behavior - for test way of safe work with data, written with bagged API version 189 var ( 190 scope = newScope(t) 191 ctx = scope.Ctx 192 db = scope.Driver() 193 ) 194 195 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 196 expectedResultWithBug := "8b499e2d-46b7-fb9c-4d08-4ede6e73b41c" 197 var resultFromDb uuid.UUID 198 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 199 res, err := tx.Execute(ctx, ` 200 DECLARE $val AS Text; 201 202 SELECT CAST($val AS UUID) 203 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 204 if err != nil { 205 return err 206 } 207 208 res.NextResultSet(ctx) 209 res.NextRow() 210 211 var resBytes [16]byte 212 var bytesWrapper *types.UUIDBytesWithIssue1501Type 213 err = res.Scan(&bytesWrapper) 214 if err != nil { 215 return err 216 } 217 resBytes = bytesWrapper.AsBytesArray() 218 219 resultFromDb = resBytes 220 return nil 221 }) 222 223 require.NoError(t, err) 224 require.Equal(t, expectedResultWithBug, resultFromDb.String()) 225 }) 226 t.Run("old-unmarshal-with-bytes-force-wrapper", func(t *testing.T) { 227 // test old behavior - for test way of safe work with data, written with bagged API version 228 var ( 229 scope = newScope(t) 230 ctx = scope.Ctx 231 db = scope.Driver() 232 ) 233 234 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 235 var resultFromDb customTestUnmarshalUUIDWithForceWrapper 236 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 237 res, err := tx.Execute(ctx, ` 238 DECLARE $val AS Text; 239 240 SELECT CAST($val AS UUID) 241 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 242 if err != nil { 243 return err 244 } 245 246 res.NextResultSet(ctx) 247 res.NextRow() 248 249 err = res.Scan(&resultFromDb) 250 if err != nil { 251 return err 252 } 253 254 return nil 255 }) 256 257 require.ErrorIs(t, err, types.ErrIssue1501BadUUID) 258 }) 259 t.Run("old-unmarshal-with-bytes-typed", func(t *testing.T) { 260 // test old behavior - for test way of safe work with data, written with bagged API version 261 var ( 262 scope = newScope(t) 263 ctx = scope.Ctx 264 db = scope.Driver() 265 ) 266 267 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 268 var resultFromDb customTestUnmarshalUUIDTyped 269 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 270 res, err := tx.Execute(ctx, ` 271 DECLARE $val AS Text; 272 273 SELECT CAST($val AS UUID) 274 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 275 if err != nil { 276 return err 277 } 278 279 res.NextResultSet(ctx) 280 res.NextRow() 281 282 err = res.Scan(&resultFromDb) 283 if err != nil { 284 return err 285 } 286 287 return nil 288 }) 289 290 require.NoError(t, err) 291 require.Equal(t, strings.ToUpper(idString), strings.ToUpper(resultFromDb.String())) 292 }) 293 294 t.Run("old-receive-to-string", func(t *testing.T) { 295 // test old behavior - for test way of safe work with data, written with bagged API version 296 var ( 297 scope = newScope(t) 298 ctx = scope.Ctx 299 db = scope.Driver() 300 ) 301 302 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 303 var resultFromDb string 304 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 305 res, err := tx.Execute(ctx, ` 306 DECLARE $val AS Text; 307 308 SELECT CAST($val AS UUID) 309 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 310 if err != nil { 311 return err 312 } 313 314 res.NextResultSet(ctx) 315 res.NextRow() 316 317 err = res.ScanWithDefaults(&resultFromDb) 318 if err != nil { 319 return err 320 } 321 322 return nil 323 }) 324 325 require.ErrorIs(t, err, types.ErrIssue1501BadUUID) 326 }) 327 t.Run("old-receive-to-string-with-force-wrapper", func(t *testing.T) { 328 // test old behavior - for test way of safe work with data, written with bagged API version 329 var ( 330 scope = newScope(t) 331 ctx = scope.Ctx 332 db = scope.Driver() 333 ) 334 335 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 336 expectedResultWithBug := []byte{0x8b, 0x49, 0x9e, 0x2d, 0x46, 0xb7, 0xfb, 0x9c, 0x4d, 0x8, 0x4e, 0xde, 0x6e, 0x73, 0xb4, 0x1c} 337 var resultFromDb string 338 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 339 res, err := tx.Execute(ctx, ` 340 DECLARE $val AS Text; 341 342 SELECT CAST($val AS UUID) 343 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 344 if err != nil { 345 return err 346 } 347 348 res.NextResultSet(ctx) 349 res.NextRow() 350 351 var wrapper types.UUIDBytesWithIssue1501Type 352 err = res.ScanWithDefaults(&wrapper) 353 if err != nil { 354 return err 355 } 356 357 resultFromDb = wrapper.AsBrokenString() 358 359 return nil 360 }) 361 362 require.NoError(t, err) 363 resultBytes := []byte(resultFromDb) 364 require.Equal(t, expectedResultWithBug, resultBytes) 365 }) 366 t.Run("old-send-receive-with-force-wrapper", func(t *testing.T) { 367 // test old behavior - for test way of safe work with data, written with bagged API version 368 var ( 369 scope = newScope(t) 370 ctx = scope.Ctx 371 db = scope.Driver() 372 ) 373 374 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 375 id := uuid.MustParse(idString) 376 377 var idFromDB uuid.UUID 378 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 379 res, err := tx.Execute(ctx, ` 380 DECLARE $val AS UUID; 381 382 SELECT $val 383 `, table.NewQueryParameters(table.ValueParam("$val", types.UUIDWithIssue1501Value(id)))) 384 if err != nil { 385 return err 386 } 387 res.NextResultSet(ctx) 388 res.NextRow() 389 390 var resBytes [16]byte 391 var resWrapper types.UUIDBytesWithIssue1501Type 392 err = res.Scan(&resWrapper) 393 if err != nil { 394 return err 395 } 396 resBytes = resWrapper.AsBytesArray() 397 398 idFromDB = resBytes 399 return nil 400 }) 401 require.NoError(t, err) 402 require.Equal(t, id, idFromDB) 403 }) 404 t.Run("good-send", func(t *testing.T) { 405 var ( 406 scope = newScope(t) 407 ctx = scope.Ctx 408 db = scope.Driver() 409 ) 410 411 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 412 id := uuid.MustParse(idString) 413 var resStringId string 414 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 415 res, err := tx.Execute(ctx, ` 416 DECLARE $val AS UUID; 417 418 SELECT CAST($val AS Utf8) 419 `, table.NewQueryParameters(table.ValueParam("$val", types.UuidValue(id)))) 420 421 res.NextResultSet(ctx) 422 res.NextRow() 423 err = res.ScanWithDefaults(&resStringId) 424 if err != nil { 425 return err 426 } 427 return nil 428 }) 429 require.NoError(t, err) 430 431 require.Equal(t, strings.ToUpper(id.String()), strings.ToUpper(resStringId)) 432 }) 433 t.Run("good-receive", func(t *testing.T) { 434 // test old behavior - for test way of safe work with data, written with bagged API version 435 var ( 436 scope = newScope(t) 437 ctx = scope.Ctx 438 db = scope.Driver() 439 ) 440 441 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 442 443 var resID uuid.UUID 444 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 445 res, err := tx.Execute(ctx, ` 446 DECLARE $val AS Text; 447 448 SELECT CAST($val AS UUID) 449 `, table.NewQueryParameters(table.ValueParam("$val", types.TextValue(idString)))) 450 if err != nil { 451 return err 452 } 453 res.NextResultSet(ctx) 454 res.NextRow() 455 return res.ScanWithDefaults(&resID) 456 }) 457 458 require.NoError(t, err) 459 require.Equal(t, strings.ToUpper(idString), strings.ToUpper(resID.String())) 460 }) 461 t.Run("good-send-receive", func(t *testing.T) { 462 var ( 463 scope = newScope(t) 464 ctx = scope.Ctx 465 db = scope.Driver() 466 ) 467 468 idString := "6E73B41C-4EDE-4D08-9CFB-B7462D9E498B" 469 id := uuid.MustParse(idString) 470 var resID uuid.UUID 471 err := db.Table().DoTx(ctx, func(ctx context.Context, tx table.TransactionActor) error { 472 res, err := tx.Execute(ctx, ` 473 DECLARE $val AS UUID; 474 475 SELECT $val 476 `, table.NewQueryParameters(table.ValueParam("$val", types.UuidValue(id)))) 477 478 res.NextResultSet(ctx) 479 res.NextRow() 480 err = res.ScanWithDefaults(&resID) 481 if err != nil { 482 return err 483 } 484 return nil 485 }) 486 require.NoError(t, err) 487 488 require.NoError(t, err) 489 require.Equal(t, strings.ToUpper(idString), strings.ToUpper(resID.String())) 490 }) 491 } 492 493 type customTestUnmarshalUUIDWithForceWrapper string 494 495 func (c *customTestUnmarshalUUIDWithForceWrapper) UnmarshalYDB(raw scanner.RawValue) error { 496 val := raw.UUIDWithIssue1501() 497 id := uuid.UUID(val) 498 *c = customTestUnmarshalUUIDWithForceWrapper(id.String()) 499 return raw.Err() 500 } 501 502 func (c *customTestUnmarshalUUIDWithForceWrapper) String() string { 503 return string(*c) 504 } 505 506 type customTestUnmarshalUUIDTyped string 507 508 func (c *customTestUnmarshalUUIDTyped) UnmarshalYDB(raw scanner.RawValue) error { 509 val := raw.UUIDTyped() 510 *c = customTestUnmarshalUUIDTyped(val.String()) 511 return raw.Err() 512 } 513 514 func (c *customTestUnmarshalUUIDTyped) String() string { 515 return string(*c) 516 }