github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/roundtrip_format_test.go (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sqlbase
    12  
    13  import (
    14  	"fmt"
    15  	"math"
    16  	"strconv"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    24  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    25  )
    26  
    27  // TestParseDatumStringAs tests that datums are roundtrippable between
    28  // printing with FmtExport and ParseDatumStringAs, but with random datums.
    29  // This test lives in sqlbase to avoid dependency cycles when trying to move
    30  // RandDatumWithNullChance into tree.
    31  func TestRandParseDatumStringAs(t *testing.T) {
    32  	defer leaktest.AfterTest(t)()
    33  	tests := append([]*types.T{
    34  		types.MakeTimestamp(0),
    35  		types.MakeTimestamp(3),
    36  		types.MakeTimestamp(6),
    37  		types.MakeTimestampTZ(0),
    38  		types.MakeTimestampTZ(3),
    39  		types.MakeTimestampTZ(6),
    40  		types.MakeTime(0),
    41  		types.MakeTime(3),
    42  		types.MakeTime(6),
    43  		types.MakeTimeTZ(0),
    44  		types.MakeTimeTZ(3),
    45  		types.MakeTimeTZ(6),
    46  		types.MakeCollatedString(types.String, "en"),
    47  		types.MakeCollatedString(types.String, "de"),
    48  	},
    49  		types.Scalar...)
    50  	for _, ty := range types.Scalar {
    51  		if ty != types.Jsonb {
    52  			tests = append(tests, types.MakeArray(ty))
    53  		}
    54  	}
    55  	evalCtx := tree.NewTestingEvalContext(nil)
    56  	rng, _ := randutil.NewPseudoRand()
    57  	for _, typ := range tests {
    58  		const testsForTyp = 100
    59  		t.Run(typ.String(), func(t *testing.T) {
    60  			for i := 0; i < testsForTyp; i++ {
    61  				datum := RandDatumWithNullChance(rng, typ, 0)
    62  				ds := tree.AsStringWithFlags(datum, tree.FmtExport)
    63  
    64  				// Because of how RandDatumWithNullChanceWorks, we might
    65  				// get an interesting datum for a time related type that
    66  				// doesn't have the precision that we requested. In these
    67  				// cases, manually correct the type ourselves.
    68  				var err error
    69  				switch d := datum.(type) {
    70  				case *tree.DTimestampTZ:
    71  					roundTo := tree.TimeFamilyPrecisionToRoundDuration(typ.Precision())
    72  					// We can't round the max time, as it exceeds bounds.
    73  					if roundTo > time.Microsecond && d.Time.Round(roundTo).Equal(tree.MaxSupportedTime.Round(roundTo)) {
    74  						continue
    75  					}
    76  					datum, err = d.Round(roundTo)
    77  				case *tree.DTimestamp:
    78  					roundTo := tree.TimeFamilyPrecisionToRoundDuration(typ.Precision())
    79  					// We can't round the max time, as it exceeds bounds.
    80  					if roundTo > time.Microsecond && d.Time.Round(roundTo).Equal(tree.MaxSupportedTime.Round(roundTo)) {
    81  						continue
    82  					}
    83  					datum, err = d.Round(roundTo)
    84  				case *tree.DTime:
    85  					datum = d.Round(tree.TimeFamilyPrecisionToRoundDuration(typ.Precision()))
    86  				case *tree.DTimeTZ:
    87  					datum = d.Round(tree.TimeFamilyPrecisionToRoundDuration(typ.Precision()))
    88  				}
    89  
    90  				if err != nil {
    91  					t.Fatal(ds, err)
    92  				}
    93  
    94  				parsed, err := ParseDatumStringAs(typ, ds, evalCtx)
    95  				if err != nil {
    96  					t.Fatal(ds, err)
    97  				}
    98  				if parsed.Compare(evalCtx, datum) != 0 {
    99  					t.Fatal(ds, "expected", datum, "found", parsed)
   100  				}
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  // TestParseDatumStringAs tests that datums are roundtrippable between
   107  // printing with FmtExport and ParseDatumStringAs.
   108  func TestParseDatumStringAs(t *testing.T) {
   109  	defer leaktest.AfterTest(t)()
   110  	tests := map[*types.T][]string{
   111  		types.Bool: {
   112  			"true",
   113  			"false",
   114  		},
   115  		types.Bytes: {
   116  			`\x`,
   117  			`\x00`,
   118  			`\xff`,
   119  			`\xffff`,
   120  			fmt.Sprintf(`\x%x`, "abc"),
   121  		},
   122  		types.Date: {
   123  			"2001-01-01",
   124  		},
   125  		types.Decimal: {
   126  			"0.0",
   127  			"1.0",
   128  			"-1.0",
   129  			strconv.FormatFloat(math.MaxFloat64, 'G', -1, 64),
   130  			strconv.FormatFloat(math.SmallestNonzeroFloat64, 'G', -1, 64),
   131  			strconv.FormatFloat(-math.MaxFloat64, 'G', -1, 64),
   132  			strconv.FormatFloat(-math.SmallestNonzeroFloat64, 'G', -1, 64),
   133  			"1E+1000",
   134  			"1E-1000",
   135  			"Infinity",
   136  			"-Infinity",
   137  			"NaN",
   138  		},
   139  		types.IntArray: {
   140  			"ARRAY[]",
   141  			"ARRAY[1, 2]",
   142  		},
   143  		types.StringArray: {
   144  			`ARRAY[NULL, 'NULL']`,
   145  			`ARRAY['hello', 'there']`,
   146  			`ARRAY['hel,lo']`,
   147  		},
   148  		types.Float: {
   149  			"0.0",
   150  			"-0.0",
   151  			"1.0",
   152  			"-1.0",
   153  			strconv.FormatFloat(math.MaxFloat64, 'g', -1, 64),
   154  			strconv.FormatFloat(math.SmallestNonzeroFloat64, 'g', -1, 64),
   155  			strconv.FormatFloat(-math.MaxFloat64, 'g', -1, 64),
   156  			strconv.FormatFloat(-math.SmallestNonzeroFloat64, 'g', -1, 64),
   157  			"+Inf",
   158  			"-Inf",
   159  			"NaN",
   160  		},
   161  		types.INet: {
   162  			"127.0.0.1",
   163  		},
   164  		types.Int: {
   165  			"1",
   166  			"0",
   167  			"-1",
   168  			strconv.Itoa(math.MaxInt64),
   169  			strconv.Itoa(math.MinInt64),
   170  		},
   171  		types.Interval: {
   172  			"01:00:00",
   173  			"-00:01:00",
   174  			"2 years 3 mons",
   175  		},
   176  		types.MakeInterval(types.IntervalTypeMetadata{}): {
   177  			"01:02:03",
   178  			"02:03:04",
   179  			"-00:01:00",
   180  			"2 years 3 mons",
   181  		},
   182  		types.MakeInterval(types.IntervalTypeMetadata{Precision: 3, PrecisionIsSet: true}): {
   183  			"01:02:03",
   184  			"02:03:04.123",
   185  		},
   186  		types.MakeInterval(types.IntervalTypeMetadata{Precision: 6, PrecisionIsSet: true}): {
   187  			"01:02:03",
   188  			"02:03:04.123456",
   189  		},
   190  		types.Jsonb: {
   191  			"{}",
   192  			"[]",
   193  			"null",
   194  			"1",
   195  			"1.0",
   196  			`""`,
   197  			`"abc"`,
   198  			`"ab\u0000c"`,
   199  			`"ab\u0001c"`,
   200  			`"ab⚣ cd"`,
   201  		},
   202  		types.String: {
   203  			"",
   204  			"abc",
   205  			"abc\x00",
   206  			"ab⚣ cd",
   207  		},
   208  		types.Geography: {
   209  			"0101000020E6100000000000000000F03F000000000000F03F",
   210  		},
   211  		types.Geometry: {
   212  			"0101000000000000000000F03F000000000000F03F",
   213  		},
   214  		types.Timestamp: {
   215  			"2001-01-01 01:02:03+00:00",
   216  			"2001-01-01 02:03:04.123456+00:00",
   217  		},
   218  		types.MakeTimestamp(0): {
   219  			"2001-01-01 01:02:03+00:00",
   220  			"2001-01-01 02:03:04+00:00",
   221  		},
   222  		types.MakeTimestamp(3): {
   223  			"2001-01-01 01:02:03+00:00",
   224  			"2001-01-01 02:03:04.123+00:00",
   225  		},
   226  		types.MakeTimestamp(6): {
   227  			"2001-01-01 01:02:03+00:00",
   228  			"2001-01-01 02:03:04.123456+00:00",
   229  		},
   230  		types.TimestampTZ: {
   231  			"2001-01-01 01:02:03+00:00",
   232  			"2001-01-01 02:03:04.123456+00:00",
   233  		},
   234  		types.MakeTimestampTZ(0): {
   235  			"2001-01-01 01:02:03+00:00",
   236  			"2001-01-01 02:03:04+00:00",
   237  		},
   238  		types.MakeTimestampTZ(3): {
   239  			"2001-01-01 01:02:03+00:00",
   240  			"2001-01-01 02:03:04.123+00:00",
   241  		},
   242  		types.MakeTimestampTZ(6): {
   243  			"2001-01-01 01:02:03+00:00",
   244  			"2001-01-01 02:03:04.123456+00:00",
   245  		},
   246  		types.Time: {
   247  			"01:02:03",
   248  			"02:03:04.123456",
   249  		},
   250  		types.MakeTime(0): {
   251  			"01:02:03",
   252  			"02:03:04",
   253  		},
   254  		types.MakeTime(3): {
   255  			"01:02:03",
   256  			"02:03:04.123",
   257  		},
   258  		types.MakeTime(6): {
   259  			"01:02:03",
   260  			"02:03:04.123456",
   261  		},
   262  		types.TimeTZ: {
   263  			"01:02:03+00:00:00",
   264  			"01:02:03+11:00:00",
   265  			"01:02:03+11:00:00",
   266  			"01:02:03-11:00:00",
   267  			"02:03:04.123456+11:00:00",
   268  		},
   269  		types.MakeTimeTZ(0): {
   270  			"01:02:03+03:30:00",
   271  		},
   272  		types.MakeTimeTZ(3): {
   273  			"01:02:03+03:30:00",
   274  			"02:03:04.123+03:30:00",
   275  		},
   276  		types.MakeTimeTZ(6): {
   277  			"01:02:03+03:30:00",
   278  			"02:03:04.123456+03:30:00",
   279  		},
   280  		types.Uuid: {
   281  			uuid.MakeV4().String(),
   282  		},
   283  	}
   284  	evalCtx := tree.NewTestingEvalContext(nil)
   285  	for typ, exprs := range tests {
   286  		t.Run(typ.String(), func(t *testing.T) {
   287  			for _, s := range exprs {
   288  				t.Run(fmt.Sprintf("%q", s), func(t *testing.T) {
   289  					d, err := ParseDatumStringAs(typ, s, evalCtx)
   290  					if err != nil {
   291  						t.Fatal(err)
   292  					}
   293  					if d.ResolvedType().Family() != typ.Family() {
   294  						t.Fatalf("unexpected type: %s", d.ResolvedType())
   295  					}
   296  					ds := tree.AsStringWithFlags(d, tree.FmtExport)
   297  					parsed, err := ParseDatumStringAs(typ, ds, evalCtx)
   298  					if err != nil {
   299  						t.Fatal(err)
   300  					}
   301  					if parsed.Compare(evalCtx, d) != 0 {
   302  						t.Fatal("expected", d, "found", parsed)
   303  					}
   304  				})
   305  			}
   306  		})
   307  	}
   308  }