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

     1  // Copyright 2016 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 tests_test
    12  
    13  import (
    14  	"context"
    15  	gosql "database/sql"
    16  	"flag"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"math/rand"
    20  	"path/filepath"
    21  	"regexp"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	// Enable CCL statements.
    28  	_ "github.com/cockroachdb/cockroach/pkg/ccl"
    29  	"github.com/cockroachdb/cockroach/pkg/ccl/utilccl"
    30  	"github.com/cockroachdb/cockroach/pkg/internal/rsg"
    31  	"github.com/cockroachdb/cockroach/pkg/internal/sqlsmith"
    32  	"github.com/cockroachdb/cockroach/pkg/sql"
    33  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    34  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    35  	"github.com/cockroachdb/cockroach/pkg/sql/sem/builtins"
    36  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    37  	"github.com/cockroachdb/cockroach/pkg/sql/tests"
    38  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    39  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    40  	"github.com/cockroachdb/cockroach/pkg/util/ctxgroup"
    41  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    42  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    43  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    44  	"github.com/cockroachdb/errors"
    45  	"github.com/lib/pq"
    46  )
    47  
    48  var (
    49  	flagRSGTime        = flag.Duration("rsg", 0, "random syntax generator test duration")
    50  	flagRSGGoRoutines  = flag.Int("rsg-routines", 1, "number of Go routines executing random statements in each RSG test")
    51  	flagRSGExecTimeout = flag.Duration("rsg-exec-timeout", 15*time.Second, "timeout duration when executing a statement")
    52  )
    53  
    54  func verifyFormat(sql string) error {
    55  	stmts, err := parser.Parse(sql)
    56  	if err != nil {
    57  		// Cannot serialize a statement list without parsing it.
    58  		return nil //nolint:returnerrcheck
    59  	}
    60  	formattedSQL := stmts.StringWithFlags(tree.FmtShowPasswords)
    61  	formattedStmts, err := parser.Parse(formattedSQL)
    62  	if err != nil {
    63  		return errors.Wrapf(err, "cannot parse output of Format: sql=%q, formattedSQL=%q", sql, formattedSQL)
    64  	}
    65  	formattedFormattedSQL := formattedStmts.StringWithFlags(tree.FmtShowPasswords)
    66  	if formattedSQL != formattedFormattedSQL {
    67  		return errors.Errorf("Parse followed by Format is not idempotent: %q -> %q != %q", sql, formattedSQL, formattedFormattedSQL)
    68  	}
    69  	// TODO(eisen): ensure that the reconstituted SQL not only parses but also has
    70  	// the same meaning as the original.
    71  	return nil
    72  }
    73  
    74  type verifyFormatDB struct {
    75  	db              *gosql.DB
    76  	verifyFormatErr error
    77  	mu              struct {
    78  		syncutil.Mutex
    79  		// active holds the currently executing statements.
    80  		active map[string]int
    81  	}
    82  }
    83  
    84  // Incr records sql in the active map and returns a func to decrement it.
    85  func (db *verifyFormatDB) Incr(sql string) func() {
    86  	db.mu.Lock()
    87  	if db.mu.active == nil {
    88  		db.mu.active = make(map[string]int)
    89  	}
    90  	db.mu.active[sql]++
    91  	db.mu.Unlock()
    92  
    93  	return func() {
    94  		db.mu.Lock()
    95  		db.mu.active[sql]--
    96  		if db.mu.active[sql] == 0 {
    97  			delete(db.mu.active, sql)
    98  		}
    99  		db.mu.Unlock()
   100  	}
   101  }
   102  
   103  type crasher struct {
   104  	sql    string
   105  	err    error
   106  	detail string
   107  }
   108  
   109  func (c *crasher) Error() string {
   110  	return fmt.Sprintf("server panic: %s", c.err)
   111  }
   112  
   113  type nonCrasher struct {
   114  	sql string
   115  	err error
   116  }
   117  
   118  func (c *nonCrasher) Error() string {
   119  	return c.err.Error()
   120  }
   121  
   122  func (db *verifyFormatDB) exec(ctx context.Context, sql string) error {
   123  	if err := verifyFormat(sql); err != nil {
   124  		db.verifyFormatErr = err
   125  		return err
   126  	}
   127  
   128  	defer db.Incr(sql)()
   129  
   130  	funcdone := make(chan error, 1)
   131  	go func() {
   132  		_, err := db.db.ExecContext(ctx, sql)
   133  		funcdone <- err
   134  	}()
   135  	select {
   136  	case err := <-funcdone:
   137  		if err != nil {
   138  			if pqerr := (*pq.Error)(nil); errors.As(err, &pqerr) {
   139  				// Output Postgres error code if it's available.
   140  				if pqerr.Code == pgcode.CrashShutdown {
   141  					return &crasher{
   142  						sql:    sql,
   143  						err:    err,
   144  						detail: pqerr.Detail,
   145  					}
   146  				}
   147  			}
   148  			if es := err.Error(); strings.Contains(es, "internal error") ||
   149  				strings.Contains(es, "driver: bad connection") ||
   150  				strings.Contains(es, "unexpected error inside CockroachDB") {
   151  				return &crasher{
   152  					sql: sql,
   153  					err: err,
   154  				}
   155  			}
   156  			return &nonCrasher{sql: sql, err: err}
   157  		}
   158  		return nil
   159  	case <-time.After(*flagRSGExecTimeout):
   160  		db.mu.Lock()
   161  		defer db.mu.Unlock()
   162  		b := make([]byte, 1024*1024)
   163  		n := runtime.Stack(b, true)
   164  		fmt.Printf("%s\n", b[:n])
   165  		// Now see if we can execute a SELECT 1. This is useful because sometimes an
   166  		// exec timeout is because of a slow-executing statement, and other times
   167  		// it's because the server is completely wedged. This is an automated way
   168  		// to find out.
   169  		errch := make(chan error, 1)
   170  		go func() {
   171  			rows, err := db.db.Query(`SELECT 1`)
   172  			if err == nil {
   173  				rows.Close()
   174  			}
   175  			errch <- err
   176  		}()
   177  		select {
   178  		case <-time.After(5 * time.Second):
   179  			fmt.Println("SELECT 1 timeout: probably a wedged server")
   180  		case err := <-errch:
   181  			if err != nil {
   182  				fmt.Println("SELECT 1 execute error:", err)
   183  			} else {
   184  				fmt.Println("SELECT 1 executed successfully: probably a slow statement")
   185  			}
   186  		}
   187  		fmt.Printf("timeout: %q. currently executing: %v\n", sql, db.mu.active)
   188  		panic("statement exec timeout")
   189  	}
   190  }
   191  
   192  func TestRandomSyntaxGeneration(t *testing.T) {
   193  	defer leaktest.AfterTest(t)()
   194  
   195  	const rootStmt = "stmt"
   196  
   197  	testRandomSyntax(t, false, "ident", nil, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   198  		s := r.Generate(rootStmt, 20)
   199  		// Don't start transactions since closing them is tricky. Just issuing a
   200  		// ROLLBACK after all queries doesn't work due to the parellel uses of db,
   201  		// which can start another immediately after the ROLLBACK and cause problems
   202  		// for the following statement. The CREATE DATABASE below would fail with
   203  		// errors about an aborted transaction and thus panic.
   204  		if strings.HasPrefix(s, "BEGIN") || strings.HasPrefix(s, "START") {
   205  			return errors.New("transactions are unsupported")
   206  		}
   207  		if strings.HasPrefix(s, "SET SESSION CHARACTERISTICS AS TRANSACTION") {
   208  			return errors.New("setting session characteristics is unsupported")
   209  		}
   210  		if strings.Contains(s, "READ ONLY") || strings.Contains(s, "read_only") {
   211  			return errors.New("READ ONLY settings are unsupported")
   212  		}
   213  		if strings.Contains(s, "REVOKE") || strings.Contains(s, "GRANT") {
   214  			return errors.New("REVOKE and GRANT are unsupported")
   215  		}
   216  		if strings.Contains(s, "EXPERIMENTAL SCRUB DATABASE SYSTEM") {
   217  			return errors.New("See #43693")
   218  		}
   219  		// Recreate the database on every run in case it was dropped or renamed in
   220  		// a previous run. Should always succeed.
   221  		if err := db.exec(ctx, `CREATE DATABASE IF NOT EXISTS ident`); err != nil {
   222  			return err
   223  		}
   224  		return db.exec(ctx, s)
   225  	})
   226  }
   227  
   228  func TestRandomSyntaxSelect(t *testing.T) {
   229  	defer leaktest.AfterTest(t)()
   230  
   231  	const rootStmt = "target_list"
   232  
   233  	testRandomSyntax(t, false, "ident", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   234  		return db.exec(ctx, `CREATE DATABASE IF NOT EXISTS ident; CREATE TABLE IF NOT EXISTS ident.ident (ident decimal);`)
   235  	}, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   236  		targets := r.Generate(rootStmt, 300)
   237  		var where, from string
   238  		// Only generate complex clauses half the time.
   239  		if rand.Intn(2) == 0 {
   240  			where = r.Generate("where_clause", 300)
   241  			from = r.Generate("from_clause", 300)
   242  		} else {
   243  			from = "FROM ident"
   244  		}
   245  		s := fmt.Sprintf("SELECT %s %s %s", targets, from, where)
   246  		return db.exec(ctx, s)
   247  	})
   248  }
   249  
   250  type namedBuiltin struct {
   251  	name    string
   252  	builtin tree.Overload
   253  }
   254  
   255  func TestRandomSyntaxFunctions(t *testing.T) {
   256  	defer leaktest.AfterTest(t)()
   257  
   258  	done := make(chan struct{})
   259  	defer close(done)
   260  	namedBuiltinChan := make(chan namedBuiltin)
   261  	go func() {
   262  		for {
   263  			for _, name := range builtins.AllBuiltinNames {
   264  				lower := strings.ToLower(name)
   265  				if strings.HasPrefix(lower, "crdb_internal.force_") {
   266  					continue
   267  				}
   268  				switch lower {
   269  				case
   270  					"pg_sleep":
   271  					continue
   272  				}
   273  				_, variations := builtins.GetBuiltinProperties(name)
   274  				for _, builtin := range variations {
   275  					select {
   276  					case <-done:
   277  						return
   278  					case namedBuiltinChan <- namedBuiltin{name: name, builtin: builtin}:
   279  					}
   280  				}
   281  			}
   282  		}
   283  	}()
   284  
   285  	testRandomSyntax(t, false, "defaultdb", nil, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   286  		nb := <-namedBuiltinChan
   287  		var args []string
   288  		switch ft := nb.builtin.Types.(type) {
   289  		case tree.ArgTypes:
   290  			for _, arg := range ft {
   291  				args = append(args, r.GenerateRandomArg(arg.Typ))
   292  			}
   293  		case tree.HomogeneousType:
   294  			for i := r.Intn(5); i > 0; i-- {
   295  				var typ *types.T
   296  				switch r.Intn(4) {
   297  				case 0:
   298  					typ = types.String
   299  				case 1:
   300  					typ = types.Float
   301  				case 2:
   302  					typ = types.Bool
   303  				case 3:
   304  					typ = types.TimestampTZ
   305  				}
   306  				args = append(args, r.GenerateRandomArg(typ))
   307  			}
   308  		case tree.VariadicType:
   309  			for _, t := range ft.FixedTypes {
   310  				args = append(args, r.GenerateRandomArg(t))
   311  			}
   312  			for i := r.Intn(5); i > 0; i-- {
   313  				args = append(args, r.GenerateRandomArg(ft.VarType))
   314  			}
   315  		default:
   316  			panic(fmt.Sprintf("unknown fn.Types: %T", ft))
   317  		}
   318  		var limit string
   319  		switch strings.ToLower(nb.name) {
   320  		case "generate_series":
   321  			limit = " LIMIT 100"
   322  		}
   323  		s := fmt.Sprintf("SELECT %s(%s) %s", nb.name, strings.Join(args, ", "), limit)
   324  		return db.exec(ctx, s)
   325  	})
   326  }
   327  
   328  func TestRandomSyntaxFuncCommon(t *testing.T) {
   329  	defer leaktest.AfterTest(t)()
   330  
   331  	const rootStmt = "func_expr_common_subexpr"
   332  
   333  	testRandomSyntax(t, false, "defaultdb", nil, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   334  		expr := r.Generate(rootStmt, 30)
   335  		s := fmt.Sprintf("SELECT %s", expr)
   336  		return db.exec(ctx, s)
   337  	})
   338  }
   339  
   340  func TestRandomSyntaxSchemaChangeDatabase(t *testing.T) {
   341  	defer leaktest.AfterTest(t)()
   342  
   343  	roots := []string{
   344  		"create_database_stmt",
   345  		"drop_database_stmt",
   346  		"alter_rename_database_stmt",
   347  		"create_user_stmt",
   348  		"drop_user_stmt",
   349  		"alter_user_stmt",
   350  	}
   351  
   352  	testRandomSyntax(t, true, "ident", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   353  		return db.exec(ctx, `
   354  			CREATE DATABASE ident;
   355  		`)
   356  	}, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   357  		n := r.Intn(len(roots))
   358  		s := r.Generate(roots[n], 30)
   359  		return db.exec(ctx, s)
   360  	})
   361  }
   362  
   363  func TestRandomSyntaxSchemaChangeColumn(t *testing.T) {
   364  	defer leaktest.AfterTest(t)()
   365  
   366  	roots := []string{
   367  		"alter_table_cmd",
   368  	}
   369  
   370  	testRandomSyntax(t, true, "ident", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   371  		return db.exec(ctx, `
   372  			CREATE DATABASE ident;
   373  			CREATE TABLE ident.ident (ident decimal);
   374  		`)
   375  	}, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   376  		n := r.Intn(len(roots))
   377  		s := fmt.Sprintf("ALTER TABLE ident.ident %s", r.Generate(roots[n], 500))
   378  		return db.exec(ctx, s)
   379  	})
   380  }
   381  
   382  var ignoredErrorPatterns = []string{
   383  	"unimplemented",
   384  	"unsupported binary operator",
   385  	"unsupported comparison operator",
   386  	"memory budget exceeded",
   387  	"generator functions are not allowed in",
   388  	"txn already encountered an error; cannot be used anymore",
   389  	"no data source matches prefix",
   390  	"index .* already contains column",
   391  	"cannot convert .* to .*",
   392  	"index .* is in used as unique constraint",
   393  	"could not decorrelate subquery",
   394  	"column reference .* is ambiguous",
   395  	"INSERT has more expressions than target columns",
   396  	"index .* is in use as unique constraint",
   397  	"frame .* offset must not be .*",
   398  	"bit string length .* does not match type",
   399  	"column reference .* not allowed in this context",
   400  	"cannot write directly to computed column",
   401  	"index .* in the middle of being added",
   402  	"could not mark job .* as succeeded",
   403  	"failed to read backup descriptor",
   404  	"AS OF SYSTEM TIME: cannot specify timestamp in the future",
   405  	"AS OF SYSTEM TIME: timestamp before 1970-01-01T00:00:00Z is invalid",
   406  	"BACKUP for requested time  needs option 'revision_history'",
   407  	"RESTORE timestamp: supplied backups do not cover requested time",
   408  
   409  	// Numeric conditions
   410  	"exponent out of range",
   411  	"result out of range",
   412  	"argument out of range",
   413  	"integer out of range",
   414  	"invalid operation",
   415  	"invalid mask",
   416  	"cannot take square root of a negative number",
   417  	"out of int64 range",
   418  	"underflow, subnormal",
   419  	"overflow",
   420  	"requested length too large",
   421  	"division by zero",
   422  	"zero modulus",
   423  	"is out of range",
   424  
   425  	// Type checking
   426  	"value type .* doesn't match type .* of column",
   427  	"incompatible value type",
   428  	"incompatible COALESCE expressions",
   429  	"error type checking constant value",
   430  	"ambiguous binary operator",
   431  	"ambiguous call",
   432  	"cannot be matched",
   433  	"unknown signature",
   434  	"cannot determine type of empty array",
   435  	"conflicting ColumnTypes",
   436  
   437  	// Data dependencies
   438  	"violates not-null constraint",
   439  	"violates unique constraint",
   440  	"column .* is referenced by the primary key",
   441  	"column .* is referenced by existing index",
   442  
   443  	// Context-specific string formats
   444  	"invalid regexp flag",
   445  	"unrecognized privilege",
   446  	"invalid escape string",
   447  	"error parsing regexp",
   448  	"could not parse .* as type bytes",
   449  	"UUID must be exactly 16 bytes long",
   450  	"unsupported timespan",
   451  	"does not exist",
   452  	"unterminated string",
   453  	"incorrect UUID length",
   454  	"the input string must not be empty",
   455  
   456  	// JSON builtins
   457  	"mismatched array dimensions",
   458  	"cannot get array length of a non-array",
   459  	"cannot get array length of a scalar",
   460  	"cannot be called on a non-array",
   461  	"cannot call json_object_keys on an array",
   462  	"cannot set path in scalar",
   463  	"cannot delete path in scalar",
   464  	"unable to encode table key: \\*tree\\.DJSON",
   465  	"path element at position .* is null",
   466  	"path element is not an integer",
   467  	"cannot delete from object using integer index",
   468  	"invalid concatenation of jsonb objects",
   469  	"null value not allowed for object key",
   470  
   471  	// Builtins that have funky preconditions
   472  	"cannot delete from scalar",
   473  	"lastval is not yet defined",
   474  	"negative substring length",
   475  	"non-positive substring length",
   476  	"bit strings of different sizes",
   477  	"inet addresses with different sizes",
   478  	"zero length IP",
   479  	"values of different sizes",
   480  	"must have even number of elements",
   481  	"cannot take logarithm of a negative number",
   482  	"input value must be",
   483  	"formats are supported for decode",
   484  	"only available in ccl",
   485  	"expect comma-separated list of filename",
   486  	"unknown constraint",
   487  	"invalid destination encoding name",
   488  	"invalid IP format",
   489  	"invalid format code",
   490  	`.*val\(\): syntax error`,
   491  	`.*val\(\): syntax error at or near`,
   492  	`.*val\(\): help token in input`,
   493  	"invalid source encoding name",
   494  	"strconv.Atoi: parsing .*: invalid syntax",
   495  	"field position .* must be greater than zero",
   496  	"cannot take logarithm of zero",
   497  	"only 'hex', 'escape', and 'base64' formats are supported for encode",
   498  	"LIKE pattern must not end with escape character",
   499  
   500  	// TODO(mjibson): fix these
   501  	"column .* must appear in the GROUP BY clause or be used in an aggregate function",
   502  	"aggregate functions are not allowed in ON",
   503  }
   504  
   505  var ignoredRegex = regexp.MustCompile(strings.Join(ignoredErrorPatterns, "|"))
   506  
   507  func TestRandomSyntaxSQLSmith(t *testing.T) {
   508  	defer leaktest.AfterTest(t)()
   509  	defer utilccl.TestingEnableEnterprise()()
   510  
   511  	var smither *sqlsmith.Smither
   512  
   513  	tableStmts := make([]string, 0)
   514  	testRandomSyntax(t, true, "defaultdb", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   515  		setups := []string{"rand-tables", "seed"}
   516  		for _, s := range setups {
   517  			randTables := sqlsmith.Setups[s](r.Rnd)
   518  			if err := db.exec(ctx, randTables); err != nil {
   519  				return err
   520  			}
   521  			tableStmts = append(tableStmts, randTables)
   522  			fmt.Printf("%s;\n", randTables)
   523  		}
   524  		var err error
   525  		smither, err = sqlsmith.NewSmither(db.db, r.Rnd, sqlsmith.DisableMutations())
   526  		return err
   527  	}, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   528  		s := smither.Generate()
   529  		err := db.exec(ctx, s)
   530  		if c := (*crasher)(nil); errors.As(err, &c) {
   531  			if err := db.exec(ctx, "USE defaultdb"); err != nil {
   532  				t.Fatalf("couldn't reconnect to db after crasher: %v", c)
   533  			}
   534  			fmt.Printf("CRASHER:\ncaused by: %s\n\nSTATEMENT:\n%s;\n\nserver stacktrace:\n%s\n\n", c.Error(), s, c.detail)
   535  			return c
   536  		}
   537  		if err == nil {
   538  			return nil
   539  		}
   540  		msg := err.Error()
   541  		shouldLogErr := true
   542  		if ignoredRegex.MatchString(msg) {
   543  			shouldLogErr = false
   544  		}
   545  		if testing.Verbose() && shouldLogErr {
   546  			fmt.Printf("ERROR: %s\ncaused by:\n%s;\n\n", err, s)
   547  		}
   548  		return err
   549  	})
   550  	if smither != nil {
   551  		smither.Close()
   552  	}
   553  
   554  	fmt.Printf("To reproduce, use schema:\n\n")
   555  	for _, stmt := range tableStmts {
   556  		fmt.Printf("%s;", stmt)
   557  	}
   558  	fmt.Printf("\n")
   559  }
   560  
   561  func TestRandomDatumRoundtrip(t *testing.T) {
   562  	defer leaktest.AfterTest(t)()
   563  
   564  	eval := tree.MakeTestingEvalContext(nil)
   565  
   566  	var smither *sqlsmith.Smither
   567  	testRandomSyntax(t, true, "", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   568  		var err error
   569  		smither, err = sqlsmith.NewSmither(nil, r.Rnd)
   570  		return err
   571  	}, func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error {
   572  		defer func() {
   573  			if err := recover(); err != nil {
   574  				s := fmt.Sprint(err)
   575  				// JSONB NaN and Infinity can't round
   576  				// trip because JSON doesn't support
   577  				// those as Numbers, only strings. (Try
   578  				// `JSON.stringify(Infinity)` in a JS console.)
   579  				if strings.Contains(s, "JSONB") && (strings.Contains(s, "Infinity") || strings.Contains(s, "NaN")) {
   580  					return
   581  				}
   582  				for _, cmp := range []string{
   583  					"ReturnType called on TypedExpr with empty typeAnnotation",
   584  					"runtime error: invalid memory address or nil pointer dereference",
   585  				} {
   586  					if strings.Contains(s, cmp) {
   587  						return
   588  					}
   589  				}
   590  				panic(err)
   591  			}
   592  		}()
   593  		generated := smither.GenerateExpr()
   594  		typ := generated.ResolvedType()
   595  		switch typ {
   596  		case types.Date, types.Decimal:
   597  			return nil
   598  		}
   599  		serializedGen := tree.Serialize(generated)
   600  
   601  		sema := tree.MakeSemaContext()
   602  		// We don't care about errors below because they are often
   603  		// caused by sqlsmith generating bogus queries. We're just
   604  		// looking for datums that don't match.
   605  		parsed1, err := parser.ParseExpr(serializedGen)
   606  		if err != nil {
   607  			return nil //nolint:returnerrcheck
   608  		}
   609  		typed1, err := parsed1.TypeCheck(ctx, &sema, typ)
   610  		if err != nil {
   611  			return nil //nolint:returnerrcheck
   612  		}
   613  		datum1, err := typed1.Eval(&eval)
   614  		if err != nil {
   615  			return nil //nolint:returnerrcheck
   616  		}
   617  		serialized1 := tree.Serialize(datum1)
   618  
   619  		parsed2, err := parser.ParseExpr(serialized1)
   620  		if err != nil {
   621  			return nil //nolint:returnerrcheck
   622  		}
   623  		typed2, err := parsed2.TypeCheck(ctx, &sema, typ)
   624  		if err != nil {
   625  			return nil //nolint:returnerrcheck
   626  		}
   627  		datum2, err := typed2.Eval(&eval)
   628  		if err != nil {
   629  			return nil //nolint:returnerrcheck
   630  		}
   631  		serialized2 := tree.Serialize(datum2)
   632  
   633  		if serialized1 != serialized2 {
   634  			panic(errors.Errorf("serialized didn't match:\nexpr: %s\nfirst: %s\nsecond: %s", generated, serialized1, serialized2))
   635  		}
   636  		if datum1.Compare(&eval, datum2) != 0 {
   637  			panic(errors.Errorf("%s [%[1]T] != %s [%[2]T] (original expr: %s)", serialized1, serialized2, serializedGen))
   638  		}
   639  		return nil
   640  	})
   641  }
   642  
   643  // testRandomSyntax performs all of the RSG setup and teardown for common
   644  // random syntax testing operations. It takes a closure where the random
   645  // expression should be generated and executed. It returns an error indicating
   646  // if the statement executed successfully. This is used to verify that at
   647  // least 1 success occurs (otherwise it is likely a bad test).
   648  func testRandomSyntax(
   649  	t *testing.T,
   650  	allowDuplicates bool,
   651  	databaseName string,
   652  	setup func(context.Context, *verifyFormatDB, *rsg.RSG) error,
   653  	fn func(context.Context, *verifyFormatDB, *rsg.RSG) error,
   654  ) {
   655  	if *flagRSGTime == 0 {
   656  		t.Skip("enable with '-rsg <duration>'")
   657  	}
   658  	ctx := context.Background()
   659  	defer utilccl.TestingEnableEnterprise()()
   660  
   661  	params, _ := tests.CreateTestServerParams()
   662  	params.UseDatabase = databaseName
   663  	// Catch panics and return them as errors.
   664  	params.Knobs.PGWireTestingKnobs = &sql.PGWireTestingKnobs{
   665  		CatchPanics: true,
   666  	}
   667  	s, rawDB, _ := serverutils.StartServer(t, params)
   668  	defer s.Stopper().Stop(ctx)
   669  	db := &verifyFormatDB{db: rawDB}
   670  
   671  	yBytes, err := ioutil.ReadFile(filepath.Join("..", "parser", "sql.y"))
   672  	if err != nil {
   673  		t.Fatal(err)
   674  	}
   675  	r, err := rsg.NewRSG(timeutil.Now().UnixNano(), string(yBytes), allowDuplicates)
   676  	if err != nil {
   677  		t.Fatal(err)
   678  	}
   679  
   680  	if setup != nil {
   681  		err := setup(ctx, db, r)
   682  		if err != nil {
   683  			t.Fatal(err)
   684  		}
   685  	}
   686  
   687  	// Broadcast channel for all workers.
   688  	done := make(chan struct{})
   689  	time.AfterFunc(*flagRSGTime, func() {
   690  		close(done)
   691  	})
   692  	var countsMu struct {
   693  		syncutil.Mutex
   694  		total, success int
   695  	}
   696  	ctx, cancel := context.WithCancel(ctx)
   697  	// Print status updates. We want this go routine to continue until all the
   698  	// workers are done, even if their ctx has been canceled, so the ctx for
   699  	// this func is a separate one with its own cancel.
   700  	go func(ctx context.Context) {
   701  		start := timeutil.Now()
   702  		for {
   703  			select {
   704  			case <-ctx.Done():
   705  				return
   706  			case <-time.After(5 * time.Second):
   707  			}
   708  			countsMu.Lock()
   709  			fmt.Printf("%v of %v: %d executions, %d successful\n",
   710  				timeutil.Since(start).Round(time.Second),
   711  				*flagRSGTime,
   712  				countsMu.total,
   713  				countsMu.success,
   714  			)
   715  			countsMu.Unlock()
   716  		}
   717  	}(ctx)
   718  	ctx, timeoutCancel := context.WithTimeout(ctx, *flagRSGTime)
   719  	err = ctxgroup.GroupWorkers(ctx, *flagRSGGoRoutines, func(ctx context.Context, _ int) error {
   720  		for {
   721  			select {
   722  			case <-ctx.Done():
   723  				return nil
   724  			default:
   725  			}
   726  			err := fn(ctx, db, r)
   727  			countsMu.Lock()
   728  			countsMu.total++
   729  			if err == nil {
   730  				countsMu.success++
   731  			} else {
   732  				if c := (*crasher)(nil); errors.As(err, &c) {
   733  					t.Errorf("Crash detected: \n%s\n\nStack trace:\n%s", c.sql, c.detail)
   734  				}
   735  			}
   736  			countsMu.Unlock()
   737  		}
   738  	})
   739  	timeoutCancel()
   740  	// cancel the timer printing's ctx
   741  	cancel()
   742  	t.Logf("%d executions, %d successful", countsMu.total, countsMu.success)
   743  	if err != nil {
   744  		t.Fatal(err)
   745  	}
   746  	if countsMu.success == 0 {
   747  		t.Fatal("0 successful executions")
   748  	}
   749  	if db.verifyFormatErr != nil {
   750  		t.Error(db.verifyFormatErr)
   751  	}
   752  }