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  }