github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/query/scanner/named_test.go (about)

     1  package scanner
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/require"
     9  	"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
    10  
    11  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/value"
    12  )
    13  
    14  func TestNamed(t *testing.T) {
    15  	for _, tt := range []struct {
    16  		name string
    17  		s    NamedScanner
    18  		dst  [][]interface{}
    19  		exp  [][]interface{}
    20  	}{
    21  		{
    22  			name: "Ydb.Type_UTF8",
    23  			s: Named(Data(
    24  				[]*Ydb.Column{
    25  					{
    26  						Name: "a",
    27  						Type: &Ydb.Type{
    28  							Type: &Ydb.Type_TypeId{
    29  								TypeId: Ydb.Type_UTF8,
    30  							},
    31  						},
    32  					},
    33  				},
    34  				[]*Ydb.Value{
    35  					{
    36  						Value: &Ydb.Value_TextValue{
    37  							TextValue: "test",
    38  						},
    39  					},
    40  				},
    41  			)),
    42  			dst: [][]interface{}{
    43  				{func(v string) *string { return &v }("")},
    44  				{func(v []byte) *[]byte { return &v }([]byte(""))},
    45  			},
    46  			exp: [][]interface{}{
    47  				{func(v string) *string { return &v }("test")},
    48  				{func(v []byte) *[]byte { return &v }([]byte("test"))},
    49  			},
    50  		},
    51  		{
    52  			name: "Ydb.Type_STRING",
    53  			s: Named(Data(
    54  				[]*Ydb.Column{
    55  					{
    56  						Name: "a",
    57  						Type: &Ydb.Type{
    58  							Type: &Ydb.Type_TypeId{
    59  								TypeId: Ydb.Type_STRING,
    60  							},
    61  						},
    62  					},
    63  				},
    64  				[]*Ydb.Value{
    65  					{
    66  						Value: &Ydb.Value_BytesValue{
    67  							BytesValue: []byte("test"),
    68  						},
    69  					},
    70  				},
    71  			)),
    72  			dst: [][]interface{}{
    73  				{func(v string) *string { return &v }("")},
    74  				{func(v []byte) *[]byte { return &v }([]byte(""))},
    75  			},
    76  			exp: [][]interface{}{
    77  				{func(v string) *string { return &v }("test")},
    78  				{func(v []byte) *[]byte { return &v }([]byte("test"))},
    79  			},
    80  		},
    81  		{
    82  			name: "Ydb.Type_UINT64",
    83  			s: Named(Data(
    84  				[]*Ydb.Column{
    85  					{
    86  						Name: "a",
    87  						Type: &Ydb.Type{
    88  							Type: &Ydb.Type_TypeId{
    89  								TypeId: Ydb.Type_UINT64,
    90  							},
    91  						},
    92  					},
    93  				},
    94  				[]*Ydb.Value{
    95  					{
    96  						Value: &Ydb.Value_Uint64Value{
    97  							Uint64Value: 123,
    98  						},
    99  					},
   100  				},
   101  			)),
   102  			dst: [][]interface{}{
   103  				{func(v uint64) *uint64 { return &v }(0)},
   104  			},
   105  			exp: [][]interface{}{
   106  				{func(v uint64) *uint64 { return &v }(123)},
   107  			},
   108  		},
   109  		{
   110  			name: "Ydb.Type_INT64",
   111  			s: Named(Data(
   112  				[]*Ydb.Column{
   113  					{
   114  						Name: "a",
   115  						Type: &Ydb.Type{
   116  							Type: &Ydb.Type_TypeId{
   117  								TypeId: Ydb.Type_INT64,
   118  							},
   119  						},
   120  					},
   121  				},
   122  				[]*Ydb.Value{
   123  					{
   124  						Value: &Ydb.Value_Int64Value{
   125  							Int64Value: 123,
   126  						},
   127  					},
   128  				},
   129  			)),
   130  			dst: [][]interface{}{
   131  				{func(v int64) *int64 { return &v }(0)},
   132  			},
   133  			exp: [][]interface{}{
   134  				{func(v int64) *int64 { return &v }(123)},
   135  			},
   136  		},
   137  		{
   138  			name: "Ydb.Type_UINT32",
   139  			s: Named(Data(
   140  				[]*Ydb.Column{
   141  					{
   142  						Name: "a",
   143  						Type: &Ydb.Type{
   144  							Type: &Ydb.Type_TypeId{
   145  								TypeId: Ydb.Type_UINT32,
   146  							},
   147  						},
   148  					},
   149  				},
   150  				[]*Ydb.Value{
   151  					{
   152  						Value: &Ydb.Value_Uint32Value{
   153  							Uint32Value: 123,
   154  						},
   155  					},
   156  				},
   157  			)),
   158  			dst: [][]interface{}{
   159  				{func(v uint64) *uint64 { return &v }(0)},
   160  				{func(v int64) *int64 { return &v }(0)},
   161  				{func(v uint32) *uint32 { return &v }(0)},
   162  				{func(v float64) *float64 { return &v }(0)},
   163  			},
   164  			exp: [][]interface{}{
   165  				{func(v uint64) *uint64 { return &v }(123)},
   166  				{func(v int64) *int64 { return &v }(123)},
   167  				{func(v uint32) *uint32 { return &v }(123)},
   168  				{func(v float64) *float64 { return &v }(123)},
   169  			},
   170  		},
   171  		{
   172  			name: "Ydb.Type_INT32",
   173  			s: Named(Data(
   174  				[]*Ydb.Column{
   175  					{
   176  						Name: "a",
   177  						Type: &Ydb.Type{
   178  							Type: &Ydb.Type_TypeId{
   179  								TypeId: Ydb.Type_INT32,
   180  							},
   181  						},
   182  					},
   183  				},
   184  				[]*Ydb.Value{
   185  					{
   186  						Value: &Ydb.Value_Int32Value{
   187  							Int32Value: 123,
   188  						},
   189  					},
   190  				},
   191  			)),
   192  			dst: [][]interface{}{
   193  				{func(v int64) *int64 { return &v }(0)},
   194  				{func(v int32) *int32 { return &v }(0)},
   195  				{func(v int) *int { return &v }(0)},
   196  				{func(v float32) *float32 { return &v }(0)},
   197  				{func(v float64) *float64 { return &v }(0)},
   198  			},
   199  			exp: [][]interface{}{
   200  				{func(v int64) *int64 { return &v }(123)},
   201  				{func(v int32) *int32 { return &v }(123)},
   202  				{func(v int) *int { return &v }(123)},
   203  				{func(v float32) *float32 { return &v }(123)},
   204  				{func(v float64) *float64 { return &v }(123)},
   205  			},
   206  		},
   207  		{
   208  			name: "Ydb.Type_UINT16",
   209  			s: Named(Data(
   210  				[]*Ydb.Column{
   211  					{
   212  						Name: "a",
   213  						Type: &Ydb.Type{
   214  							Type: &Ydb.Type_TypeId{
   215  								TypeId: Ydb.Type_UINT16,
   216  							},
   217  						},
   218  					},
   219  				},
   220  				[]*Ydb.Value{
   221  					{
   222  						Value: &Ydb.Value_Uint32Value{
   223  							Uint32Value: 123,
   224  						},
   225  					},
   226  				},
   227  			)),
   228  			dst: [][]interface{}{
   229  				{func(v uint64) *uint64 { return &v }(0)},
   230  				{func(v int64) *int64 { return &v }(0)},
   231  				{func(v uint32) *uint32 { return &v }(0)},
   232  				{func(v int32) *int32 { return &v }(0)},
   233  				{func(v float32) *float32 { return &v }(0)},
   234  				{func(v float64) *float64 { return &v }(0)},
   235  			},
   236  			exp: [][]interface{}{
   237  				{func(v uint64) *uint64 { return &v }(123)},
   238  				{func(v int64) *int64 { return &v }(123)},
   239  				{func(v uint32) *uint32 { return &v }(123)},
   240  				{func(v int32) *int32 { return &v }(123)},
   241  				{func(v float32) *float32 { return &v }(123)},
   242  				{func(v float64) *float64 { return &v }(123)},
   243  			},
   244  		},
   245  		{
   246  			name: "Ydb.Type_INT16",
   247  			s: Named(Data(
   248  				[]*Ydb.Column{
   249  					{
   250  						Name: "a",
   251  						Type: &Ydb.Type{
   252  							Type: &Ydb.Type_TypeId{
   253  								TypeId: Ydb.Type_INT16,
   254  							},
   255  						},
   256  					},
   257  				},
   258  				[]*Ydb.Value{
   259  					{
   260  						Value: &Ydb.Value_Int32Value{
   261  							Int32Value: 123,
   262  						},
   263  					},
   264  				},
   265  			)),
   266  			dst: [][]interface{}{
   267  				{func(v int64) *int64 { return &v }(0)},
   268  				{func(v int32) *int32 { return &v }(0)},
   269  				{func(v float32) *float32 { return &v }(0)},
   270  				{func(v float64) *float64 { return &v }(0)},
   271  			},
   272  			exp: [][]interface{}{
   273  				{func(v int64) *int64 { return &v }(123)},
   274  				{func(v int32) *int32 { return &v }(123)},
   275  				{func(v float32) *float32 { return &v }(123)},
   276  				{func(v float64) *float64 { return &v }(123)},
   277  			},
   278  		},
   279  		{
   280  			name: "Ydb.Type_UINT8",
   281  			s: Named(Data(
   282  				[]*Ydb.Column{
   283  					{
   284  						Name: "a",
   285  						Type: &Ydb.Type{
   286  							Type: &Ydb.Type_TypeId{
   287  								TypeId: Ydb.Type_UINT8,
   288  							},
   289  						},
   290  					},
   291  				},
   292  				[]*Ydb.Value{
   293  					{
   294  						Value: &Ydb.Value_Uint32Value{
   295  							Uint32Value: 123,
   296  						},
   297  					},
   298  				},
   299  			)),
   300  			dst: [][]interface{}{
   301  				{func(v uint64) *uint64 { return &v }(0)},
   302  				{func(v int64) *int64 { return &v }(0)},
   303  				{func(v uint32) *uint32 { return &v }(0)},
   304  				{func(v int32) *int32 { return &v }(0)},
   305  				{func(v uint8) *uint8 { return &v }(0)},
   306  				{func(v float32) *float32 { return &v }(0)},
   307  				{func(v float64) *float64 { return &v }(0)},
   308  			},
   309  			exp: [][]interface{}{
   310  				{func(v uint64) *uint64 { return &v }(123)},
   311  				{func(v int64) *int64 { return &v }(123)},
   312  				{func(v uint32) *uint32 { return &v }(123)},
   313  				{func(v int32) *int32 { return &v }(123)},
   314  				{func(v uint8) *uint8 { return &v }(123)},
   315  				{func(v float32) *float32 { return &v }(123)},
   316  				{func(v float64) *float64 { return &v }(123)},
   317  			},
   318  		},
   319  		{
   320  			name: "Ydb.Type_INT8",
   321  			s: Named(Data(
   322  				[]*Ydb.Column{
   323  					{
   324  						Name: "a",
   325  						Type: &Ydb.Type{
   326  							Type: &Ydb.Type_TypeId{
   327  								TypeId: Ydb.Type_INT8,
   328  							},
   329  						},
   330  					},
   331  				},
   332  				[]*Ydb.Value{
   333  					{
   334  						Value: &Ydb.Value_Int32Value{
   335  							Int32Value: 123,
   336  						},
   337  					},
   338  				},
   339  			)),
   340  			dst: [][]interface{}{
   341  				{func(v int64) *int64 { return &v }(0)},
   342  				{func(v int32) *int32 { return &v }(0)},
   343  				{func(v int8) *int8 { return &v }(0)},
   344  				{func(v float32) *float32 { return &v }(0)},
   345  				{func(v float64) *float64 { return &v }(0)},
   346  			},
   347  			exp: [][]interface{}{
   348  				{func(v int64) *int64 { return &v }(123)},
   349  				{func(v int32) *int32 { return &v }(123)},
   350  				{func(v int8) *int8 { return &v }(123)},
   351  				{func(v float32) *float32 { return &v }(123)},
   352  				{func(v float64) *float64 { return &v }(123)},
   353  			},
   354  		},
   355  		{
   356  			name: "Ydb.Type_BOOL",
   357  			s: Named(Data(
   358  				[]*Ydb.Column{
   359  					{
   360  						Name: "a",
   361  						Type: &Ydb.Type{
   362  							Type: &Ydb.Type_TypeId{
   363  								TypeId: Ydb.Type_BOOL,
   364  							},
   365  						},
   366  					},
   367  				},
   368  				[]*Ydb.Value{
   369  					{
   370  						Value: &Ydb.Value_BoolValue{
   371  							BoolValue: true,
   372  						},
   373  					},
   374  				},
   375  			)),
   376  			dst: [][]interface{}{
   377  				{func(v bool) *bool { return &v }(false)},
   378  			},
   379  			exp: [][]interface{}{
   380  				{func(v bool) *bool { return &v }(true)},
   381  			},
   382  		},
   383  		{
   384  			name: "Ydb.Type_DATE",
   385  			s: Named(Data(
   386  				[]*Ydb.Column{
   387  					{
   388  						Name: "a",
   389  						Type: &Ydb.Type{
   390  							Type: &Ydb.Type_TypeId{
   391  								TypeId: Ydb.Type_DATE,
   392  							},
   393  						},
   394  					},
   395  				},
   396  				[]*Ydb.Value{
   397  					{
   398  						Value: &Ydb.Value_Uint32Value{
   399  							Uint32Value: 100500,
   400  						},
   401  					},
   402  				},
   403  			)),
   404  			dst: [][]interface{}{
   405  				{func(v uint64) *uint64 { return &v }(0)},
   406  				{func(v int64) *int64 { return &v }(0)},
   407  				{func(v int32) *int32 { return &v }(0)},
   408  				{func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
   409  			},
   410  			exp: [][]interface{}{
   411  				{func(v uint64) *uint64 { return &v }(100500)},
   412  				{func(v int64) *int64 { return &v }(100500)},
   413  				{func(v int32) *int32 { return &v }(100500)},
   414  				{func(v time.Time) *time.Time { return &v }(time.Unix(8683200000, 0))},
   415  			},
   416  		},
   417  		{
   418  			name: "Ydb.Type_DATETIME",
   419  			s: Named(Data(
   420  				[]*Ydb.Column{
   421  					{
   422  						Name: "a",
   423  						Type: &Ydb.Type{
   424  							Type: &Ydb.Type_TypeId{
   425  								TypeId: Ydb.Type_DATETIME,
   426  							},
   427  						},
   428  					},
   429  				},
   430  				[]*Ydb.Value{
   431  					{
   432  						Value: &Ydb.Value_Uint32Value{
   433  							Uint32Value: 100500,
   434  						},
   435  					},
   436  				},
   437  			)),
   438  			dst: [][]interface{}{
   439  				{func(v uint64) *uint64 { return &v }(0)},
   440  				{func(v int64) *int64 { return &v }(0)},
   441  				{func(v uint32) *uint32 { return &v }(0)},
   442  				{func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
   443  			},
   444  			exp: [][]interface{}{
   445  				{func(v uint64) *uint64 { return &v }(100500)},
   446  				{func(v int64) *int64 { return &v }(100500)},
   447  				{func(v uint32) *uint32 { return &v }(100500)},
   448  				{func(v time.Time) *time.Time { return &v }(time.Unix(100500, 0))},
   449  			},
   450  		},
   451  		{
   452  			name: "Ydb.Type_TIMESTAMP",
   453  			s: Named(Data(
   454  				[]*Ydb.Column{
   455  					{
   456  						Name: "a",
   457  						Type: &Ydb.Type{
   458  							Type: &Ydb.Type_TypeId{
   459  								TypeId: Ydb.Type_TIMESTAMP,
   460  							},
   461  						},
   462  					},
   463  				},
   464  				[]*Ydb.Value{
   465  					{
   466  						Value: &Ydb.Value_Uint64Value{
   467  							Uint64Value: 12345678987654321,
   468  						},
   469  					},
   470  				},
   471  			)),
   472  			dst: [][]interface{}{
   473  				{func(v uint64) *uint64 { return &v }(0)},
   474  				{func(v time.Time) *time.Time { return &v }(time.Unix(0, 0))},
   475  			},
   476  			exp: [][]interface{}{
   477  				{func(v uint64) *uint64 { return &v }(12345678987654321)},
   478  				{func(v time.Time) *time.Time { return &v }(time.Unix(12345678987, 654321000))},
   479  			},
   480  		},
   481  		{
   482  			name: "Ydb.Type_INTERVAL",
   483  			s: Named(Data(
   484  				[]*Ydb.Column{
   485  					{
   486  						Name: "a",
   487  						Type: &Ydb.Type{
   488  							Type: &Ydb.Type_TypeId{
   489  								TypeId: Ydb.Type_INTERVAL,
   490  							},
   491  						},
   492  					},
   493  				},
   494  				[]*Ydb.Value{
   495  					{
   496  						Value: &Ydb.Value_Int64Value{
   497  							Int64Value: 100500,
   498  						},
   499  					},
   500  				},
   501  			)),
   502  			dst: [][]interface{}{
   503  				{func(v int64) *int64 { return &v }(0)},
   504  				{func(v time.Duration) *time.Duration { return &v }(time.Duration(0))},
   505  			},
   506  			exp: [][]interface{}{
   507  				{func(v int64) *int64 { return &v }(100500)},
   508  				{func(v time.Duration) *time.Duration { return &v }(time.Duration(100500000))},
   509  			},
   510  		},
   511  	} {
   512  		for i := range tt.dst {
   513  			t.Run(tt.name+"→"+reflect.TypeOf(tt.dst[i][0]).Elem().String(), func(t *testing.T) {
   514  				err := tt.s.ScanNamed(func() (dst []NamedDestination) {
   515  					for j := range tt.dst[i] {
   516  						dst = append(dst, NamedRef("a", tt.dst[i][j]))
   517  					}
   518  
   519  					return dst
   520  				}()...)
   521  				require.NoError(t, err)
   522  				require.Equal(t, tt.exp[i], tt.dst[i])
   523  			})
   524  		}
   525  	}
   526  }
   527  
   528  func TestScannerNamedNotFoundByName(t *testing.T) {
   529  	scanner := Named(Data(
   530  		[]*Ydb.Column{
   531  			{
   532  				Name: "a",
   533  				Type: &Ydb.Type{
   534  					Type: &Ydb.Type_TypeId{
   535  						TypeId: Ydb.Type_UTF8,
   536  					},
   537  				},
   538  			},
   539  		},
   540  		[]*Ydb.Value{
   541  			{
   542  				Value: &Ydb.Value_TextValue{
   543  					TextValue: "test",
   544  				},
   545  			},
   546  		},
   547  	))
   548  	var s string
   549  	err := scanner.ScanNamed(NamedRef("b", &s))
   550  	require.ErrorIs(t, err, errColumnsNotFoundInRow)
   551  }
   552  
   553  func TestScannerNamedOrdering(t *testing.T) {
   554  	scanner := Named(Data(
   555  		[]*Ydb.Column{
   556  			{
   557  				Name: "a",
   558  				Type: &Ydb.Type{
   559  					Type: &Ydb.Type_TypeId{
   560  						TypeId: Ydb.Type_UTF8,
   561  					},
   562  				},
   563  			},
   564  			{
   565  				Name: "b",
   566  				Type: &Ydb.Type{
   567  					Type: &Ydb.Type_TypeId{
   568  						TypeId: Ydb.Type_UTF8,
   569  					},
   570  				},
   571  			},
   572  			{
   573  				Name: "c",
   574  				Type: &Ydb.Type{
   575  					Type: &Ydb.Type_TypeId{
   576  						TypeId: Ydb.Type_UTF8,
   577  					},
   578  				},
   579  			},
   580  		},
   581  		[]*Ydb.Value{
   582  			{
   583  				Value: &Ydb.Value_TextValue{
   584  					TextValue: "A",
   585  				},
   586  			},
   587  			{
   588  				Value: &Ydb.Value_TextValue{
   589  					TextValue: "B",
   590  				},
   591  			},
   592  			{
   593  				Value: &Ydb.Value_TextValue{
   594  					TextValue: "C",
   595  				},
   596  			},
   597  		},
   598  	))
   599  	var a, b, c string
   600  	err := scanner.ScanNamed(
   601  		NamedRef("c", &c),
   602  		NamedRef("b", &b),
   603  		NamedRef("a", &a),
   604  	)
   605  	require.NoError(t, err)
   606  	require.Equal(t, "A", a)
   607  	require.Equal(t, "B", b)
   608  	require.Equal(t, "C", c)
   609  }
   610  
   611  func TestNamedRef(t *testing.T) {
   612  	for _, tt := range []struct {
   613  		name  string
   614  		ref   interface{}
   615  		dst   NamedDestination
   616  		panic bool
   617  	}{
   618  		{
   619  			name:  "",
   620  			ref:   nil,
   621  			dst:   NamedDestination{},
   622  			panic: true,
   623  		},
   624  		{
   625  			name:  "nil_ref",
   626  			ref:   nil,
   627  			dst:   NamedDestination{},
   628  			panic: true,
   629  		},
   630  		{
   631  			name:  "not_ref",
   632  			ref:   123,
   633  			dst:   NamedDestination{},
   634  			panic: true,
   635  		},
   636  		{
   637  			name: "int_ptr",
   638  			ref:  func(v int) *int { return &v }(123),
   639  			dst: NamedDestination{
   640  				name: "int_ptr",
   641  				ref:  func(v int) *int { return &v }(123),
   642  			},
   643  			panic: false,
   644  		},
   645  		{
   646  			name: "int_dbl_ptr",
   647  			ref: func(v int) **int {
   648  				vv := &v
   649  
   650  				return &vv
   651  			}(123),
   652  			dst: NamedDestination{
   653  				name: "int_dbl_ptr",
   654  				ref: func(v int) **int {
   655  					vv := &v
   656  
   657  					return &vv
   658  				}(123),
   659  			},
   660  			panic: false,
   661  		},
   662  	} {
   663  		t.Run(tt.name, func(t *testing.T) {
   664  			if tt.panic {
   665  				defer func() {
   666  					require.NotNil(t, recover())
   667  				}()
   668  			} else {
   669  				defer func() {
   670  					require.Nil(t, recover())
   671  				}()
   672  			}
   673  			require.Equal(t, tt.dst, NamedRef(tt.name, tt.ref))
   674  		})
   675  	}
   676  }
   677  
   678  func TestNamedCastFailed(t *testing.T) {
   679  	scanner := Named(Data(
   680  		[]*Ydb.Column{
   681  			{
   682  				Name: "a",
   683  				Type: &Ydb.Type{
   684  					Type: &Ydb.Type_TypeId{
   685  						TypeId: Ydb.Type_UTF8,
   686  					},
   687  				},
   688  			},
   689  		},
   690  		[]*Ydb.Value{
   691  			{
   692  				Value: &Ydb.Value_TextValue{
   693  					TextValue: "test",
   694  				},
   695  			},
   696  		},
   697  	))
   698  	var A uint64
   699  	err := scanner.ScanNamed(NamedRef("a", &A))
   700  	require.ErrorIs(t, err, value.ErrCannotCast)
   701  }