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 }