github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/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),
   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 TestStructNotFoundColumns(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  		B string
   588  		C string
   589  	}
   590  	err := scanner.ScanStruct(&row)
   591  	require.ErrorIs(t, err, errColumnsNotFoundInRow)
   592  }
   593  
   594  func TestStructWithAllowMissingColumnsFromSelect(t *testing.T) {
   595  	scanner := Struct(Data(
   596  		[]*Ydb.Column{
   597  			{
   598  				Name: "A",
   599  				Type: &Ydb.Type{
   600  					Type: &Ydb.Type_TypeId{
   601  						TypeId: Ydb.Type_UTF8,
   602  					},
   603  				},
   604  			},
   605  		},
   606  		[]*Ydb.Value{
   607  			{
   608  				Value: &Ydb.Value_TextValue{
   609  					TextValue: "test",
   610  				},
   611  			},
   612  		},
   613  	))
   614  	var row struct {
   615  		A string
   616  		B string
   617  		C string
   618  	}
   619  	err := scanner.ScanStruct(&row,
   620  		WithAllowMissingColumnsFromSelect(),
   621  	)
   622  	require.NoError(t, err)
   623  	require.Equal(t, "test", row.A)
   624  	require.Equal(t, "", row.B)
   625  	require.Equal(t, "", row.C)
   626  }
   627  
   628  func TestStructNotFoundFields(t *testing.T) {
   629  	scanner := Struct(Data(
   630  		[]*Ydb.Column{
   631  			{
   632  				Name: "A",
   633  				Type: &Ydb.Type{
   634  					Type: &Ydb.Type_TypeId{
   635  						TypeId: Ydb.Type_UTF8,
   636  					},
   637  				},
   638  			},
   639  			{
   640  				Name: "B",
   641  				Type: &Ydb.Type{
   642  					Type: &Ydb.Type_TypeId{
   643  						TypeId: Ydb.Type_UTF8,
   644  					},
   645  				},
   646  			},
   647  			{
   648  				Name: "C",
   649  				Type: &Ydb.Type{
   650  					Type: &Ydb.Type_TypeId{
   651  						TypeId: Ydb.Type_UTF8,
   652  					},
   653  				},
   654  			},
   655  		},
   656  		[]*Ydb.Value{
   657  			{
   658  				Value: &Ydb.Value_TextValue{
   659  					TextValue: "test",
   660  				},
   661  			},
   662  			{
   663  				Value: &Ydb.Value_TextValue{
   664  					TextValue: "test",
   665  				},
   666  			},
   667  			{
   668  				Value: &Ydb.Value_TextValue{
   669  					TextValue: "test",
   670  				},
   671  			},
   672  		},
   673  	))
   674  	var row struct {
   675  		A string
   676  	}
   677  	err := scanner.ScanStruct(&row)
   678  	require.ErrorIs(t, err, errFieldsNotFoundInStruct)
   679  }
   680  
   681  func TestStructWithAllowMissingFieldsInStruct(t *testing.T) {
   682  	scanner := Struct(Data(
   683  		[]*Ydb.Column{
   684  			{
   685  				Name: "A",
   686  				Type: &Ydb.Type{
   687  					Type: &Ydb.Type_TypeId{
   688  						TypeId: Ydb.Type_UTF8,
   689  					},
   690  				},
   691  			},
   692  			{
   693  				Name: "B",
   694  				Type: &Ydb.Type{
   695  					Type: &Ydb.Type_TypeId{
   696  						TypeId: Ydb.Type_UTF8,
   697  					},
   698  				},
   699  			},
   700  			{
   701  				Name: "C",
   702  				Type: &Ydb.Type{
   703  					Type: &Ydb.Type_TypeId{
   704  						TypeId: Ydb.Type_UTF8,
   705  					},
   706  				},
   707  			},
   708  		},
   709  		[]*Ydb.Value{
   710  			{
   711  				Value: &Ydb.Value_TextValue{
   712  					TextValue: "test",
   713  				},
   714  			},
   715  			{
   716  				Value: &Ydb.Value_TextValue{
   717  					TextValue: "test",
   718  				},
   719  			},
   720  			{
   721  				Value: &Ydb.Value_TextValue{
   722  					TextValue: "test",
   723  				},
   724  			},
   725  		},
   726  	))
   727  	var row struct {
   728  		A string
   729  	}
   730  	err := scanner.ScanStruct(&row,
   731  		WithAllowMissingFieldsInStruct(),
   732  	)
   733  	require.NoError(t, err)
   734  	require.Equal(t, "test", row.A)
   735  }
   736  
   737  func TestStructWithTagName(t *testing.T) {
   738  	scanner := Struct(Data(
   739  		[]*Ydb.Column{
   740  			{
   741  				Name: "A",
   742  				Type: &Ydb.Type{
   743  					Type: &Ydb.Type_TypeId{
   744  						TypeId: Ydb.Type_UTF8,
   745  					},
   746  				},
   747  			},
   748  			{
   749  				Name: "B",
   750  				Type: &Ydb.Type{
   751  					Type: &Ydb.Type_TypeId{
   752  						TypeId: Ydb.Type_UTF8,
   753  					},
   754  				},
   755  			},
   756  			{
   757  				Name: "C",
   758  				Type: &Ydb.Type{
   759  					Type: &Ydb.Type_TypeId{
   760  						TypeId: Ydb.Type_UTF8,
   761  					},
   762  				},
   763  			},
   764  		},
   765  		[]*Ydb.Value{
   766  			{
   767  				Value: &Ydb.Value_TextValue{
   768  					TextValue: "AA",
   769  				},
   770  			},
   771  			{
   772  				Value: &Ydb.Value_TextValue{
   773  					TextValue: "BB",
   774  				},
   775  			},
   776  			{
   777  				Value: &Ydb.Value_TextValue{
   778  					TextValue: "CC",
   779  				},
   780  			},
   781  		},
   782  	))
   783  	var row struct {
   784  		A string `test:"A"`
   785  		B string `test:"B"`
   786  		C string `test:"C"`
   787  	}
   788  	err := scanner.ScanStruct(&row,
   789  		WithTagName("test"),
   790  	)
   791  	require.NoError(t, err)
   792  	require.Equal(t, "AA", row.A)
   793  	require.Equal(t, "BB", row.B)
   794  	require.Equal(t, "CC", row.C)
   795  }
   796  
   797  func TestScannerStructOrdering(t *testing.T) {
   798  	scanner := Struct(Data(
   799  		[]*Ydb.Column{
   800  			{
   801  				Name: "B",
   802  				Type: &Ydb.Type{
   803  					Type: &Ydb.Type_TypeId{
   804  						TypeId: Ydb.Type_UTF8,
   805  					},
   806  				},
   807  			},
   808  			{
   809  				Name: "A",
   810  				Type: &Ydb.Type{
   811  					Type: &Ydb.Type_TypeId{
   812  						TypeId: Ydb.Type_UTF8,
   813  					},
   814  				},
   815  			},
   816  			{
   817  				Name: "C",
   818  				Type: &Ydb.Type{
   819  					Type: &Ydb.Type_TypeId{
   820  						TypeId: Ydb.Type_UTF8,
   821  					},
   822  				},
   823  			},
   824  		},
   825  		[]*Ydb.Value{
   826  			{
   827  				Value: &Ydb.Value_TextValue{
   828  					TextValue: "B",
   829  				},
   830  			},
   831  			{
   832  				Value: &Ydb.Value_TextValue{
   833  					TextValue: "A",
   834  				},
   835  			},
   836  			{
   837  				Value: &Ydb.Value_TextValue{
   838  					TextValue: "C",
   839  				},
   840  			},
   841  		},
   842  	))
   843  	var row struct {
   844  		A string
   845  		B string
   846  		C string
   847  	}
   848  	err := scanner.ScanStruct(&row)
   849  	require.NoError(t, err)
   850  	require.Equal(t, "A", row.A)
   851  	require.Equal(t, "B", row.B)
   852  	require.Equal(t, "C", row.C)
   853  }