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  }