github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/tests/integration/query_execute_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"math/rand"
    13  	"os"
    14  	"path"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/uuid"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"github.com/ydb-platform/ydb-go-sdk/v3"
    23  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
    24  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    25  	"github.com/ydb-platform/ydb-go-sdk/v3/log"
    26  	"github.com/ydb-platform/ydb-go-sdk/v3/query"
    27  	"github.com/ydb-platform/ydb-go-sdk/v3/table/types"
    28  	"github.com/ydb-platform/ydb-go-sdk/v3/trace"
    29  )
    30  
    31  func TestQueryExecute(t *testing.T) {
    32  	if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
    33  		t.Skip("query service not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
    34  	}
    35  
    36  	ctx, cancel := context.WithCancel(xtest.Context(t))
    37  	defer cancel()
    38  
    39  	db, err := ydb.Open(ctx,
    40  		os.Getenv("YDB_CONNECTION_STRING"),
    41  		ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
    42  		ydb.WithTraceQuery(
    43  			log.Query(
    44  				log.Default(os.Stdout,
    45  					log.WithLogQuery(),
    46  					log.WithColoring(),
    47  					log.WithMinLevel(log.INFO),
    48  				),
    49  				trace.QueryEvents,
    50  			),
    51  		),
    52  	)
    53  	require.NoError(t, err)
    54  	t.Run("Query", func(t *testing.T) {
    55  		var (
    56  			p1 string
    57  			p2 uint64
    58  			p3 time.Duration
    59  		)
    60  		var s query.Stats
    61  		result, err := db.Query().Query(ctx, `
    62  				DECLARE $p1 AS Text;
    63  				DECLARE $p2 AS Uint64;
    64  				DECLARE $p3 AS Interval;
    65  				SELECT $p1, $p2, $p3;
    66  				`,
    67  			query.WithParameters(
    68  				ydb.ParamsBuilder().
    69  					Param("$p1").Text("test").
    70  					Param("$p2").Uint64(100500000000).
    71  					Param("$p3").Interval(time.Duration(100500000000)).
    72  					Build(),
    73  			),
    74  			query.WithSyntax(query.SyntaxYQL),
    75  			query.WithStatsMode(query.StatsModeFull, func(stats query.Stats) {
    76  				s = stats
    77  			}),
    78  			query.WithIdempotent(),
    79  		)
    80  		require.NoError(t, err)
    81  		resultSet, err := result.NextResultSet(ctx)
    82  		require.NoError(t, err)
    83  		row, err := resultSet.NextRow(ctx)
    84  		require.NoError(t, err)
    85  		err = row.Scan(&p1, &p2, &p3)
    86  		require.NoError(t, err)
    87  		require.EqualValues(t, "test", p1)
    88  		require.EqualValues(t, 100500000000, p2)
    89  		require.EqualValues(t, time.Duration(100500000000), p3)
    90  		t.Run("Stats", func(t *testing.T) {
    91  			require.NotNil(t, s)
    92  			t.Logf("Stats: %+v", s)
    93  			require.NotZero(t, s.QueryAST())
    94  			require.NotZero(t, s.QueryPlan())
    95  			require.NotZero(t, s.TotalDuration)
    96  			require.NotZero(t, s.TotalCPUTime)
    97  			require.NotZero(t, s.ProcessCPUTime)
    98  			require.NotZero(t, s.Compilation)
    99  			_, ok := s.NextPhase()
   100  			require.True(t, ok)
   101  		})
   102  	})
   103  	t.Run("Explain", func(t *testing.T) {
   104  		var (
   105  			ast  string
   106  			plan map[string]any
   107  		)
   108  		err := db.Query().Exec(ctx,
   109  			`SELECT CAST(42 AS Uint32);`,
   110  			query.WithExecMode(query.ExecModeExplain),
   111  			query.WithStatsMode(query.StatsModeNone, func(stats query.Stats) {
   112  				ast = stats.QueryAST()
   113  				err := json.Unmarshal([]byte(stats.QueryPlan()), &plan)
   114  				require.NoError(t, err)
   115  			}),
   116  			query.WithIdempotent(),
   117  		)
   118  		require.NoError(t, err)
   119  		for _, key := range []string{"Plan", "tables", "meta"} {
   120  			_, has := plan[key]
   121  			require.True(t, has, key)
   122  		}
   123  		require.Contains(t, ast, "return")
   124  	})
   125  	t.Run("Scan", func(t *testing.T) {
   126  		var (
   127  			p1 string
   128  			p2 uint64
   129  			p3 time.Duration
   130  		)
   131  		err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
   132  			result, err := s.Query(ctx, `
   133  				DECLARE $p1 AS Text;
   134  				DECLARE $p2 AS Uint64;
   135  				DECLARE $p3 AS Interval;
   136  				SELECT $p1, $p2, $p3;
   137  				`,
   138  				query.WithParameters(
   139  					ydb.ParamsBuilder().
   140  						Param("$p1").Text("test").
   141  						Param("$p2").Uint64(100500000000).
   142  						Param("$p3").Interval(time.Duration(100500000000)).
   143  						Build(),
   144  				),
   145  				query.WithSyntax(query.SyntaxYQL),
   146  				query.WithIdempotent(),
   147  			)
   148  			if err != nil {
   149  				return err
   150  			}
   151  			resultSet, err := result.NextResultSet(ctx)
   152  			if err != nil {
   153  				return err
   154  			}
   155  			row, err := resultSet.NextRow(ctx)
   156  			if err != nil {
   157  				return err
   158  			}
   159  			err = row.Scan(&p1, &p2, &p3)
   160  			if err != nil {
   161  				return err
   162  			}
   163  			return nil
   164  		}, query.WithIdempotent())
   165  		require.NoError(t, err)
   166  		require.EqualValues(t, "test", p1)
   167  		require.EqualValues(t, 100500000000, p2)
   168  		require.EqualValues(t, time.Duration(100500000000), p3)
   169  	})
   170  	t.Run("ScanNamed", func(t *testing.T) {
   171  		var (
   172  			p1 string
   173  			p2 uint64
   174  			p3 time.Duration
   175  		)
   176  		err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
   177  			result, err := s.Query(ctx, `
   178  				DECLARE $p1 AS Text;
   179  				DECLARE $p2 AS Uint64;
   180  				DECLARE $p3 AS Interval;
   181  				SELECT $p1 AS p1, $p2 AS p2, $p3 AS p3;
   182  				`,
   183  				query.WithParameters(
   184  					ydb.ParamsBuilder().
   185  						Param("$p1").Text("test").
   186  						Param("$p2").Uint64(100500000000).
   187  						Param("$p3").Interval(time.Duration(100500000000)).
   188  						Build(),
   189  				),
   190  				query.WithSyntax(query.SyntaxYQL),
   191  				query.WithIdempotent(),
   192  			)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			resultSet, err := result.NextResultSet(ctx)
   197  			if err != nil {
   198  				return err
   199  			}
   200  			row, err := resultSet.NextRow(ctx)
   201  			if err != nil {
   202  				return err
   203  			}
   204  			err = row.ScanNamed(
   205  				query.Named("p1", &p1),
   206  				query.Named("p2", &p2),
   207  				query.Named("p3", &p3),
   208  			)
   209  			if err != nil {
   210  				return err
   211  			}
   212  			return nil
   213  		}, query.WithIdempotent())
   214  		require.NoError(t, err)
   215  		require.EqualValues(t, "test", p1)
   216  		require.EqualValues(t, 100500000000, p2)
   217  		require.EqualValues(t, time.Duration(100500000000), p3)
   218  	})
   219  	t.Run("ScanStruct", func(t *testing.T) {
   220  		var data struct {
   221  			P1 *string       `sql:"p1"`
   222  			P2 uint64        `sql:"p2"`
   223  			P3 time.Duration `sql:"p3"`
   224  			P4 *string       `sql:"p4"`
   225  		}
   226  		err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
   227  			result, err := s.Query(ctx, `
   228  				DECLARE $p1 AS Text;
   229  				DECLARE $p2 AS Uint64;
   230  				DECLARE $p3 AS Interval;
   231  				SELECT CAST($p1 AS Optional<Text>) AS p1, $p2 AS p2, $p3 AS p3, CAST(NULL AS Optional<Text>) AS p4;`,
   232  				query.WithParameters(
   233  					ydb.ParamsBuilder().
   234  						Param("$p1").Text("test").
   235  						Param("$p2").Uint64(100500000000).
   236  						Param("$p3").Interval(time.Duration(100500000000)).
   237  						Build(),
   238  				),
   239  				query.WithSyntax(query.SyntaxYQL),
   240  				query.WithIdempotent(),
   241  			)
   242  			if err != nil {
   243  				return err
   244  			}
   245  			resultSet, err := result.NextResultSet(ctx)
   246  			if err != nil {
   247  				return err
   248  			}
   249  			row, err := resultSet.NextRow(ctx)
   250  			if err != nil {
   251  				return err
   252  			}
   253  			err = row.ScanStruct(&data)
   254  			if err != nil {
   255  				return err
   256  			}
   257  			return nil
   258  		}, query.WithIdempotent())
   259  		require.NoError(t, err)
   260  		require.NotNil(t, data.P1)
   261  		require.EqualValues(t, "test", *data.P1)
   262  		require.EqualValues(t, 100500000000, data.P2)
   263  		require.EqualValues(t, time.Duration(100500000000), data.P3)
   264  		require.Nil(t, data.P4)
   265  	})
   266  	t.Run("Tx", func(t *testing.T) {
   267  		err = db.Query().Do(ctx, func(ctx context.Context, s query.Session) (err error) {
   268  			tx, err := s.Begin(ctx, query.TxSettings(query.WithSerializableReadWrite()))
   269  			if err != nil {
   270  				return err
   271  			}
   272  			result, err := tx.Query(ctx, `SELECT 1`)
   273  			if err != nil {
   274  				return err
   275  			}
   276  			resultSet, err := result.NextResultSet(ctx)
   277  			if err != nil {
   278  				return err
   279  			}
   280  			row, err := resultSet.NextRow(ctx)
   281  			if err != nil {
   282  				return err
   283  			}
   284  			var v int32
   285  			err = row.Scan(&v)
   286  			if err != nil {
   287  				return err
   288  			}
   289  			if v != 1 {
   290  				return fmt.Errorf("unexpected value from database: %d", v)
   291  			}
   292  			return tx.CommitTx(ctx)
   293  		}, query.WithIdempotent())
   294  		require.NoError(t, err)
   295  	})
   296  }
   297  
   298  // https://github.com/ydb-platform/ydb-go-sdk/issues/1456
   299  func TestIssue1456TooManyUnknownTransactions(t *testing.T) {
   300  	if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
   301  		t.Skip("query service not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
   302  	}
   303  
   304  	ctx, cancel := context.WithCancel(xtest.Context(t))
   305  	defer cancel()
   306  
   307  	db, err := ydb.Open(ctx,
   308  		os.Getenv("YDB_CONNECTION_STRING"),
   309  		ydb.WithAccessTokenCredentials(os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS")),
   310  	)
   311  	require.NoError(t, err)
   312  
   313  	const (
   314  		tableSize = 10000
   315  		queries   = 1000
   316  		chSize    = 50
   317  	)
   318  
   319  	tableName := path.Join(db.Name(), t.Name(), "test")
   320  
   321  	err = db.Query().Exec(ctx, "DROP TABLE IF EXISTS `"+tableName+"`;")
   322  	require.NoError(t, err)
   323  
   324  	err = db.Query().Exec(ctx, `CREATE TABLE `+"`"+tableName+"`"+` (
   325  			id Utf8,
   326  			value Uint64,
   327  			PRIMARY KEY(id)
   328  			)`,
   329  	)
   330  	require.NoError(t, err)
   331  
   332  	var vals []types.Value
   333  	for i := 0; i < tableSize; i++ {
   334  		vals = append(vals, types.StructValue(
   335  			types.StructFieldValue("id", types.UTF8Value(uuid.NewString())),
   336  			types.StructFieldValue("value", types.Uint64Value(rand.Uint64())),
   337  		))
   338  	}
   339  	err = db.Query().Do(context.Background(), func(ctx context.Context, s query.Session) error {
   340  		return s.Exec(ctx, `
   341  				PRAGMA AnsiInForEmptyOrNullableItemsCollections;
   342  				DECLARE $vals AS List<Struct<
   343  					id: Utf8,
   344  					value: Uint64
   345  				>>;
   346  				
   347  				INSERT INTO `+"`"+tableName+"`"+` 
   348  				SELECT id, value FROM AS_TABLE($vals);`,
   349  			query.WithParameters(
   350  				ydb.ParamsBuilder().
   351  					Param("$vals").BeginList().AddItems(vals...).EndList().Build(),
   352  			),
   353  		)
   354  	})
   355  	require.NoError(t, err)
   356  
   357  	t.Run("Query", func(t *testing.T) {
   358  		wg := sync.WaitGroup{}
   359  		wg.Add(queries)
   360  		ch := make(chan struct{}, chSize)
   361  		for i := 0; i < queries; i++ {
   362  			ch <- struct{}{}
   363  			go func() {
   364  				defer func() { <-ch }()
   365  				defer wg.Done()
   366  
   367  				err := db.Query().DoTx(ctx, func(ctx context.Context, tx query.TxActor) error {
   368  					var (
   369  						id string
   370  						v  uint64
   371  					)
   372  
   373  					res, err := tx.Query(ctx, `SELECT id, value FROM `+"`"+tableName+"`")
   374  					if err != nil {
   375  						return err
   376  					}
   377  
   378  					for {
   379  						set, err := res.NextResultSet(ctx)
   380  						if err != nil {
   381  							if errors.Is(err, io.EOF) {
   382  								break
   383  							}
   384  
   385  							return err
   386  						}
   387  
   388  						for {
   389  							row, err := set.NextRow(ctx)
   390  							if err != nil {
   391  								if errors.Is(err, io.EOF) {
   392  									break
   393  								}
   394  
   395  								return err
   396  							}
   397  
   398  							err = row.Scan(&id, &v)
   399  							if err != nil {
   400  								return err
   401  							}
   402  						}
   403  					}
   404  					return res.Close(ctx)
   405  				}, query.WithTxSettings(query.TxSettings(query.WithSerializableReadWrite())))
   406  				require.NoError(t, err)
   407  			}()
   408  		}
   409  		wg.Wait()
   410  	})
   411  }