diff --git a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal index 4c41858bc822..2e58cad5190a 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal +++ b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal @@ -201,7 +201,7 @@ ALTER TENANT [5] GRANT CAPABILITY can_admin_split query ITT colnames,retry,rowsort SELECT * FROM crdb_internal.node_tenant_capabilities_cache WHERE capability_name = 'can_admin_split' ---- -tenant_id capability_name capability_value +tenant_id capability_name capability_value 1 can_admin_split true 5 can_admin_split true diff --git a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant index a95c9400cb27..52236f38f988 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant +++ b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant @@ -398,9 +398,9 @@ select crdb_internal.get_vmodule() · query T -select regexp_replace(regexp_replace(crdb_internal.node_executable_version()::string, '(-\d+)?$', ''), '10000', ''); +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -23.2 +24.1 query ITTT colnames,rowsort select node_id, component, field, regexp_replace(regexp_replace(value, '^\d+$', ''), e':\\d+', ':') as value from crdb_internal.node_runtime_info @@ -488,9 +488,9 @@ select * from crdb_internal.gossip_alerts # Anyone can see the executable version. query T -select regexp_replace(regexp_replace(crdb_internal.node_executable_version()::string, '(-\d+)?$', ''), '10000', ''); +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -23.2 +24.1 user root diff --git a/pkg/clusterversion/BUILD.bazel b/pkg/clusterversion/BUILD.bazel index eff3aedf3013..42609983d25f 100644 --- a/pkg/clusterversion/BUILD.bazel +++ b/pkg/clusterversion/BUILD.bazel @@ -16,6 +16,7 @@ go_library( importpath = "github.com/cockroachdb/cockroach/pkg/clusterversion", visibility = ["//visibility:public"], deps = [ + "//pkg/build", "//pkg/roachpb", "//pkg/settings", "//pkg/util/envutil", @@ -39,6 +40,7 @@ go_test( args = ["-test.timeout=55s"], embed = [":clusterversion"], deps = [ + "//pkg/build", "//pkg/roachpb", "//pkg/settings", "//pkg/settings/cluster", diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 9be2d60ff32d..0a38313a5d02 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -10,7 +10,10 @@ package clusterversion -import "github.com/cockroachdb/cockroach/pkg/roachpb" +import ( + "github.com/cockroachdb/cockroach/pkg/build" + "github.com/cockroachdb/cockroach/pkg/roachpb" +) // Key is a unique identifier for a version of CockroachDB. type Key int @@ -650,34 +653,45 @@ const developmentBranch = true // finalVersion should be set on a release branch to the minted final cluster // version key, e.g. to V23_2 on the release-23.2 branch once it is minted. -// Setting it has the effect of ensuring no versions are subsequently added. +// Setting it has the effect of ensuring no versions are subsequently added (see +// TestFinalVersion). const finalVersion Key = -1 -func init() { - if finalVersion >= 0 { - if Latest != finalVersion { - panic("latest version does not match final version") - } - if developmentBranch { - panic("final version set on development branch") - } - } else if Latest.IsFinal() { - panic("finalVersion not set but latetest version is final") - } -} - // Version returns the roachpb.Version corresponding to a key. func (k Key) Version() roachpb.Version { version := versionTable[k] - return maybeDevOffset(k, version) + return maybeApplyDevOffset(k, version) } -// IsFinal returns true if the key is a final version (as opposed to a -// transitional internal version during upgrade). +// IsFinal returns true if the key corresponds to a final version (as opposed to +// a transitional internal version during upgrade). func (k Key) IsFinal() bool { return k.Version().IsFinal() } +// ReleaseSeries returns the final version for the release series the Key +// belongs to. Specifically: +// - if the key corresponds to a final version (e.g. 23.2), the result is the +// same version; e.g. V23_2.ReleaseSeries() is v23.2. +// - if the key corresponds to a transitional upgrade version (e.g. v23.2-8), +// the result is the next final version (e.g. v24.1). +// +// Note that the result does not have any DevOffset applied. +func (k Key) ReleaseSeries() roachpb.Version { + // Find the first key >= k that is a final version. + for k := k; k < numKeys; k++ { + if k.IsFinal() { + return removeDevOffset(k.Version()) + } + } + // k is a dev version in the latest release series. + major, minor := build.BranchReleaseSeries() + return roachpb.Version{ + Major: int32(major), + Minor: int32(minor), + } +} + func (k Key) String() string { return k.Version().String() } diff --git a/pkg/clusterversion/cockroach_versions_test.go b/pkg/clusterversion/cockroach_versions_test.go index b3c49c6db951..f8341210e5d5 100644 --- a/pkg/clusterversion/cockroach_versions_test.go +++ b/pkg/clusterversion/cockroach_versions_test.go @@ -11,8 +11,10 @@ package clusterversion import ( + "fmt" "testing" + "github.com/cockroachdb/cockroach/pkg/build" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/redact" @@ -92,6 +94,15 @@ func TestKeyConstants(t *testing.T) { require.Equal(t, PreviousRelease, supported[len(supported)-1]) } +func TestFinalVersion(t *testing.T) { + if finalVersion >= 0 { + require.False(t, developmentBranch, "final version set but developmentBranch is still set") + require.Equal(t, Latest, finalVersion, "finalVersion must match the minted latest version") + } else { + require.False(t, Latest.IsFinal(), "finalVersion not set but Latest is final") + } +} + func TestVersionFormat(t *testing.T) { defer leaktest.AfterTest(t)() @@ -147,3 +158,15 @@ func TestClusterVersionPrettyPrint(t *testing.T) { } } } + +func TestReleaseSeries(t *testing.T) { + require.Equal(t, fmt.Sprintf("v%s", Latest.ReleaseSeries()), build.BinaryVersionPrefix()) + if Latest.IsFinal() { + require.True(t, Latest.Version() == Latest.ReleaseSeries()) + } else { + require.True(t, removeDevOffset(Latest.Version()).Less(Latest.ReleaseSeries())) + } + require.Equal(t, PreviousRelease.ReleaseSeries(), removeDevOffset(PreviousRelease.Version())) + require.Equal(t, (PreviousRelease - 1).ReleaseSeries(), removeDevOffset(PreviousRelease.Version())) + require.Equal(t, MinSupported.ReleaseSeries(), removeDevOffset(MinSupported.Version())) +} diff --git a/pkg/clusterversion/dev_offset.go b/pkg/clusterversion/dev_offset.go index 988752d31b61..4cbc59782352 100644 --- a/pkg/clusterversion/dev_offset.go +++ b/pkg/clusterversion/dev_offset.go @@ -78,10 +78,18 @@ var devOffsetKeyStart = func() Key { // a dev branch. const DevOffset = 1_000_000 -// maybeDevOffset applies DevOffset to the major version, if appropriate. -func maybeDevOffset(key Key, v roachpb.Version) roachpb.Version { +// maybeApplyDevOffset applies DevOffset to the major version, if appropriate. +func maybeApplyDevOffset(key Key, v roachpb.Version) roachpb.Version { if key >= devOffsetKeyStart { v.Major += DevOffset } return v } + +// removeDevOffset removes DevOffset from the given version, if it was applied. +func removeDevOffset(v roachpb.Version) roachpb.Version { + if v.Major > DevOffset { + v.Major -= DevOffset + } + return v +} diff --git a/pkg/roachpb/version.go b/pkg/roachpb/version.go index 22f8ffa0ec80..3c604c1be3c6 100644 --- a/pkg/roachpb/version.go +++ b/pkg/roachpb/version.go @@ -66,8 +66,10 @@ func (v Version) SafeFormat(p redact.SafePrinter, _ rune) { p.Printf("%d.%d-%d", v.Major, v.Minor, v.Internal) } -// IsFinal returns true if this is a final version (as opposed to a -// transitional internal version during upgrade). +// IsFinal returns true if this is a final version (as opposed to a transitional +// internal version during upgrade). +// +// A version is final iff Internal = 0. func (v Version) IsFinal() bool { return v.Internal == 0 } diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal b/pkg/sql/logictest/testdata/logic_test/crdb_internal index 084cb4ae79df..8fc5b1e8086a 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal @@ -638,9 +638,15 @@ select crdb_internal.get_vmodule() · query T -select regexp_replace(crdb_internal.node_executable_version()::string, '(-\d+)?$', ''); +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -1000023.2 +24.1 + +query error version '1.0' not supported +SELECT crdb_internal.release_series('1.0') + +query error version '2000000.0' not supported +SELECT crdb_internal.release_series('2000000.0') query ITTT colnames,rowsort select node_id, component, field, regexp_replace(regexp_replace(value, '^\d+$', ''), e':\\d+', ':') as value from crdb_internal.node_runtime_info @@ -792,9 +798,9 @@ SELECT * FROM crdb_internal.check_consistency(true, b'\x02', b'\x04') # Anyone can see the executable version. query T -select regexp_replace(crdb_internal.node_executable_version()::string, '(-\d+)?$', ''); +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -1000023.2 +24.1 user root diff --git a/pkg/sql/logictest/testdata/logic_test/mixed_version_can_login b/pkg/sql/logictest/testdata/logic_test/mixed_version_can_login index 1393761fce90..8782631b2b44 100644 --- a/pkg/sql/logictest/testdata/logic_test/mixed_version_can_login +++ b/pkg/sql/logictest/testdata/logic_test/mixed_version_can_login @@ -9,10 +9,10 @@ upgrade 0 upgrade 1 -query B nodeidx=0 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=0 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 query T nodeidx=2 SELECT crdb_internal.node_executable_version() @@ -22,18 +22,18 @@ SELECT crdb_internal.node_executable_version() # Verify that a non-root user can login on the upgraded node. user testuser nodeidx=0 -query B -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 # Verify that a root user can login on the upgraded node. user root nodeidx=1 -query B -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 # Verify that a non-root user can login on the non-upgraded node. user testuser nodeidx=2 @@ -53,7 +53,7 @@ SELECT crdb_internal.node_executable_version() upgrade 2 -query B -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 diff --git a/pkg/sql/logictest/testdata/logic_test/mixed_version_role_members_user_ids b/pkg/sql/logictest/testdata/logic_test/mixed_version_role_members_user_ids index 198d45239181..85175912d640 100644 --- a/pkg/sql/logictest/testdata/logic_test/mixed_version_role_members_user_ids +++ b/pkg/sql/logictest/testdata/logic_test/mixed_version_role_members_user_ids @@ -46,10 +46,10 @@ upgrade 1 # Test that there are no problems creating role memberships on a mixed-version cluster. -query B nodeidx=1 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=1 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 user root nodeidx=1 @@ -89,17 +89,17 @@ upgrade 2 # Verify that all nodes are now running 23.2 binaries. -query B nodeidx=0 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=0 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 -query B nodeidx=1 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=1 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 -query B nodeidx=2 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=2 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 diff --git a/pkg/sql/logictest/testdata/logic_test/mixed_version_udf_execute_privileges b/pkg/sql/logictest/testdata/logic_test/mixed_version_udf_execute_privileges index 9673688e371f..eee13a51882a 100644 --- a/pkg/sql/logictest/testdata/logic_test/mixed_version_udf_execute_privileges +++ b/pkg/sql/logictest/testdata/logic_test/mixed_version_udf_execute_privileges @@ -54,20 +54,20 @@ upgrade 2 # Verify that all nodes are now running 24.1 binaries. -query B nodeidx=0 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=0 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 -query B nodeidx=1 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=1 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 -query B nodeidx=2 -SELECT crdb_internal.node_executable_version() SIMILAR TO '23.2-%' +query T nodeidx=2 +SELECT crdb_internal.release_series(crdb_internal.node_executable_version()) ---- -true +24.1 # Makes sure the upgrade job has finished, and the cluster version gate is # passed. diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index 5322c47b152d..33520afcdb1d 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -4995,6 +4995,37 @@ value if you rely on the HLC for accuracy.`, }, ), + "crdb_internal.release_series": makeBuiltin( + tree.FunctionProperties{Category: builtinconstants.CategorySystemInfo}, + tree.Overload{ + Types: tree.ParamTypes{{Name: "version", Typ: types.String}}, + ReturnType: tree.FixedReturnType(types.String), + Fn: func(ctx context.Context, evalCtx *eval.Context, args tree.Datums) (tree.Datum, error) { + s, ok := tree.AsDString(args[0]) + if !ok { + return nil, errors.Newf("expected string value, got %T", args[0]) + } + version, err := roachpb.ParseVersion(string(s)) + if err != nil { + return nil, err + } + if version.Less(clusterversion.MinSupported.Version()) || clusterversion.Latest.Version().Less(version) { + return nil, errors.Newf( + "version %s not supported; this binary only understands versions %s through %s", + args[0], clusterversion.MinSupported, clusterversion.Latest, + ) + } + for k := clusterversion.Latest; ; k-- { + if k.Version().LessEq(version) { + return tree.NewDString(k.ReleaseSeries().String()), nil + } + } + }, + Info: "Converts a cluster version to the final cluster version in that release series.", + Volatility: volatility.Stable, + }, + ), + "crdb_internal.approximate_timestamp": makeBuiltin( tree.FunctionProperties{Category: builtinconstants.CategorySystemInfo}, tree.Overload{ diff --git a/pkg/sql/sem/builtins/fixed_oids.go b/pkg/sql/sem/builtins/fixed_oids.go index 4fdf329c1311..8626e5e59c6c 100644 --- a/pkg/sql/sem/builtins/fixed_oids.go +++ b/pkg/sql/sem/builtins/fixed_oids.go @@ -2510,6 +2510,7 @@ var builtinOidsArray = []string{ 2539: `pg_encoding_max_length(encoding: int) -> int`, 2540: `information_schema._pg_datetime_precision(typid: oid, typmod: int4) -> int`, 2541: `information_schema._pg_interval_type(typid: oid, typmod: int4) -> string`, + 2542: `crdb_internal.release_series(version: string) -> string`, } var builtinOidsBySignature map[string]oid.Oid diff --git a/pkg/upgrade/upgrades/builtins_test.go b/pkg/upgrade/upgrades/builtins_test.go index 73a989b580b8..5bc8333b18a2 100644 --- a/pkg/upgrade/upgrades/builtins_test.go +++ b/pkg/upgrade/upgrades/builtins_test.go @@ -50,10 +50,14 @@ func TestIsAtLeastVersionBuiltin(t *testing.T) { // Check that the builtin returns false when comparing against the new // version because we are still on the bootstrap version. sqlDB.CheckQueryResults(t, "SELECT crdb_internal.is_at_least_version('"+v+"')", [][]string{{"false"}}) + sqlDB.CheckQueryResults(t, "SELECT crdb_internal.release_series(version) FROM [SHOW CLUSTER SETTING version]", + [][]string{{clusterversion.MinSupported.ReleaseSeries().String()}}) // Run the upgrade. sqlDB.Exec(t, "SET CLUSTER SETTING version = $1", v) // It should now return true. sqlDB.CheckQueryResultsRetry(t, "SELECT crdb_internal.is_at_least_version('"+v+"')", [][]string{{"true"}}) + sqlDB.CheckQueryResults(t, "SELECT crdb_internal.release_series(version) FROM [SHOW CLUSTER SETTING version]", + [][]string{{clusterversion.Latest.ReleaseSeries().String()}}) }