From 91f7f4b07e54bbfd97f8705a76c4fb18d065b2a5 Mon Sep 17 00:00:00 2001 From: Faizan Qazi Date: Wed, 1 May 2024 15:02:13 -0400 Subject: [PATCH] workload/schemachange: add routine input DEFAULT expressions Previously, the randomized schema changer workload would generate functions with no default parameters and only use enum types for input parameters. This limited what was tested by this workload. To address this, this patch will generate functions with default values and non-enum type parameters. Additionally, function invocations will now use non-null values for input parameters. Fixes: #121718 Release note: None --- .../schemachange/operation_generator.go | 102 ++++++++++++++++-- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index bcf6104eda52..031290f75d66 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -39,6 +39,7 @@ import ( "github.com/cockroachdb/errors" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" + "github.com/lib/pq/oid" "golang.org/x/exp/slices" ) @@ -3936,7 +3937,9 @@ func (og *operationGenerator) createFunction(ctx context.Context, tx pgx.Tx) (*o }, `SELECT quote_ident(schema_id::REGNAMESPACE::STRING) AS schema, quote_ident(name) AS name, - array_to_string(proargnames, ',') AS args + array_to_string(proargnames, ',') AS args, + proargtypes AS argtypes, + pronargdefaults as numdefaults FROM functions INNER JOIN pg_catalog.pg_proc ON oid = (id + 100000) @@ -3968,6 +3971,7 @@ FROM var droppingEnumMembers []string var possibleBodyReferences []string var possibleParamReferences []string + var possibleParamReferencesWithDefaults []string var possibleReturnReferences []string var fnDuplicate bool @@ -3979,6 +3983,55 @@ FROM possibleReturnReferences = append(possibleReturnReferences, enum["name"].(string)) possibleParamReferences = append(possibleParamReferences, fmt.Sprintf(`enum_%d %s`, i, enum["name"])) } + // PGLSN was added in 23.2. + pgLSNNotSupported, err := isClusterVersionLessThan( + ctx, + tx, + clusterversion.V23_2.Version()) + if err != nil { + return nil, err + } + // REFCURSOR was added in 23.2. + refCursorNotSupported, err := isClusterVersionLessThan( + ctx, + tx, + clusterversion.V23_2.Version()) + if err != nil { + return nil, err + } + + defaultExpressionsNotSupported, err := isClusterVersionLessThan( + ctx, + tx, + clusterversion.V24_1.Version()) + if err != nil { + return nil, err + } + + // Generate random parameters / values for builtin types. + for i, typeVal := range randgen.SeedTypes { + // If we have types where invalid values can exist then skip over these, + // since randgen will not introspect to generate valid values (i.e. OID + // and REGCLASS). + if typeVal.Identical(types.AnyTuple) || + typeVal.IsWildcardType() || + typeVal == types.RegClass || + typeVal.Family() == types.OidFamily || + (pgLSNNotSupported && typeVal.Family() == types.PGLSNFamily) || + (refCursorNotSupported && typeVal.Family() == types.RefCursorFamily) { + continue + } + possibleReturnReferences = append(possibleReturnReferences, typeVal.SQLStandardName()) + possibleParamReferences = append(possibleParamReferences, fmt.Sprintf("val_%d %s", i+len(enums), typeVal.SQLStandardName())) + optionalDefaultValue := randgen.RandDatum(og.params.rng, typeVal, true) + if !defaultExpressionsNotSupported { + possibleParamReferencesWithDefaults = append(possibleParamReferencesWithDefaults, fmt.Sprintf("val_%d %s DEFAULT %s", + i+len(enums)+len(randgen.SeedTypes), + typeVal.SQLStandardName(), + tree.AsStringWithFlags(optionalDefaultValue, tree.FmtParsable))) + } + + } for _, member := range enumMembers { if member["dropping"].(bool) { @@ -4003,14 +4056,41 @@ FROM args := "" if function["args"] != nil { args = function["args"].(string) + argTypesStr := strings.Split(function["argtypes"].(string), " ") + argTypes := make([]int, 0, len(argTypesStr)) + // Determine how many default arguemnts should be used. + numDefaultArgs := int(function["numdefaults"].(int16)) + if numDefaultArgs > 0 { + numDefaultArgs = rand.Intn(numDefaultArgs) + } + // Populate the arguments for this signature, and select some number + // of default arguments. + for _, oidStr := range argTypesStr { + oid, err := strconv.Atoi(oidStr) + if err != nil { + return nil, err + } + argTypes = append(argTypes, oid) + } argIn := strings.Builder{} - // TODO(fqazi): Longer term we should populate actual arguments and - // not just NULLs. - for range strings.Split(args, ",") { + for idx := range strings.Split(args, ",") { + // We have hit the default arguments that we want to not populate. + if idx > len(argTypesStr)-numDefaultArgs { + break + } if argIn.Len() > 0 { argIn.WriteString(",") } - argIn.WriteString("NULL") + // Resolve the type for each column and if possible generate a random + // value via randgen. + oidValue := oid.Oid(argTypes[idx]) + typ, ok := types.OidToType[oidValue] + if !ok { + argIn.WriteString("NULL") + } else { + randomDatum := randgen.RandDatum(og.params.rng, typ, true) + argIn.WriteString(tree.AsStringWithFlags(randomDatum, tree.FmtParsable)) + } } args = argIn.String() } @@ -4034,8 +4114,12 @@ FROM }, "ParamRefs": func() (string, error) { refs, err := PickBetween(og.params.rng, 1, 99, possibleParamReferences) + if err != nil { + return "", err + } + refsWithDefaults, err := PickBetween(og.params.rng, 1, 99, possibleParamReferencesWithDefaults) if useParamRefs && err == nil { - return strings.Join(refs, ", "), nil + return strings.Join(append(refs, refsWithDefaults...), ", "), nil } return "", nil //nolint:returnerrcheck }, @@ -4109,7 +4193,7 @@ func (og *operationGenerator) dropFunction(ctx context.Context, tx pgx.Tx) (*opS FROM functions JOIN LATERAL ( SELECT - COALESCE(array_agg(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname)), '{}') AS funcargs + COALESCE(array_agg(replace(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname), 'pg_catalog.', '')), '{}') AS funcargs FROM pg_catalog.pg_type JOIN LATERAL ( SELECT unnest(proargtypes) AS oid FROM pg_catalog.pg_proc WHERE oid = (id + 100000) @@ -4190,7 +4274,7 @@ func (og *operationGenerator) alterFunctionRename(ctx context.Context, tx pgx.Tx FROM functions JOIN LATERAL ( SELECT - COALESCE(array_agg(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname)), '{}') AS funcargs + COALESCE(array_agg(replace(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname), 'pg_catalog.', '')), '{}') AS funcargs FROM pg_catalog.pg_type JOIN LATERAL ( SELECT unnest(proargtypes) AS oid FROM pg_catalog.pg_proc WHERE oid = (id + 100000) @@ -4283,7 +4367,7 @@ func (og *operationGenerator) alterFunctionSetSchema( FROM functions JOIN LATERAL ( SELECT - COALESCE(array_agg(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname)), '{}') AS funcargs + COALESCE(array_agg(replace(quote_ident(typnamespace::REGNAMESPACE::TEXT) || '.' || quote_ident(typname), 'pg_catalog.', '')), '{}') AS funcargs FROM pg_catalog.pg_type JOIN LATERAL ( SELECT unnest(proargtypes) AS oid FROM pg_catalog.pg_proc WHERE oid = (id + 100000)