github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/query/scanner/struct_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  	"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
    13  )
    14  
    15  func TestFieldName(t *testing.T) {
    16  	for _, tt := range []struct {
    17  		name string
    18  		in   interface{}
    19  		out  string
    20  	}{
    21  		{
    22  			name: xtest.CurrentFileLine(),
    23  			in: struct {
    24  				Col0 string
    25  			}{},
    26  			out: "Col0",
    27  		},
    28  		{
    29  			name: xtest.CurrentFileLine(),
    30  			in: struct {
    31  				Col0 string `sql:"col0"`
    32  			}{},
    33  			out: "col0",
    34  		},
    35  	} {
    36  		t.Run(tt.name, func(t *testing.T) {
    37  			require.Equal(t, tt.out, fieldName(reflect.ValueOf(tt.in).Type().Field(0), "sql"))
    38  		})
    39  	}
    40  }
    41  
    42  func TestStruct(t *testing.T) {
    43  	newScannerData := func(mapping map[*Ydb.Column]*Ydb.Value) *data {
    44  		data := &data{
    45  			columns: make([]*Ydb.Column, 0, len(mapping)),
    46  			values:  make([]*Ydb.Value, 0, len(mapping)),
    47  		}
    48  		for c, v := range mapping {
    49  			data.columns = append(data.columns, c)
    50  			data.values = append(data.values, v)
    51  		}
    52  
    53  		return data
    54  	}
    55  
    56  	type scanData struct { //nolint:maligned
    57  		Utf8String    string
    58  		Utf8Bytes     []byte
    59  		StringString  string
    60  		StringBytes   []byte
    61  		Uint64Uint64  uint64
    62  		Int64Int64    int64
    63  		Uint32Uint64  uint64
    64  		Uint32Int64   int64
    65  		Uint32Uint32  uint32
    66  		Int32Int64    int64
    67  		Int32Int32    int32
    68  		Uint16Uint64  uint64
    69  		Uint16Int64   int64
    70  		Uint16Uint32  uint32
    71  		Uint16Int32   int32
    72  		Uint16Uint16  uint16
    73  		Int16Int64    int64
    74  		Int16Int32    int32
    75  		Uint8Uint64   uint64
    76  		Uint8Int64    int64
    77  		Uint8Uint32   uint32
    78  		Uint8Int32    int32
    79  		Uint8Uint16   uint16
    80  		Int8Int64     int64
    81  		Int8Int32     int32
    82  		Int8Int16     int16
    83  		BoolBool      bool
    84  		DateTime      time.Time
    85  		DatetimeTime  time.Time
    86  		TimestampTime time.Time
    87  	}
    88  	var dst scanData
    89  	err := Struct(newScannerData(map[*Ydb.Column]*Ydb.Value{
    90  		{
    91  			Name: "Utf8String",
    92  			Type: &Ydb.Type{
    93  				Type: &Ydb.Type_TypeId{
    94  					TypeId: Ydb.Type_UTF8,
    95  				},
    96  			},
    97  		}: {
    98  			Value: &Ydb.Value_TextValue{
    99  				TextValue: "A",
   100  			},
   101  		},
   102  		{
   103  			Name: "Utf8Bytes",
   104  			Type: &Ydb.Type{
   105  				Type: &Ydb.Type_TypeId{
   106  					TypeId: Ydb.Type_UTF8,
   107  				},
   108  			},
   109  		}: {
   110  			Value: &Ydb.Value_TextValue{
   111  				TextValue: "A",
   112  			},
   113  		},
   114  		{
   115  			Name: "StringString",
   116  			Type: &Ydb.Type{
   117  				Type: &Ydb.Type_TypeId{
   118  					TypeId: Ydb.Type_STRING,
   119  				},
   120  			},
   121  		}: {
   122  			Value: &Ydb.Value_BytesValue{
   123  				BytesValue: []byte("A"),
   124  			},
   125  		},
   126  		{
   127  			Name: "StringBytes",
   128  			Type: &Ydb.Type{
   129  				Type: &Ydb.Type_TypeId{
   130  					TypeId: Ydb.Type_STRING,
   131  				},
   132  			},
   133  		}: {
   134  			Value: &Ydb.Value_BytesValue{
   135  				BytesValue: []byte("A"),
   136  			},
   137  		},
   138  		{
   139  			Name: "Uint64Uint64",
   140  			Type: &Ydb.Type{
   141  				Type: &Ydb.Type_TypeId{
   142  					TypeId: Ydb.Type_UINT64,
   143  				},
   144  			},
   145  		}: {
   146  			Value: &Ydb.Value_Uint64Value{
   147  				Uint64Value: 123,
   148  			},
   149  		},
   150  		{
   151  			Name: "Int64Int64",
   152  			Type: &Ydb.Type{
   153  				Type: &Ydb.Type_TypeId{
   154  					TypeId: Ydb.Type_INT64,
   155  				},
   156  			},
   157  		}: {
   158  			Value: &Ydb.Value_Int64Value{
   159  				Int64Value: 123,
   160  			},
   161  		},
   162  		{
   163  			Name: "Uint32Uint64",
   164  			Type: &Ydb.Type{
   165  				Type: &Ydb.Type_TypeId{
   166  					TypeId: Ydb.Type_UINT32,
   167  				},
   168  			},
   169  		}: {
   170  			Value: &Ydb.Value_Uint32Value{
   171  				Uint32Value: 123,
   172  			},
   173  		},
   174  		{
   175  			Name: "Uint32Int64",
   176  			Type: &Ydb.Type{
   177  				Type: &Ydb.Type_TypeId{
   178  					TypeId: Ydb.Type_UINT32,
   179  				},
   180  			},
   181  		}: {
   182  			Value: &Ydb.Value_Uint32Value{
   183  				Uint32Value: 123,
   184  			},
   185  		},
   186  		{
   187  			Name: "Uint32Uint32",
   188  			Type: &Ydb.Type{
   189  				Type: &Ydb.Type_TypeId{
   190  					TypeId: Ydb.Type_UINT32,
   191  				},
   192  			},
   193  		}: {
   194  			Value: &Ydb.Value_Uint32Value{
   195  				Uint32Value: 123,
   196  			},
   197  		},
   198  		{
   199  			Name: "Int32Int64",
   200  			Type: &Ydb.Type{
   201  				Type: &Ydb.Type_TypeId{
   202  					TypeId: Ydb.Type_INT32,
   203  				},
   204  			},
   205  		}: {
   206  			Value: &Ydb.Value_Int32Value{
   207  				Int32Value: 123,
   208  			},
   209  		},
   210  		{
   211  			Name: "Int32Int32",
   212  			Type: &Ydb.Type{
   213  				Type: &Ydb.Type_TypeId{
   214  					TypeId: Ydb.Type_INT32,
   215  				},
   216  			},
   217  		}: {
   218  			Value: &Ydb.Value_Int32Value{
   219  				Int32Value: 123,
   220  			},
   221  		},
   222  		{
   223  			Name: "Uint16Uint64",
   224  			Type: &Ydb.Type{
   225  				Type: &Ydb.Type_TypeId{
   226  					TypeId: Ydb.Type_UINT16,
   227  				},
   228  			},
   229  		}: {
   230  			Value: &Ydb.Value_Uint32Value{
   231  				Uint32Value: 123,
   232  			},
   233  		},
   234  		{
   235  			Name: "Uint16Int64",
   236  			Type: &Ydb.Type{
   237  				Type: &Ydb.Type_TypeId{
   238  					TypeId: Ydb.Type_UINT16,
   239  				},
   240  			},
   241  		}: {
   242  			Value: &Ydb.Value_Uint32Value{
   243  				Uint32Value: 123,
   244  			},
   245  		},
   246  		{
   247  			Name: "Uint16Uint32",
   248  			Type: &Ydb.Type{
   249  				Type: &Ydb.Type_TypeId{
   250  					TypeId: Ydb.Type_UINT16,
   251  				},
   252  			},
   253  		}: {
   254  			Value: &Ydb.Value_Uint32Value{
   255  				Uint32Value: 123,
   256  			},
   257  		},
   258  		{
   259  			Name: "Uint16Int32",
   260  			Type: &Ydb.Type{
   261  				Type: &Ydb.Type_TypeId{
   262  					TypeId: Ydb.Type_UINT16,
   263  				},
   264  			},
   265  		}: {
   266  			Value: &Ydb.Value_Uint32Value{
   267  				Uint32Value: 123,
   268  			},
   269  		},
   270  		{
   271  			Name: "Uint16Uint16",
   272  			Type: &Ydb.Type{
   273  				Type: &Ydb.Type_TypeId{
   274  					TypeId: Ydb.Type_UINT16,
   275  				},
   276  			},
   277  		}: {
   278  			Value: &Ydb.Value_Uint32Value{
   279  				Uint32Value: 123,
   280  			},
   281  		},
   282  		{
   283  			Name: "Int16Int64",
   284  			Type: &Ydb.Type{
   285  				Type: &Ydb.Type_TypeId{
   286  					TypeId: Ydb.Type_INT16,
   287  				},
   288  			},
   289  		}: {
   290  			Value: &Ydb.Value_Int32Value{
   291  				Int32Value: 123,
   292  			},
   293  		},
   294  		{
   295  			Name: "Int16Int32",
   296  			Type: &Ydb.Type{
   297  				Type: &Ydb.Type_TypeId{
   298  					TypeId: Ydb.Type_INT16,
   299  				},
   300  			},
   301  		}: {
   302  			Value: &Ydb.Value_Int32Value{
   303  				Int32Value: 123,
   304  			},
   305  		},
   306  		{
   307  			Name: "Uint8Uint64",
   308  			Type: &Ydb.Type{
   309  				Type: &Ydb.Type_TypeId{
   310  					TypeId: Ydb.Type_UINT16,
   311  				},
   312  			},
   313  		}: {
   314  			Value: &Ydb.Value_Uint32Value{
   315  				Uint32Value: 123,
   316  			},
   317  		},
   318  		{
   319  			Name: "Uint8Int64",
   320  			Type: &Ydb.Type{
   321  				Type: &Ydb.Type_TypeId{
   322  					TypeId: Ydb.Type_UINT16,
   323  				},
   324  			},
   325  		}: {
   326  			Value: &Ydb.Value_Uint32Value{
   327  				Uint32Value: 123,
   328  			},
   329  		},
   330  		{
   331  			Name: "Uint8Uint32",
   332  			Type: &Ydb.Type{
   333  				Type: &Ydb.Type_TypeId{
   334  					TypeId: Ydb.Type_UINT16,
   335  				},
   336  			},
   337  		}: {
   338  			Value: &Ydb.Value_Uint32Value{
   339  				Uint32Value: 123,
   340  			},
   341  		},
   342  		{
   343  			Name: "Uint8Int32",
   344  			Type: &Ydb.Type{
   345  				Type: &Ydb.Type_TypeId{
   346  					TypeId: Ydb.Type_UINT16,
   347  				},
   348  			},
   349  		}: {
   350  			Value: &Ydb.Value_Uint32Value{
   351  				Uint32Value: 123,
   352  			},
   353  		},
   354  		{
   355  			Name: "Uint8Uint16",
   356  			Type: &Ydb.Type{
   357  				Type: &Ydb.Type_TypeId{
   358  					TypeId: Ydb.Type_UINT16,
   359  				},
   360  			},
   361  		}: {
   362  			Value: &Ydb.Value_Uint32Value{
   363  				Uint32Value: 123,
   364  			},
   365  		},
   366  		{
   367  			Name: "Int8Int64",
   368  			Type: &Ydb.Type{
   369  				Type: &Ydb.Type_TypeId{
   370  					TypeId: Ydb.Type_INT16,
   371  				},
   372  			},
   373  		}: {
   374  			Value: &Ydb.Value_Int32Value{
   375  				Int32Value: 123,
   376  			},
   377  		},
   378  		{
   379  			Name: "Int8Int32",
   380  			Type: &Ydb.Type{
   381  				Type: &Ydb.Type_TypeId{
   382  					TypeId: Ydb.Type_INT16,
   383  				},
   384  			},
   385  		}: {
   386  			Value: &Ydb.Value_Int32Value{
   387  				Int32Value: 123,
   388  			},
   389  		},
   390  		{
   391  			Name: "Int8Int16",
   392  			Type: &Ydb.Type{
   393  				Type: &Ydb.Type_TypeId{
   394  					TypeId: Ydb.Type_INT16,
   395  				},
   396  			},
   397  		}: {
   398  			Value: &Ydb.Value_Int32Value{
   399  				Int32Value: 123,
   400  			},
   401  		},
   402  		{
   403  			Name: "BoolBool",
   404  			Type: &Ydb.Type{
   405  				Type: &Ydb.Type_TypeId{
   406  					TypeId: Ydb.Type_BOOL,
   407  				},
   408  			},
   409  		}: {
   410  			Value: &Ydb.Value_BoolValue{
   411  				BoolValue: true,
   412  			},
   413  		},
   414  		{
   415  			Name: "DateTime",
   416  			Type: &Ydb.Type{
   417  				Type: &Ydb.Type_TypeId{
   418  					TypeId: Ydb.Type_DATE,
   419  				},
   420  			},
   421  		}: {
   422  			Value: &Ydb.Value_Uint32Value{
   423  				Uint32Value: 100500,
   424  			},
   425  		},
   426  		{
   427  			Name: "DatetimeTime",
   428  			Type: &Ydb.Type{
   429  				Type: &Ydb.Type_TypeId{
   430  					TypeId: Ydb.Type_DATETIME,
   431  				},
   432  			},
   433  		}: {
   434  			Value: &Ydb.Value_Uint32Value{
   435  				Uint32Value: 100500,
   436  			},
   437  		},
   438  		{
   439  			Name: "TimestampTime",
   440  			Type: &Ydb.Type{
   441  				Type: &Ydb.Type_TypeId{
   442  					TypeId: Ydb.Type_TIMESTAMP,
   443  				},
   444  			},
   445  		}: {
   446  			Value: &Ydb.Value_Uint64Value{
   447  				Uint64Value: 12345678987654321,
   448  			},
   449  		},
   450  	})).ScanStruct(&dst)
   451  	require.NoError(t, err)
   452  	require.Equal(t, scanData{
   453  		Utf8String:    "A",
   454  		Utf8Bytes:     []byte("A"),
   455  		StringString:  "A",
   456  		StringBytes:   []byte("A"),
   457  		Uint64Uint64:  123,
   458  		Int64Int64:    123,
   459  		Uint32Uint64:  123,
   460  		Uint32Int64:   123,
   461  		Uint32Uint32:  123,
   462  		Int32Int64:    123,
   463  		Int32Int32:    123,
   464  		Uint16Uint64:  123,
   465  		Uint16Int64:   123,
   466  		Uint16Uint32:  123,
   467  		Uint16Int32:   123,
   468  		Uint16Uint16:  123,
   469  		Int16Int64:    123,
   470  		Int16Int32:    123,
   471  		Uint8Uint64:   123,
   472  		Uint8Int64:    123,
   473  		Uint8Uint32:   123,
   474  		Uint8Int32:    123,
   475  		Uint8Uint16:   123,
   476  		Int8Int64:     123,
   477  		Int8Int32:     123,
   478  		Int8Int16:     123,
   479  		BoolBool:      true,
   480  		DateTime:      time.Unix(8683200000, 0).UTC(),
   481  		DatetimeTime:  time.Unix(100500, 0),
   482  		TimestampTime: time.Unix(12345678987, 654321000),
   483  	}, dst)
   484  }
   485  
   486  func TestStructNotAPointer(t *testing.T) {
   487  	scanner := Struct(Data(
   488  		[]*Ydb.Column{
   489  			{
   490  				Name: "a",
   491  				Type: &Ydb.Type{
   492  					Type: &Ydb.Type_TypeId{
   493  						TypeId: Ydb.Type_UTF8,
   494  					},
   495  				},
   496  			},
   497  		},
   498  		[]*Ydb.Value{
   499  			{
   500  				Value: &Ydb.Value_TextValue{
   501  					TextValue: "test",
   502  				},
   503  			},
   504  		},
   505  	))
   506  	var row struct {
   507  		B string
   508  		C string
   509  	}
   510  	err := scanner.ScanStruct(row)
   511  	require.ErrorIs(t, err, errDstTypeIsNotAPointer)
   512  }
   513  
   514  func TestStructNotAPointerToStruct(t *testing.T) {
   515  	scanner := Struct(Data(
   516  		[]*Ydb.Column{
   517  			{
   518  				Name: "a",
   519  				Type: &Ydb.Type{
   520  					Type: &Ydb.Type_TypeId{
   521  						TypeId: Ydb.Type_UTF8,
   522  					},
   523  				},
   524  			},
   525  		},
   526  		[]*Ydb.Value{
   527  			{
   528  				Value: &Ydb.Value_TextValue{
   529  					TextValue: "test",
   530  				},
   531  			},
   532  		},
   533  	))
   534  	var row string
   535  	err := scanner.ScanStruct(&row)
   536  	require.ErrorIs(t, err, errDstTypeIsNotAPointerToStruct)
   537  }
   538  
   539  func TestStructCastFailed(t *testing.T) {
   540  	scanner := Struct(Data(
   541  		[]*Ydb.Column{
   542  			{
   543  				Name: "A",
   544  				Type: &Ydb.Type{
   545  					Type: &Ydb.Type_TypeId{
   546  						TypeId: Ydb.Type_UTF8,
   547  					},
   548  				},
   549  			},
   550  		},
   551  		[]*Ydb.Value{
   552  			{
   553  				Value: &Ydb.Value_TextValue{
   554  					TextValue: "test",
   555  				},
   556  			},
   557  		},
   558  	))
   559  	var row struct {
   560  		A uint64
   561  	}
   562  	err := scanner.ScanStruct(&row)
   563  	require.ErrorIs(t, err, value.ErrCannotCast)
   564  }
   565  
   566  func TestStructCastFailedErrMsg(t *testing.T) {
   567  	scanner := Struct(Data(
   568  		[]*Ydb.Column{
   569  			{
   570  				Name: "A",
   571  				Type: &Ydb.Type{
   572  					Type: &Ydb.Type_TypeId{
   573  						TypeId: Ydb.Type_UTF8,
   574  					},
   575  				},
   576  			},
   577  		},
   578  		[]*Ydb.Value{
   579  			{
   580  				Value: &Ydb.Value_TextValue{
   581  					TextValue: "test",
   582  				},
   583  			},
   584  		},
   585  	))
   586  	var row struct {
   587  		A uint64
   588  	}
   589  	err := scanner.ScanStruct(&row)
   590  	require.ErrorContains(t, err, "scan error on struct field name 'A': cast failed")
   591  }
   592  
   593  func TestStructNotFoundColumns(t *testing.T) {
   594  	scanner := Struct(Data(
   595  		[]*Ydb.Column{
   596  			{
   597  				Name: "a",
   598  				Type: &Ydb.Type{
   599  					Type: &Ydb.Type_TypeId{
   600  						TypeId: Ydb.Type_UTF8,
   601  					},
   602  				},
   603  			},
   604  		},
   605  		[]*Ydb.Value{
   606  			{
   607  				Value: &Ydb.Value_TextValue{
   608  					TextValue: "test",
   609  				},
   610  			},
   611  		},
   612  	))
   613  	var row struct {
   614  		B string
   615  		C string
   616  	}
   617  	err := scanner.ScanStruct(&row)
   618  	require.ErrorIs(t, err, ErrColumnsNotFoundInRow)
   619  }
   620  
   621  func TestStructSkippedColumns(t *testing.T) {
   622  	scanner := Struct(Data(
   623  		[]*Ydb.Column{
   624  			{
   625  				Name: "A",
   626  				Type: &Ydb.Type{
   627  					Type: &Ydb.Type_TypeId{
   628  						TypeId: Ydb.Type_UTF8,
   629  					},
   630  				},
   631  			},
   632  		},
   633  		[]*Ydb.Value{
   634  			{
   635  				Value: &Ydb.Value_TextValue{
   636  					TextValue: "test-a",
   637  				},
   638  			},
   639  		},
   640  	))
   641  
   642  	var row struct {
   643  		A string
   644  		C string `sql:"-"`
   645  	}
   646  	err := scanner.ScanStruct(&row)
   647  	require.NoError(t, err)
   648  	require.Equal(t, "test-a", row.A)
   649  	require.Empty(t, row.C)
   650  }
   651  
   652  func TestStructWithAllowMissingColumnsFromSelect(t *testing.T) {
   653  	scanner := Struct(Data(
   654  		[]*Ydb.Column{
   655  			{
   656  				Name: "A",
   657  				Type: &Ydb.Type{
   658  					Type: &Ydb.Type_TypeId{
   659  						TypeId: Ydb.Type_UTF8,
   660  					},
   661  				},
   662  			},
   663  		},
   664  		[]*Ydb.Value{
   665  			{
   666  				Value: &Ydb.Value_TextValue{
   667  					TextValue: "test",
   668  				},
   669  			},
   670  		},
   671  	))
   672  	var row struct {
   673  		A string
   674  		B string
   675  		C string
   676  	}
   677  	err := scanner.ScanStruct(&row,
   678  		WithAllowMissingColumnsFromSelect(),
   679  	)
   680  	require.NoError(t, err)
   681  	require.Equal(t, "test", row.A)
   682  	require.Equal(t, "", row.B)
   683  	require.Equal(t, "", row.C)
   684  }
   685  
   686  func TestStructNotFoundFields(t *testing.T) {
   687  	scanner := Struct(Data(
   688  		[]*Ydb.Column{
   689  			{
   690  				Name: "A",
   691  				Type: &Ydb.Type{
   692  					Type: &Ydb.Type_TypeId{
   693  						TypeId: Ydb.Type_UTF8,
   694  					},
   695  				},
   696  			},
   697  			{
   698  				Name: "B",
   699  				Type: &Ydb.Type{
   700  					Type: &Ydb.Type_TypeId{
   701  						TypeId: Ydb.Type_UTF8,
   702  					},
   703  				},
   704  			},
   705  			{
   706  				Name: "C",
   707  				Type: &Ydb.Type{
   708  					Type: &Ydb.Type_TypeId{
   709  						TypeId: Ydb.Type_UTF8,
   710  					},
   711  				},
   712  			},
   713  		},
   714  		[]*Ydb.Value{
   715  			{
   716  				Value: &Ydb.Value_TextValue{
   717  					TextValue: "test",
   718  				},
   719  			},
   720  			{
   721  				Value: &Ydb.Value_TextValue{
   722  					TextValue: "test",
   723  				},
   724  			},
   725  			{
   726  				Value: &Ydb.Value_TextValue{
   727  					TextValue: "test",
   728  				},
   729  			},
   730  		},
   731  	))
   732  	var row struct {
   733  		A string
   734  	}
   735  	err := scanner.ScanStruct(&row)
   736  	require.ErrorIs(t, err, ErrFieldsNotFoundInStruct)
   737  }
   738  
   739  func TestStructWithAllowMissingFieldsInStruct(t *testing.T) {
   740  	scanner := Struct(Data(
   741  		[]*Ydb.Column{
   742  			{
   743  				Name: "A",
   744  				Type: &Ydb.Type{
   745  					Type: &Ydb.Type_TypeId{
   746  						TypeId: Ydb.Type_UTF8,
   747  					},
   748  				},
   749  			},
   750  			{
   751  				Name: "B",
   752  				Type: &Ydb.Type{
   753  					Type: &Ydb.Type_TypeId{
   754  						TypeId: Ydb.Type_UTF8,
   755  					},
   756  				},
   757  			},
   758  			{
   759  				Name: "C",
   760  				Type: &Ydb.Type{
   761  					Type: &Ydb.Type_TypeId{
   762  						TypeId: Ydb.Type_UTF8,
   763  					},
   764  				},
   765  			},
   766  		},
   767  		[]*Ydb.Value{
   768  			{
   769  				Value: &Ydb.Value_TextValue{
   770  					TextValue: "test",
   771  				},
   772  			},
   773  			{
   774  				Value: &Ydb.Value_TextValue{
   775  					TextValue: "test",
   776  				},
   777  			},
   778  			{
   779  				Value: &Ydb.Value_TextValue{
   780  					TextValue: "test",
   781  				},
   782  			},
   783  		},
   784  	))
   785  	var row struct {
   786  		A string
   787  	}
   788  	err := scanner.ScanStruct(&row,
   789  		WithAllowMissingFieldsInStruct(),
   790  	)
   791  	require.NoError(t, err)
   792  	require.Equal(t, "test", row.A)
   793  }
   794  
   795  func TestStructWithTagName(t *testing.T) {
   796  	scanner := Struct(Data(
   797  		[]*Ydb.Column{
   798  			{
   799  				Name: "A",
   800  				Type: &Ydb.Type{
   801  					Type: &Ydb.Type_TypeId{
   802  						TypeId: Ydb.Type_UTF8,
   803  					},
   804  				},
   805  			},
   806  			{
   807  				Name: "B",
   808  				Type: &Ydb.Type{
   809  					Type: &Ydb.Type_TypeId{
   810  						TypeId: Ydb.Type_UTF8,
   811  					},
   812  				},
   813  			},
   814  			{
   815  				Name: "C",
   816  				Type: &Ydb.Type{
   817  					Type: &Ydb.Type_TypeId{
   818  						TypeId: Ydb.Type_UTF8,
   819  					},
   820  				},
   821  			},
   822  		},
   823  		[]*Ydb.Value{
   824  			{
   825  				Value: &Ydb.Value_TextValue{
   826  					TextValue: "AA",
   827  				},
   828  			},
   829  			{
   830  				Value: &Ydb.Value_TextValue{
   831  					TextValue: "BB",
   832  				},
   833  			},
   834  			{
   835  				Value: &Ydb.Value_TextValue{
   836  					TextValue: "CC",
   837  				},
   838  			},
   839  		},
   840  	))
   841  	var row struct {
   842  		A string `test:"A"`
   843  		B string `test:"B"`
   844  		C string `test:"C"`
   845  	}
   846  	err := scanner.ScanStruct(&row,
   847  		WithTagName("test"),
   848  	)
   849  	require.NoError(t, err)
   850  	require.Equal(t, "AA", row.A)
   851  	require.Equal(t, "BB", row.B)
   852  	require.Equal(t, "CC", row.C)
   853  }
   854  
   855  func TestScannerStructOrdering(t *testing.T) {
   856  	scanner := Struct(Data(
   857  		[]*Ydb.Column{
   858  			{
   859  				Name: "B",
   860  				Type: &Ydb.Type{
   861  					Type: &Ydb.Type_TypeId{
   862  						TypeId: Ydb.Type_UTF8,
   863  					},
   864  				},
   865  			},
   866  			{
   867  				Name: "A",
   868  				Type: &Ydb.Type{
   869  					Type: &Ydb.Type_TypeId{
   870  						TypeId: Ydb.Type_UTF8,
   871  					},
   872  				},
   873  			},
   874  			{
   875  				Name: "C",
   876  				Type: &Ydb.Type{
   877  					Type: &Ydb.Type_TypeId{
   878  						TypeId: Ydb.Type_UTF8,
   879  					},
   880  				},
   881  			},
   882  		},
   883  		[]*Ydb.Value{
   884  			{
   885  				Value: &Ydb.Value_TextValue{
   886  					TextValue: "B",
   887  				},
   888  			},
   889  			{
   890  				Value: &Ydb.Value_TextValue{
   891  					TextValue: "A",
   892  				},
   893  			},
   894  			{
   895  				Value: &Ydb.Value_TextValue{
   896  					TextValue: "C",
   897  				},
   898  			},
   899  		},
   900  	))
   901  	var row struct {
   902  		A string
   903  		B string
   904  		C string
   905  	}
   906  	err := scanner.ScanStruct(&row)
   907  	require.NoError(t, err)
   908  	require.Equal(t, "A", row.A)
   909  	require.Equal(t, "B", row.B)
   910  	require.Equal(t, "C", row.C)
   911  }