diff --git a/src/backend/columnar/columnar_tableam.c b/src/backend/columnar/columnar_tableam.c index 2b11dd27a..1d98b6309 100644 --- a/src/backend/columnar/columnar_tableam.c +++ b/src/backend/columnar/columnar_tableam.c @@ -162,6 +162,8 @@ static bool CitusColumnarHasBeenLoadedInternal(void); static bool CitusColumnarHasBeenLoaded(void); static bool CheckCitusColumnarVersion(int elevel); static bool MajorVersionsCompatibleColumnar(char *leftVersion, char *rightVersion); +static bool MinorVersionsCompatibleRelaxedColumnar(char *leftVersion, char *rightVersion); +static int ParseVersionComponent(const char *version, char **endPtr); /* global variables for CheckCitusColumnarVersion */ static bool extensionLoadedColumnar = false; @@ -2939,7 +2941,7 @@ CheckInstalledVersionColumnar(int elevel) char *installedVersion = InstalledExtensionVersionColumnar(); - if (!MajorVersionsCompatibleColumnar(installedVersion, CITUS_EXTENSIONVERSION)) + if (!MinorVersionsCompatibleRelaxedColumnar(installedVersion, CITUS_EXTENSIONVERSION)) { ereport(elevel, (errmsg("loaded Citus library version differs from installed " "extension version"), @@ -2998,6 +3000,55 @@ MajorVersionsCompatibleColumnar(char *leftVersion, char *rightVersion) } +/* + * ParseVersionComponent parses the integer at the current position and + * advances endPtr past the parsed digits to the next character. + */ +static int +ParseVersionComponent(const char *version, char **endPtr) +{ + errno = 0; + long int val = strtol(version, endPtr, 10); + + if (errno == ERANGE || val > INT_MAX || val < INT_MIN) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("Invalid integer in version string"))); + } + return (int) val; +} + + +/* + * MinorVersionsCompatibleRelaxedColumnar checks if two versions have the same major + * version and their minor versions differ by at most 1. The schema version + * (after '-') is ignored. Returns true if compatible, false otherwise. + * + * Version format expected: "major.minor-schema" (e.g., "13.1-2") + */ +bool +MinorVersionsCompatibleRelaxedColumnar(char *leftVersion, char *rightVersion) +{ + char *leftSep; + char *rightSep; + + int leftMajor = ParseVersionComponent(leftVersion, &leftSep); + int rightMajor = ParseVersionComponent(rightVersion, &rightSep); + + if (leftMajor != rightMajor) + { + return false; + } + + int leftMinor = (*leftSep == '.') ? ParseVersionComponent(leftSep + 1, &leftSep) : 0; + int rightMinor = (*rightSep == '.') ? ParseVersionComponent(rightSep + 1, &rightSep) : + 0; + + int diff = leftMinor - rightMinor; + return diff >= -1 && diff <= 1; +} + + /* * AvailableExtensionVersion returns the Citus version from citus.control file. It also * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will diff --git a/src/backend/distributed/commands/extension.c b/src/backend/distributed/commands/extension.c index d5bb50317..1f3ba351e 100644 --- a/src/backend/distributed/commands/extension.c +++ b/src/backend/distributed/commands/extension.c @@ -80,7 +80,7 @@ ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parseTree) if (newExtensionVersion != NULL) { /* explicit version provided in CREATE or ALTER EXTENSION UPDATE; verify */ - if (!MajorVersionsCompatible(newExtensionVersion, CITUS_EXTENSIONVERSION)) + if (!MinorVersionsCompatibleRelaxed(newExtensionVersion, CITUS_EXTENSIONVERSION)) { ereport(ERROR, (errmsg("specified version incompatible with loaded " "Citus library"), diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index 9694b85bf..7bc6d45b2 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -339,6 +339,7 @@ static Oid DistAuthinfoRelationId(void); static Oid DistAuthinfoIndexId(void); static Oid DistPoolinfoRelationId(void); static Oid DistPoolinfoIndexId(void); +static int ParseVersionComponent(const char *version, char **endPtr); /* exports for SQL callable functions */ PG_FUNCTION_INFO_V1(citus_dist_partition_cache_invalidate); @@ -2440,7 +2441,7 @@ CheckInstalledVersion(int elevel) char *installedVersion = InstalledExtensionVersion(); - if (!MajorVersionsCompatible(installedVersion, CITUS_EXTENSIONVERSION)) + if (!MinorVersionsCompatibleRelaxed(installedVersion, CITUS_EXTENSIONVERSION)) { ereport(elevel, (errmsg("loaded Citus library version differs from installed " "extension version"), @@ -2518,6 +2519,55 @@ MajorVersionsCompatible(char *leftVersion, char *rightVersion) } +/* + * ParseVersionComponent parses the integer at the current position and + * advances endPtr past the parsed digits to the next character. + */ +static int +ParseVersionComponent(const char *version, char **endPtr) +{ + errno = 0; + long int val = strtol(version, endPtr, 10); + + if (errno == ERANGE || val > INT_MAX || val < INT_MIN) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("Invalid integer in version string"))); + } + return (int) val; +} + + +/* + * MinorVersionsCompatibleRelaxed checks if two versions have the same major + * version and their minor versions differ by at most 1. The schema version + * (after '-') is ignored. Returns true if compatible, false otherwise. + * + * Version format expected: "major.minor-schema" (e.g., "13.1-2") + */ +bool +MinorVersionsCompatibleRelaxed(char *leftVersion, char *rightVersion) +{ + char *leftSep; + char *rightSep; + + int leftMajor = ParseVersionComponent(leftVersion, &leftSep); + int rightMajor = ParseVersionComponent(rightVersion, &rightSep); + + if (leftMajor != rightMajor) + { + return false; + } + + int leftMinor = (*leftSep == '.') ? ParseVersionComponent(leftSep + 1, &leftSep) : 0; + int rightMinor = (*rightSep == '.') ? ParseVersionComponent(rightSep + 1, &rightSep) : + 0; + + int diff = leftMinor - rightMinor; + return diff >= -1 && diff <= 1; +} + + /* * AvailableExtensionVersion returns the Citus version from citus.control file. It also * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will diff --git a/src/include/distributed/metadata_cache.h b/src/include/distributed/metadata_cache.h index d3a7eaa06..459bf5f03 100644 --- a/src/include/distributed/metadata_cache.h +++ b/src/include/distributed/metadata_cache.h @@ -216,6 +216,7 @@ extern bool CheckCitusVersion(int elevel); extern bool CheckAvailableVersion(int elevel); extern bool InstalledAndAvailableVersionsSame(void); extern bool MajorVersionsCompatible(char *leftVersion, char *rightVersion); +extern bool MinorVersionsCompatibleRelaxed(char *leftVersion, char *rightVersion); extern void ErrorIfInconsistentShardIntervals(CitusTableCacheEntry *cacheEntry); extern void EnsureModificationsCanRun(void); extern void EnsureModificationsCanRunOnRelation(Oid relationId);