storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/sql/value_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package sql
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"strconv"
    23  	"testing"
    24  	"time"
    25  )
    26  
    27  // valueBuilders contains one constructor for each value type.
    28  // Values should match if type is the same.
    29  var valueBuilders = []func() *Value{
    30  	func() *Value {
    31  		return FromNull()
    32  	},
    33  	func() *Value {
    34  		return FromBool(true)
    35  	},
    36  	func() *Value {
    37  		return FromBytes([]byte("byte contents"))
    38  	},
    39  	func() *Value {
    40  		return FromFloat(math.Pi)
    41  	},
    42  	func() *Value {
    43  		return FromInt(0x1337)
    44  	},
    45  	func() *Value {
    46  		t, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
    47  		if err != nil {
    48  			panic(err)
    49  		}
    50  		return FromTimestamp(t)
    51  	},
    52  	func() *Value {
    53  		return FromString("string contents")
    54  	},
    55  }
    56  
    57  // altValueBuilders contains one constructor for each value type.
    58  // Values are zero values and should NOT match the values in valueBuilders, except Null type.
    59  var altValueBuilders = []func() *Value{
    60  	func() *Value {
    61  		return FromNull()
    62  	},
    63  	func() *Value {
    64  		return FromBool(false)
    65  	},
    66  	func() *Value {
    67  		return FromBytes(nil)
    68  	},
    69  	func() *Value {
    70  		return FromFloat(0)
    71  	},
    72  	func() *Value {
    73  		return FromInt(0)
    74  	},
    75  	func() *Value {
    76  		return FromTimestamp(time.Time{})
    77  	},
    78  	func() *Value {
    79  		return FromString("")
    80  	},
    81  }
    82  
    83  func TestValue_SameTypeAs(t *testing.T) {
    84  	type fields struct {
    85  		a, b Value
    86  	}
    87  	type test struct {
    88  		name   string
    89  		fields fields
    90  		wantOk bool
    91  	}
    92  	var tests []test
    93  	for i := range valueBuilders {
    94  		a := valueBuilders[i]()
    95  		for j := range valueBuilders {
    96  			b := valueBuilders[j]()
    97  			tests = append(tests, test{
    98  				name: fmt.Sprint(a.GetTypeString(), "==", b.GetTypeString()),
    99  				fields: fields{
   100  					a: *a, b: *b,
   101  				},
   102  				wantOk: i == j,
   103  			})
   104  		}
   105  	}
   106  
   107  	for _, tt := range tests {
   108  		t.Run(tt.name, func(t *testing.T) {
   109  			if gotOk := tt.fields.a.SameTypeAs(tt.fields.b); gotOk != tt.wantOk {
   110  				t.Errorf("SameTypeAs() = %v, want %v", gotOk, tt.wantOk)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func TestValue_Equals(t *testing.T) {
   117  	type fields struct {
   118  		a, b Value
   119  	}
   120  	type test struct {
   121  		name   string
   122  		fields fields
   123  		wantOk bool
   124  	}
   125  	var tests []test
   126  	for i := range valueBuilders {
   127  		a := valueBuilders[i]()
   128  		for j := range valueBuilders {
   129  			b := valueBuilders[j]()
   130  			tests = append(tests, test{
   131  				name: fmt.Sprint(a.GetTypeString(), "==", b.GetTypeString()),
   132  				fields: fields{
   133  					a: *a, b: *b,
   134  				},
   135  				wantOk: i == j,
   136  			})
   137  		}
   138  	}
   139  	for i := range valueBuilders {
   140  		a := valueBuilders[i]()
   141  		for j := range altValueBuilders {
   142  			b := altValueBuilders[j]()
   143  			tests = append(tests, test{
   144  				name: fmt.Sprint(a.GetTypeString(), "!=", b.GetTypeString()),
   145  				fields: fields{
   146  					a: *a, b: *b,
   147  				},
   148  				// Only Null == Null
   149  				wantOk: a.IsNull() && b.IsNull() && i == 0 && j == 0,
   150  			})
   151  		}
   152  	}
   153  	for _, tt := range tests {
   154  		t.Run(tt.name, func(t *testing.T) {
   155  			if gotOk := tt.fields.a.Equals(tt.fields.b); gotOk != tt.wantOk {
   156  				t.Errorf("Equals() = %v, want %v", gotOk, tt.wantOk)
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  func TestValue_CSVString(t *testing.T) {
   163  	type test struct {
   164  		name    string
   165  		want    string
   166  		wantAlt string
   167  	}
   168  
   169  	tests := []test{
   170  		{
   171  			name:    valueBuilders[0]().String(),
   172  			want:    "",
   173  			wantAlt: "",
   174  		},
   175  		{
   176  			name:    valueBuilders[1]().String(),
   177  			want:    "true",
   178  			wantAlt: "false",
   179  		},
   180  		{
   181  			name:    valueBuilders[2]().String(),
   182  			want:    "byte contents",
   183  			wantAlt: "",
   184  		},
   185  		{
   186  			name:    valueBuilders[3]().String(),
   187  			want:    "3.141592653589793",
   188  			wantAlt: "0",
   189  		},
   190  		{
   191  			name:    valueBuilders[4]().String(),
   192  			want:    "4919",
   193  			wantAlt: "0",
   194  		},
   195  		{
   196  			name:    valueBuilders[5]().String(),
   197  			want:    "2006-01-02T15:04:05Z",
   198  			wantAlt: "0001T",
   199  		},
   200  		{
   201  			name:    valueBuilders[6]().String(),
   202  			want:    "string contents",
   203  			wantAlt: "",
   204  		},
   205  	}
   206  
   207  	for i, tt := range tests {
   208  		t.Run(tt.name, func(t *testing.T) {
   209  			v := valueBuilders[i]()
   210  			vAlt := altValueBuilders[i]()
   211  			if got := v.CSVString(); got != tt.want {
   212  				t.Errorf("CSVString() = %v, want %v", got, tt.want)
   213  			}
   214  			if got := vAlt.CSVString(); got != tt.wantAlt {
   215  				t.Errorf("CSVString() = %v, want %v", got, tt.wantAlt)
   216  			}
   217  		})
   218  	}
   219  }
   220  
   221  func TestValue_bytesToInt(t *testing.T) {
   222  	type fields struct {
   223  		value interface{}
   224  	}
   225  	tests := []struct {
   226  		name   string
   227  		fields fields
   228  		want   int64
   229  		wantOK bool
   230  	}{
   231  		{
   232  			name: "zero",
   233  			fields: fields{
   234  				value: []byte("0"),
   235  			},
   236  			want:   0,
   237  			wantOK: true,
   238  		},
   239  		{
   240  			name: "minuszero",
   241  			fields: fields{
   242  				value: []byte("-0"),
   243  			},
   244  			want:   0,
   245  			wantOK: true,
   246  		},
   247  		{
   248  			name: "one",
   249  			fields: fields{
   250  				value: []byte("1"),
   251  			},
   252  			want:   1,
   253  			wantOK: true,
   254  		},
   255  		{
   256  			name: "minusone",
   257  			fields: fields{
   258  				value: []byte("-1"),
   259  			},
   260  			want:   -1,
   261  			wantOK: true,
   262  		},
   263  		{
   264  			name: "plusone",
   265  			fields: fields{
   266  				value: []byte("+1"),
   267  			},
   268  			want:   1,
   269  			wantOK: true,
   270  		},
   271  		{
   272  			name: "max",
   273  			fields: fields{
   274  				value: []byte(strconv.FormatInt(math.MaxInt64, 10)),
   275  			},
   276  			want:   math.MaxInt64,
   277  			wantOK: true,
   278  		},
   279  		{
   280  			name: "min",
   281  			fields: fields{
   282  				value: []byte(strconv.FormatInt(math.MinInt64, 10)),
   283  			},
   284  			want:   math.MinInt64,
   285  			wantOK: true,
   286  		},
   287  		{
   288  			name: "max-overflow",
   289  			fields: fields{
   290  				value: []byte("9223372036854775808"),
   291  			},
   292  			// Seems to be what strconv.ParseInt returns
   293  			want:   math.MaxInt64,
   294  			wantOK: false,
   295  		},
   296  		{
   297  			name: "min-underflow",
   298  			fields: fields{
   299  				value: []byte("-9223372036854775809"),
   300  			},
   301  			// Seems to be what strconv.ParseInt returns
   302  			want:   math.MinInt64,
   303  			wantOK: false,
   304  		},
   305  		{
   306  			name: "zerospace",
   307  			fields: fields{
   308  				value: []byte(" 0"),
   309  			},
   310  			want:   0,
   311  			wantOK: true,
   312  		},
   313  		{
   314  			name: "onespace",
   315  			fields: fields{
   316  				value: []byte("1 "),
   317  			},
   318  			want:   1,
   319  			wantOK: true,
   320  		},
   321  		{
   322  			name: "minusonespace",
   323  			fields: fields{
   324  				value: []byte(" -1 "),
   325  			},
   326  			want:   -1,
   327  			wantOK: true,
   328  		},
   329  		{
   330  			name: "plusonespace",
   331  			fields: fields{
   332  				value: []byte("\t+1\t"),
   333  			},
   334  			want:   1,
   335  			wantOK: true,
   336  		},
   337  		{
   338  			name: "scientific",
   339  			fields: fields{
   340  				value: []byte("3e5"),
   341  			},
   342  			want:   0,
   343  			wantOK: false,
   344  		},
   345  		{
   346  			// No support for prefixes
   347  			name: "hex",
   348  			fields: fields{
   349  				value: []byte("0xff"),
   350  			},
   351  			want:   0,
   352  			wantOK: false,
   353  		},
   354  	}
   355  	for _, tt := range tests {
   356  		t.Run(tt.name, func(t *testing.T) {
   357  			v := &Value{
   358  				value: tt.fields.value,
   359  			}
   360  			got, got1 := v.bytesToInt()
   361  			if got != tt.want {
   362  				t.Errorf("bytesToInt() got = %v, want %v", got, tt.want)
   363  			}
   364  			if got1 != tt.wantOK {
   365  				t.Errorf("bytesToInt() got1 = %v, want %v", got1, tt.wantOK)
   366  			}
   367  		})
   368  	}
   369  }
   370  
   371  func TestValue_bytesToFloat(t *testing.T) {
   372  	type fields struct {
   373  		value interface{}
   374  	}
   375  	tests := []struct {
   376  		name   string
   377  		fields fields
   378  		want   float64
   379  		wantOK bool
   380  	}{
   381  		// Copied from TestValue_bytesToInt.
   382  		{
   383  			name: "zero",
   384  			fields: fields{
   385  				value: []byte("0"),
   386  			},
   387  			want:   0,
   388  			wantOK: true,
   389  		},
   390  		{
   391  			name: "minuszero",
   392  			fields: fields{
   393  				value: []byte("-0"),
   394  			},
   395  			want:   0,
   396  			wantOK: true,
   397  		},
   398  		{
   399  			name: "one",
   400  			fields: fields{
   401  				value: []byte("1"),
   402  			},
   403  			want:   1,
   404  			wantOK: true,
   405  		},
   406  		{
   407  			name: "minusone",
   408  			fields: fields{
   409  				value: []byte("-1"),
   410  			},
   411  			want:   -1,
   412  			wantOK: true,
   413  		},
   414  		{
   415  			name: "plusone",
   416  			fields: fields{
   417  				value: []byte("+1"),
   418  			},
   419  			want:   1,
   420  			wantOK: true,
   421  		},
   422  		{
   423  			name: "maxint",
   424  			fields: fields{
   425  				value: []byte(strconv.FormatInt(math.MaxInt64, 10)),
   426  			},
   427  			want:   math.MaxInt64,
   428  			wantOK: true,
   429  		},
   430  		{
   431  			name: "minint",
   432  			fields: fields{
   433  				value: []byte(strconv.FormatInt(math.MinInt64, 10)),
   434  			},
   435  			want:   math.MinInt64,
   436  			wantOK: true,
   437  		},
   438  		{
   439  			name: "max-overflow-int",
   440  			fields: fields{
   441  				value: []byte("9223372036854775808"),
   442  			},
   443  			// Seems to be what strconv.ParseInt returns
   444  			want:   math.MaxInt64,
   445  			wantOK: true,
   446  		},
   447  		{
   448  			name: "min-underflow-int",
   449  			fields: fields{
   450  				value: []byte("-9223372036854775809"),
   451  			},
   452  			// Seems to be what strconv.ParseInt returns
   453  			want:   math.MinInt64,
   454  			wantOK: true,
   455  		},
   456  		{
   457  			name: "max",
   458  			fields: fields{
   459  				value: []byte(strconv.FormatFloat(math.MaxFloat64, 'g', -1, 64)),
   460  			},
   461  			want:   math.MaxFloat64,
   462  			wantOK: true,
   463  		},
   464  		{
   465  			name: "min",
   466  			fields: fields{
   467  				value: []byte(strconv.FormatFloat(-math.MaxFloat64, 'g', -1, 64)),
   468  			},
   469  			want:   -math.MaxFloat64,
   470  			wantOK: true,
   471  		},
   472  		{
   473  			name: "max-overflow",
   474  			fields: fields{
   475  				value: []byte("1.797693134862315708145274237317043567981e+309"),
   476  			},
   477  			// Seems to be what strconv.ParseInt returns
   478  			want:   math.Inf(1),
   479  			wantOK: false,
   480  		},
   481  		{
   482  			name: "min-underflow",
   483  			fields: fields{
   484  				value: []byte("-1.797693134862315708145274237317043567981e+309"),
   485  			},
   486  			// Seems to be what strconv.ParseInt returns
   487  			want:   math.Inf(-1),
   488  			wantOK: false,
   489  		},
   490  		{
   491  			name: "smallest-pos",
   492  			fields: fields{
   493  				value: []byte(strconv.FormatFloat(math.SmallestNonzeroFloat64, 'g', -1, 64)),
   494  			},
   495  			want:   math.SmallestNonzeroFloat64,
   496  			wantOK: true,
   497  		},
   498  		{
   499  			name: "smallest-pos",
   500  			fields: fields{
   501  				value: []byte(strconv.FormatFloat(-math.SmallestNonzeroFloat64, 'g', -1, 64)),
   502  			},
   503  			want:   -math.SmallestNonzeroFloat64,
   504  			wantOK: true,
   505  		},
   506  		{
   507  			name: "zerospace",
   508  			fields: fields{
   509  				value: []byte(" 0"),
   510  			},
   511  			want:   0,
   512  			wantOK: true,
   513  		},
   514  		{
   515  			name: "onespace",
   516  			fields: fields{
   517  				value: []byte("1 "),
   518  			},
   519  			want:   1,
   520  			wantOK: true,
   521  		},
   522  		{
   523  			name: "minusonespace",
   524  			fields: fields{
   525  				value: []byte(" -1 "),
   526  			},
   527  			want:   -1,
   528  			wantOK: true,
   529  		},
   530  		{
   531  			name: "plusonespace",
   532  			fields: fields{
   533  				value: []byte("\t+1\t"),
   534  			},
   535  			want:   1,
   536  			wantOK: true,
   537  		},
   538  		{
   539  			name: "scientific",
   540  			fields: fields{
   541  				value: []byte("3e5"),
   542  			},
   543  			want:   300000,
   544  			wantOK: true,
   545  		},
   546  		{
   547  			// No support for prefixes
   548  			name: "hex",
   549  			fields: fields{
   550  				value: []byte("0xff"),
   551  			},
   552  			want:   0,
   553  			wantOK: false,
   554  		},
   555  	}
   556  	for _, tt := range tests {
   557  		t.Run(tt.name, func(t *testing.T) {
   558  			v := Value{
   559  				value: tt.fields.value,
   560  			}
   561  			got, got1 := v.bytesToFloat()
   562  			diff := math.Abs(got - tt.want)
   563  			if diff > floatCmpTolerance {
   564  				t.Errorf("bytesToFloat() got = %v, want %v", got, tt.want)
   565  			}
   566  			if got1 != tt.wantOK {
   567  				t.Errorf("bytesToFloat() got1 = %v, want %v", got1, tt.wantOK)
   568  			}
   569  		})
   570  	}
   571  }
   572  
   573  func TestValue_bytesToBool(t *testing.T) {
   574  	type fields struct {
   575  		value interface{}
   576  	}
   577  	tests := []struct {
   578  		name    string
   579  		fields  fields
   580  		wantVal bool
   581  		wantOk  bool
   582  	}{
   583  		{
   584  			name: "true",
   585  			fields: fields{
   586  				value: []byte("true"),
   587  			},
   588  			wantVal: true,
   589  			wantOk:  true,
   590  		},
   591  		{
   592  			name: "false",
   593  			fields: fields{
   594  				value: []byte("false"),
   595  			},
   596  			wantVal: false,
   597  			wantOk:  true,
   598  		},
   599  		{
   600  			name: "t",
   601  			fields: fields{
   602  				value: []byte("t"),
   603  			},
   604  			wantVal: true,
   605  			wantOk:  true,
   606  		},
   607  		{
   608  			name: "f",
   609  			fields: fields{
   610  				value: []byte("f"),
   611  			},
   612  			wantVal: false,
   613  			wantOk:  true,
   614  		},
   615  		{
   616  			name: "1",
   617  			fields: fields{
   618  				value: []byte("1"),
   619  			},
   620  			wantVal: true,
   621  			wantOk:  true,
   622  		},
   623  		{
   624  			name: "0",
   625  			fields: fields{
   626  				value: []byte("0"),
   627  			},
   628  			wantVal: false,
   629  			wantOk:  true,
   630  		},
   631  		{
   632  			name: "truespace",
   633  			fields: fields{
   634  				value: []byte(" true "),
   635  			},
   636  			wantVal: true,
   637  			wantOk:  true,
   638  		},
   639  		{
   640  			name: "truetabs",
   641  			fields: fields{
   642  				value: []byte("\ttrue\t"),
   643  			},
   644  			wantVal: true,
   645  			wantOk:  true,
   646  		},
   647  		{
   648  			name: "TRUE",
   649  			fields: fields{
   650  				value: []byte("TRUE"),
   651  			},
   652  			wantVal: true,
   653  			wantOk:  true,
   654  		},
   655  		{
   656  			name: "FALSE",
   657  			fields: fields{
   658  				value: []byte("FALSE"),
   659  			},
   660  			wantVal: false,
   661  			wantOk:  true,
   662  		},
   663  		{
   664  			name: "invalid",
   665  			fields: fields{
   666  				value: []byte("no"),
   667  			},
   668  			wantVal: false,
   669  			wantOk:  false,
   670  		},
   671  	}
   672  	for _, tt := range tests {
   673  		t.Run(tt.name, func(t *testing.T) {
   674  			v := Value{
   675  				value: tt.fields.value,
   676  			}
   677  			gotVal, gotOk := v.bytesToBool()
   678  			if gotVal != tt.wantVal {
   679  				t.Errorf("bytesToBool() gotVal = %v, want %v", gotVal, tt.wantVal)
   680  			}
   681  			if gotOk != tt.wantOk {
   682  				t.Errorf("bytesToBool() gotOk = %v, want %v", gotOk, tt.wantOk)
   683  			}
   684  		})
   685  	}
   686  }