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  }