github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/query_bind_test.go (about) 1 package ydb_test 2 3 import ( 4 "database/sql" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/require" 9 10 "github.com/ydb-platform/ydb-go-sdk/v3" 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/bind" 12 "github.com/ydb-platform/ydb-go-sdk/v3/internal/params" 13 "github.com/ydb-platform/ydb-go-sdk/v3/table" 14 "github.com/ydb-platform/ydb-go-sdk/v3/table/types" 15 "github.com/ydb-platform/ydb-go-sdk/v3/testutil" 16 ) 17 18 //nolint:funlen, maintidx 19 func TestQueryBind(t *testing.T) { 20 now := time.Now() 21 for _, tt := range []struct { 22 b testutil.QueryBindings 23 sql string 24 args []interface{} 25 yql string 26 params *table.QueryParameters 27 err error 28 }{ 29 { 30 b: testutil.QueryBind( 31 ydb.WithTablePathPrefix("/local/test"), 32 ydb.WithAutoDeclare(), 33 ydb.WithPositionalArgs(), 34 ), 35 sql: `$cnt = (SELECT 2 * COUNT(*) FROM my_table); 36 37 UPDATE my_table SET data = CAST($cnt AS Optional<Uint64>) WHERE id = ?;`, 38 args: []interface{}{uint64(6)}, 39 yql: `-- bind TablePathPrefix 40 PRAGMA TablePathPrefix("/local/test"); 41 42 -- bind declares 43 DECLARE $p0 AS Uint64; 44 45 -- origin query with positional args replacement 46 $cnt = (SELECT 2 * COUNT(*) FROM my_table); 47 48 UPDATE my_table SET data = CAST($cnt AS Optional<Uint64>) WHERE id = $p0;`, 49 params: table.NewQueryParameters( 50 table.ValueParam("$p0", types.Uint64Value(6)), 51 ), 52 }, 53 { 54 b: testutil.QueryBind( 55 ydb.WithTablePathPrefix("/local/test"), 56 ydb.WithAutoDeclare(), 57 ydb.WithNumericArgs(), 58 ), 59 sql: `$cnt = (SELECT 2 * COUNT(*) FROM my_table); 60 61 UPDATE my_table SET data = CAST($cnt AS Uint64) WHERE id = $1;`, 62 args: []interface{}{uint64(6)}, 63 yql: `-- bind TablePathPrefix 64 PRAGMA TablePathPrefix("/local/test"); 65 66 -- bind declares 67 DECLARE $p0 AS Uint64; 68 69 -- origin query with numeric args replacement 70 $cnt = (SELECT 2 * COUNT(*) FROM my_table); 71 72 UPDATE my_table SET data = CAST($cnt AS Uint64) WHERE id = $p0;`, 73 params: table.NewQueryParameters( 74 table.ValueParam("$p0", types.Uint64Value(6)), 75 ), 76 }, 77 { 78 b: nil, 79 sql: "SELECT ?, $1, $p0", 80 yql: "SELECT ?, $1, $p0", 81 params: table.NewQueryParameters(), 82 }, 83 { 84 b: testutil.QueryBind(), 85 sql: "SELECT ?, $1, $p0", 86 yql: "SELECT ?, $1, $p0", 87 params: table.NewQueryParameters(), 88 }, 89 { 90 b: testutil.QueryBind( 91 ydb.WithTablePathPrefix("/local/path/to/table"), 92 ), 93 sql: "SELECT ?, $1, $p0", 94 yql: `-- bind TablePathPrefix 95 PRAGMA TablePathPrefix("/local/path/to/table"); 96 97 SELECT ?, $1, $p0`, 98 params: table.NewQueryParameters(), 99 }, 100 { 101 b: testutil.QueryBind( 102 ydb.WithAutoDeclare(), 103 ), 104 sql: "SELECT $p0, $p1, $p2, $p0, $p1", 105 args: []interface{}{ 106 1, 107 "test", 108 []string{ 109 "test1", 110 "test2", 111 "test3", 112 }, 113 }, 114 yql: `-- bind declares 115 DECLARE $p0 AS Int32; 116 DECLARE $p1 AS Utf8; 117 DECLARE $p2 AS List<Utf8>; 118 119 SELECT $p0, $p1, $p2, $p0, $p1`, 120 params: table.NewQueryParameters( 121 table.ValueParam("$p0", types.Int32Value(1)), 122 table.ValueParam("$p1", types.TextValue("test")), 123 table.ValueParam("$p2", types.ListValue( 124 types.TextValue("test1"), 125 types.TextValue("test2"), 126 types.TextValue("test3"), 127 )), 128 ), 129 }, 130 { 131 b: testutil.QueryBind( 132 ydb.WithAutoDeclare(), 133 ydb.WithPositionalArgs(), 134 ), 135 sql: "SELECT ?, ?, ?", 136 args: []interface{}{ 137 1, 138 "test", 139 []string{ 140 "test1", 141 "test2", 142 "test3", 143 }, 144 }, 145 yql: `-- bind declares 146 DECLARE $p0 AS Int32; 147 DECLARE $p1 AS Utf8; 148 DECLARE $p2 AS List<Utf8>; 149 150 -- origin query with positional args replacement 151 SELECT $p0, $p1, $p2`, 152 params: table.NewQueryParameters( 153 table.ValueParam("$p0", types.Int32Value(1)), 154 table.ValueParam("$p1", types.TextValue("test")), 155 table.ValueParam("$p2", types.ListValue( 156 types.TextValue("test1"), 157 types.TextValue("test2"), 158 types.TextValue("test3"), 159 )), 160 ), 161 }, 162 { 163 b: testutil.QueryBind( 164 ydb.WithAutoDeclare(), 165 ydb.WithNumericArgs(), 166 ), 167 sql: "SELECT $1, $2, $3, $1, $2", 168 args: []interface{}{ 169 1, 170 "test", 171 []string{ 172 "test1", 173 "test2", 174 "test3", 175 }, 176 }, 177 yql: `-- bind declares 178 DECLARE $p0 AS Int32; 179 DECLARE $p1 AS Utf8; 180 DECLARE $p2 AS List<Utf8>; 181 182 -- origin query with numeric args replacement 183 SELECT $p0, $p1, $p2, $p0, $p1`, 184 params: table.NewQueryParameters( 185 table.ValueParam("$p0", types.Int32Value(1)), 186 table.ValueParam("$p1", types.TextValue("test")), 187 table.ValueParam("$p2", types.ListValue( 188 types.TextValue("test1"), 189 types.TextValue("test2"), 190 types.TextValue("test3"), 191 )), 192 ), 193 }, 194 { 195 b: testutil.QueryBind( 196 ydb.WithTablePathPrefix("/local/path/to/my/folder"), 197 ydb.WithAutoDeclare(), 198 ydb.WithPositionalArgs(), 199 ), 200 sql: "SELECT a, b, c WHERE id = ? AND date < ? AND value IN (?)", 201 args: []interface{}{ 202 1, now, []string{"3"}, 203 }, 204 yql: `-- bind TablePathPrefix 205 PRAGMA TablePathPrefix("/local/path/to/my/folder"); 206 207 -- bind declares 208 DECLARE $p0 AS Int32; 209 DECLARE $p1 AS Timestamp; 210 DECLARE $p2 AS List<Utf8>; 211 212 -- origin query with positional args replacement 213 SELECT a, b, c WHERE id = $p0 AND date < $p1 AND value IN ($p2)`, 214 params: table.NewQueryParameters( 215 table.ValueParam("$p0", types.Int32Value(1)), 216 table.ValueParam("$p1", types.TimestampValueFromTime(now)), 217 table.ValueParam("$p2", types.ListValue(types.TextValue("3"))), 218 ), 219 }, 220 { 221 b: testutil.QueryBind( 222 ydb.WithTablePathPrefix("/local/"), 223 ydb.WithAutoDeclare(), 224 ydb.WithPositionalArgs(), 225 ydb.WithNumericArgs(), 226 ), 227 sql: `SELECT 1`, 228 yql: `-- bind TablePathPrefix 229 PRAGMA TablePathPrefix("/local/"); 230 231 SELECT 1`, 232 params: table.NewQueryParameters(), 233 }, 234 { 235 b: testutil.QueryBind( 236 ydb.WithTablePathPrefix("/local/"), 237 ydb.WithAutoDeclare(), 238 ), 239 sql: ` 240 DECLARE $param1 AS Text; -- some comment 241 DECLARE $param2 AS Text; 242 SELECT $param1, $param2`, 243 args: []interface{}{ 244 sql.Named("param1", 100), 245 sql.Named("$param2", 200), 246 }, 247 yql: `-- bind TablePathPrefix 248 PRAGMA TablePathPrefix("/local/"); 249 250 -- bind declares 251 DECLARE $param1 AS Int32; 252 DECLARE $param2 AS Int32; 253 254 255 DECLARE $param1 AS Text; -- some comment 256 DECLARE $param2 AS Text; 257 SELECT $param1, $param2`, 258 params: table.NewQueryParameters( 259 table.ValueParam("$param1", types.Int32Value(100)), 260 table.ValueParam("$param2", types.Int32Value(200)), 261 ), 262 }, 263 { 264 b: testutil.QueryBind( 265 ydb.WithTablePathPrefix("/local/"), 266 ydb.WithAutoDeclare(), 267 ), 268 sql: ` 269 DECLARE $param2 AS Text; -- some comment 270 SELECT $param1, $param2`, 271 args: []interface{}{ 272 sql.Named("param1", 100), 273 sql.Named("$param2", 200), 274 }, 275 yql: `-- bind TablePathPrefix 276 PRAGMA TablePathPrefix("/local/"); 277 278 -- bind declares 279 DECLARE $param1 AS Int32; 280 DECLARE $param2 AS Int32; 281 282 283 DECLARE $param2 AS Text; -- some comment 284 SELECT $param1, $param2`, 285 params: table.NewQueryParameters( 286 table.ValueParam("$param1", types.Int32Value(100)), 287 table.ValueParam("$param2", types.Int32Value(200)), 288 ), 289 }, 290 { 291 sql: "SELECT 1", 292 yql: "SELECT 1", 293 params: table.NewQueryParameters(), 294 }, 295 { 296 sql: ` 297 SELECT 1`, 298 yql: ` 299 SELECT 1`, 300 params: table.NewQueryParameters(), 301 }, 302 { 303 b: testutil.QueryBind(ydb.WithPositionalArgs()), 304 sql: "SELECT ?, ?", 305 args: []interface{}{ 306 1, 307 }, 308 err: bind.ErrInconsistentArgs, 309 }, 310 { 311 b: testutil.QueryBind(ydb.WithNumericArgs()), 312 sql: "SELECT $0, $1", 313 args: []interface{}{ 314 1, 1, 315 }, 316 err: bind.ErrUnexpectedNumericArgZero, 317 }, 318 { 319 b: testutil.QueryBind(ydb.WithPositionalArgs()), 320 sql: "SELECT ?, ? -- some comment", 321 args: []interface{}{ 322 100, 323 200, 324 }, 325 yql: `-- origin query with positional args replacement 326 SELECT $p0, $p1 -- some comment`, 327 params: table.NewQueryParameters( 328 table.ValueParam("$p0", types.Int32Value(100)), 329 table.ValueParam("$p1", types.Int32Value(200)), 330 ), 331 }, 332 { 333 b: testutil.QueryBind(ydb.WithPositionalArgs()), 334 sql: "SELECT ?, ? -- some comment", 335 args: []interface{}{ 336 100, 337 }, 338 yql: `-- origin query with positional args replacement 339 SELECT $p0, $p1 -- some comment`, 340 params: table.NewQueryParameters( 341 table.ValueParam("$p0", types.Int32Value(100)), 342 ), 343 err: bind.ErrInconsistentArgs, 344 }, 345 { 346 b: testutil.QueryBind(ydb.WithPositionalArgs()), 347 sql: "SELECT ?, ?, ?", 348 args: []interface{}{ 349 100, 350 200, 351 }, 352 yql: `-- origin query with positional args replacement 353 SELECT $p0, $p1`, 354 params: table.NewQueryParameters( 355 table.ValueParam("$p0", types.Int32Value(100)), 356 table.ValueParam("$p1", types.Int32Value(200)), 357 ), 358 err: bind.ErrInconsistentArgs, 359 }, 360 { 361 b: testutil.QueryBind(ydb.WithPositionalArgs()), 362 sql: ` 363 SELECT ? /* some comment with ? */, ?`, 364 args: []interface{}{ 365 100, 366 200, 367 }, 368 yql: `-- origin query with positional args replacement 369 370 SELECT $p0 /* some comment with ? */, $p1`, 371 params: table.NewQueryParameters( 372 table.ValueParam("$p0", types.Int32Value(100)), 373 table.ValueParam("$p1", types.Int32Value(200)), 374 ), 375 }, 376 { 377 b: testutil.QueryBind( 378 ydb.WithTablePathPrefix("/local/"), 379 ydb.WithPositionalArgs(), 380 ), 381 sql: "SELECT ?, ?", 382 args: []interface{}{ 383 100, 384 200, 385 }, 386 yql: `-- bind TablePathPrefix 387 PRAGMA TablePathPrefix("/local/"); 388 389 -- origin query with positional args replacement 390 SELECT $p0, $p1`, 391 params: table.NewQueryParameters( 392 table.ValueParam("$p0", types.Int32Value(100)), 393 table.ValueParam("$p1", types.Int32Value(200)), 394 ), 395 }, 396 { 397 b: testutil.QueryBind( 398 ydb.WithTablePathPrefix("/local/"), 399 ydb.WithAutoDeclare(), 400 ydb.WithPositionalArgs(), 401 ), 402 sql: "SELECT ?, ?", 403 args: []interface{}{ 404 100, 405 200, 406 }, 407 yql: `-- bind TablePathPrefix 408 PRAGMA TablePathPrefix("/local/"); 409 410 -- bind declares 411 DECLARE $p0 AS Int32; 412 DECLARE $p1 AS Int32; 413 414 -- origin query with positional args replacement 415 SELECT $p0, $p1`, 416 params: table.NewQueryParameters( 417 table.ValueParam("$p0", types.Int32Value(100)), 418 table.ValueParam("$p1", types.Int32Value(200)), 419 ), 420 }, 421 { 422 b: testutil.QueryBind(ydb.WithNumericArgs()), 423 sql: "SELECT $1 /* some comment with $3 */, $2", 424 args: []interface{}{ 425 1, 426 }, 427 err: bind.ErrInconsistentArgs, 428 }, 429 { 430 b: testutil.QueryBind(ydb.WithNumericArgs()), 431 sql: "SELECT $1 /* some comment with $3 */, $2", 432 args: []interface{}{ 433 100, 434 200, 435 }, 436 yql: `-- origin query with numeric args replacement 437 SELECT $p0 /* some comment with $3 */, $p1`, 438 params: table.NewQueryParameters( 439 table.ValueParam("$p0", types.Int32Value(100)), 440 table.ValueParam("$p1", types.Int32Value(200)), 441 ), 442 }, 443 { 444 b: testutil.QueryBind( 445 ydb.WithAutoDeclare(), 446 ydb.WithNumericArgs(), 447 ), 448 sql: "SELECT $1, $2", 449 args: []interface{}{ 450 100, 451 200, 452 }, 453 yql: `-- bind declares 454 DECLARE $p0 AS Int32; 455 DECLARE $p1 AS Int32; 456 457 -- origin query with numeric args replacement 458 SELECT $p0, $p1`, 459 params: table.NewQueryParameters( 460 table.ValueParam("$p0", types.Int32Value(100)), 461 table.ValueParam("$p1", types.Int32Value(200)), 462 ), 463 }, 464 { 465 b: testutil.QueryBind(ydb.WithNumericArgs()), 466 sql: ` 467 SELECT $1, $2`, 468 args: []interface{}{ 469 100, 470 200, 471 }, 472 yql: `-- origin query with numeric args replacement 473 474 SELECT $p0, $p1`, 475 params: table.NewQueryParameters( 476 table.ValueParam("$p0", types.Int32Value(100)), 477 table.ValueParam("$p1", types.Int32Value(200)), 478 ), 479 }, 480 { 481 b: testutil.QueryBind( 482 ydb.WithTablePathPrefix("/local/"), 483 ydb.WithNumericArgs(), 484 ), 485 sql: "SELECT $1 /* some comment with $3 */, $2", 486 args: []interface{}{ 487 100, 488 200, 489 }, 490 yql: `-- bind TablePathPrefix 491 PRAGMA TablePathPrefix("/local/"); 492 493 -- origin query with numeric args replacement 494 SELECT $p0 /* some comment with $3 */, $p1`, 495 params: table.NewQueryParameters( 496 table.ValueParam("$p0", types.Int32Value(100)), 497 table.ValueParam("$p1", types.Int32Value(200)), 498 ), 499 }, 500 { 501 b: testutil.QueryBind( 502 ydb.WithTablePathPrefix("/local/"), 503 ydb.WithAutoDeclare(), 504 ydb.WithNumericArgs(), 505 ), 506 sql: "SELECT $1, $2", 507 args: []interface{}{ 508 100, 509 200, 510 }, 511 yql: `-- bind TablePathPrefix 512 PRAGMA TablePathPrefix("/local/"); 513 514 -- bind declares 515 DECLARE $p0 AS Int32; 516 DECLARE $p1 AS Int32; 517 518 -- origin query with numeric args replacement 519 SELECT $p0, $p1`, 520 params: table.NewQueryParameters( 521 table.ValueParam("$p0", types.Int32Value(100)), 522 table.ValueParam("$p1", types.Int32Value(200)), 523 ), 524 }, 525 { 526 b: testutil.QueryBind(ydb.WithTablePathPrefix("/local/")), 527 sql: "SELECT 1", 528 yql: `-- bind TablePathPrefix 529 PRAGMA TablePathPrefix("/local/"); 530 531 SELECT 1`, 532 params: table.NewQueryParameters(), 533 }, 534 { 535 b: testutil.QueryBind( 536 ydb.WithTablePathPrefix("/local/"), 537 ydb.WithAutoDeclare(), 538 ), 539 sql: "SELECT 1", 540 yql: `-- bind TablePathPrefix 541 PRAGMA TablePathPrefix("/local/"); 542 543 SELECT 1`, 544 params: table.NewQueryParameters(), 545 }, 546 { 547 b: testutil.QueryBind( 548 ydb.WithTablePathPrefix("/local/"), 549 ydb.WithAutoDeclare(), 550 ), 551 sql: "SELECT $param1, $param2", 552 args: []interface{}{ 553 sql.Named("param1", 100), 554 sql.Named("$param2", 200), 555 }, 556 yql: `-- bind TablePathPrefix 557 PRAGMA TablePathPrefix("/local/"); 558 559 -- bind declares 560 DECLARE $param1 AS Int32; 561 DECLARE $param2 AS Int32; 562 563 SELECT $param1, $param2`, 564 params: table.NewQueryParameters( 565 table.ValueParam("$param1", types.Int32Value(100)), 566 table.ValueParam("$param2", types.Int32Value(200)), 567 ), 568 }, 569 } { 570 t.Run("", func(t *testing.T) { 571 yql, parameters, err := tt.b.RewriteQuery(tt.sql, tt.args...) 572 if tt.err != nil { 573 require.Error(t, err) 574 require.ErrorIs(t, err, tt.err) 575 } else { 576 require.NoError(t, err) 577 require.Equal(t, tt.yql, yql) 578 require.Equal(t, []*params.Parameter(*tt.params), parameters) 579 } 580 }) 581 } 582 }