From b64b320095fad30d51b8a031ec0b6d87b6a1a20c Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Mon, 23 Jun 2025 18:30:30 +0200 Subject: [PATCH] Make PgVersionId a newtype This makes several casts & checks a lot easier to check, as `type` is nothing but an alias and doesn't actually prevent casts. --- libs/postgres_versioninfo/src/lib.rs | 53 ++++++++++++++++++++++++-- safekeeper/src/control_file_upgrade.rs | 4 +- safekeeper/src/safekeeper.rs | 14 ++++--- safekeeper/src/state.rs | 3 +- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/libs/postgres_versioninfo/src/lib.rs b/libs/postgres_versioninfo/src/lib.rs index 481e30d015..01d28298ca 100644 --- a/libs/postgres_versioninfo/src/lib.rs +++ b/libs/postgres_versioninfo/src/lib.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::fmt::{Display, Formatter}; use std::str::FromStr; @@ -14,7 +15,51 @@ pub enum PgMajorVersion { // !!! When you add a new PgMajorVersion, don't forget to update PgMajorVersion::ALL } -pub type PgVersionId = u32; +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub struct PgVersionId(u32); + +impl PgVersionId { + pub const UNKNOWN: PgVersionId = PgVersionId(0); + + pub fn from_full_pg_version(version: u32) -> PgVersionId { + match version { + 0 => PgVersionId(version), // unknown version + 140000..180000 => PgVersionId(version), + _ => panic!("Invalid full PostgreSQL version ID {version}"), + } + } +} + +impl Display for PgVersionId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + u32::fmt(&self.0, f) + } +} + +impl Serialize for PgVersionId { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + u32::serialize(&self.0, serializer) + } +} + +impl<'de> Deserialize<'de> for PgVersionId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + u32::deserialize(deserializer).map(PgVersionId) + } + + fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + u32::deserialize_in_place(deserializer, &mut place.0) + } +} impl PgMajorVersion { /// Get the numerical representation of the represented Major Version @@ -89,13 +134,13 @@ impl TryFrom for PgMajorVersion { type Error = InvalidPgVersion; fn try_from(value: PgVersionId) -> Result { - Ok(match value / 10000 { + Ok(match value.0 / 10000 { 14 => PgMajorVersion::PG14, 15 => PgMajorVersion::PG15, 16 => PgMajorVersion::PG16, 17 => PgMajorVersion::PG17, _ => { - panic!("invalid pg version ID {value}"); + panic!("Unknown PgVersionId: {value}"); } }) } @@ -103,7 +148,7 @@ impl TryFrom for PgMajorVersion { impl From for PgVersionId { fn from(value: PgMajorVersion) -> Self { - ((value as u32) * 10000) as PgVersionId + PgVersionId((value as u32) * 10000) } } diff --git a/safekeeper/src/control_file_upgrade.rs b/safekeeper/src/control_file_upgrade.rs index 588810cf6e..555cbe457b 100644 --- a/safekeeper/src/control_file_upgrade.rs +++ b/safekeeper/src/control_file_upgrade.rs @@ -445,13 +445,13 @@ pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result