From 66657135f3be936b366420e332921708a05d4e3d Mon Sep 17 00:00:00 2001 From: Michael Victor Zink Date: Tue, 7 May 2024 13:59:29 -0700 Subject: [PATCH] Support MySQL's `MEDIUMINT` column type Since there is no `i24/u24` native to Rust, we use `u32` and do some manual bounds checking when converting from other types. We also introduce a MySQL-specific logictest subdirectory, for tests (such as the one included here) which we never expect to run against PostgreSQL. Likely some existing tests should be moved there. Release-Note-Core: Added support for MySQL's `MEDIUMINT` column type. Fixes: REA-4285 Change-Id: I4530093ea029957dc4c8b32ab6b56a47cce177ca Reviewed-on: https://gerrit.readyset.name/c/readyset/+/7461 Tested-by: Buildkite CI Reviewed-by: Jason Brown --- .buildkite/pipeline.public-common.yml | 1 + data-generator/src/lib.rs | 8 +++- dataflow-expression/src/lower.rs | 6 +++ logictests/mysql/mediumint.test | 23 +++++++++ nom-sql/src/literal.rs | 6 +++ nom-sql/src/sql_type.rs | 61 +++++++++++++++++++++++ readyset-data/src/float.rs | 69 +++++++++++++++++++++++++++ readyset-data/src/integer.rs | 10 ++++ readyset-data/src/lib.rs | 6 +++ readyset-data/src/text.rs | 66 +++++++++++++++++++++++-- readyset-data/src/timestamp.rs | 2 + readyset-data/src/type.rs | 13 +++++ readyset-mysql/src/schema.rs | 5 ++ readyset-psql/src/schema.rs | 4 ++ 14 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 logictests/mysql/mediumint.test diff --git a/.buildkite/pipeline.public-common.yml b/.buildkite/pipeline.public-common.yml index 134ba3dfc2..2b5a67ba41 100644 --- a/.buildkite/pipeline.public-common.yml +++ b/.buildkite/pipeline.public-common.yml @@ -117,6 +117,7 @@ steps: - export RUST_BACKTRACE=full - cargo --locked run --bin readyset-logictest -- verify logictests - cargo --locked run --bin readyset-logictest -- verify logictests/psql --database-type postgresql + - cargo --locked run --bin readyset-logictest -- verify logictests/mysql --database-type mysql timeout_in_minutes: 60 depends_on: - build-image diff --git a/data-generator/src/lib.rs b/data-generator/src/lib.rs index d747f5e023..2a4e4e6bd9 100644 --- a/data-generator/src/lib.rs +++ b/data-generator/src/lib.rs @@ -427,9 +427,9 @@ pub fn value_of_type(typ: &SqlType) -> DfValue { // octets. DfValue::ByteArray(Arc::new(vec![0u8])) } - SqlType::Int(_) | SqlType::Int4 | SqlType::Serial => 1i32.into(), + SqlType::Int(_) | SqlType::MediumInt(_) | SqlType::Int4 | SqlType::Serial => 1i32.into(), SqlType::BigInt(_) | SqlType::Int8 | SqlType::BigSerial => 1i64.into(), - SqlType::UnsignedInt(_) => 1u32.into(), + SqlType::UnsignedInt(_) | SqlType::UnsignedMediumInt(_) => 1u32.into(), SqlType::UnsignedBigInt(_) => 1u64.into(), SqlType::TinyInt(_) => 1i8.into(), SqlType::UnsignedTinyInt(_) => 1u8.into(), @@ -533,6 +533,8 @@ where SqlType::UnsignedTinyInt(_) => rng.gen::().into(), SqlType::SmallInt(_) | SqlType::Int2 => rng.gen::().into(), SqlType::UnsignedSmallInt(_) => rng.gen::().into(), + SqlType::MediumInt(_) => rng.gen_range((-1i32 << 23)..(1i32 << 23)).into(), + SqlType::UnsignedMediumInt(_) => rng.gen_range(0..(1u32 << 24)).into(), SqlType::Float | SqlType::Double => 1.5f64.try_into().unwrap(), SqlType::Real => 1.5f32.try_into().unwrap(), SqlType::Decimal(prec, scale) => { @@ -660,6 +662,8 @@ pub fn unique_value_of_type(typ: &SqlType, idx: u32) -> DfValue { SqlType::UnsignedTinyInt(_) => (idx).into(), SqlType::SmallInt(_) | SqlType::Int2 => (idx as i16).into(), SqlType::UnsignedSmallInt(_) => (idx as u16).into(), + SqlType::MediumInt(_) => (idx as i32).into(), + SqlType::UnsignedMediumInt(_) => (idx).into(), SqlType::Float | SqlType::Double => (1.5 + idx as f64).try_into().unwrap(), SqlType::Real => (1.5 + idx as f32).try_into().unwrap(), SqlType::Decimal(prec, scale) => Decimal::new(clamp_digits(*prec as _), *scale as _).into(), diff --git a/dataflow-expression/src/lower.rs b/dataflow-expression/src/lower.rs index ea86718d2d..7022ead9d2 100644 --- a/dataflow-expression/src/lower.rs +++ b/dataflow-expression/src/lower.rs @@ -471,6 +471,8 @@ fn mysql_type_conversion(left_ty: &DfType, right_ty: &DfType) -> DfType { | DfType::UnsignedTinyInt | DfType::SmallInt | DfType::UnsignedSmallInt + | DfType::MediumInt + | DfType::UnsignedMediumInt | DfType::Int | DfType::UnsignedInt | DfType::BigInt @@ -479,6 +481,8 @@ fn mysql_type_conversion(left_ty: &DfType, right_ty: &DfType) -> DfType { | DfType::UnsignedTinyInt | DfType::SmallInt | DfType::UnsignedSmallInt + | DfType::MediumInt + | DfType::UnsignedMediumInt | DfType::Int | DfType::UnsignedInt | DfType::BigInt @@ -503,6 +507,8 @@ fn mysql_type_conversion(left_ty: &DfType, right_ty: &DfType) -> DfType { | DfType::UnsignedTinyInt | DfType::SmallInt | DfType::UnsignedSmallInt + | DfType::MediumInt + | DfType::UnsignedMediumInt | DfType::Int | DfType::UnsignedInt | DfType::BigInt diff --git a/logictests/mysql/mediumint.test b/logictests/mysql/mediumint.test new file mode 100644 index 0000000000..2c72f8504e --- /dev/null +++ b/logictests/mysql/mediumint.test @@ -0,0 +1,23 @@ +statement ok +create table t1 (mint mediumint, umint mediumint unsigned); + +statement ok +insert into t1 values (-8388608, 0), (8388607, 16777215); + +statement error +insert into t1 values (-8388609, 0); + +statement error +insert into t1 values (8388608, 0); + +statement error +insert into t1 values (0, -1); + +statement error +insert into t1 values (0, 16777216); + +query I +select umint from t1 where mint = ?; +? = 8388607 +---- +16777215 diff --git a/nom-sql/src/literal.rs b/nom-sql/src/literal.rs index 321c2bb01d..c513f208e3 100644 --- a/nom-sql/src/literal.rs +++ b/nom-sql/src/literal.rs @@ -315,6 +315,12 @@ impl Literal { SqlType::UnsignedSmallInt(_) => any::() .prop_map(|i| Self::UnsignedInteger(i as _)) .boxed(), + SqlType::MediumInt(_) => ((-1i32 << 23)..(1i32 << 23)) + .prop_map(|i| Self::Integer(i as _)) + .boxed(), + SqlType::UnsignedMediumInt(_) => (0..(1u32 << 24)) + .prop_map(|i| Self::UnsignedInteger(i as _)) + .boxed(), SqlType::Blob | SqlType::ByteArray | SqlType::LongBlob diff --git a/nom-sql/src/sql_type.rs b/nom-sql/src/sql_type.rs index 0b4b584dd6..3dcaedf644 100644 --- a/nom-sql/src/sql_type.rs +++ b/nom-sql/src/sql_type.rs @@ -76,6 +76,8 @@ pub enum SqlType { UnsignedTinyInt(Option), SmallInt(Option), UnsignedSmallInt(Option), + MediumInt(Option), + UnsignedMediumInt(Option), Int2, Int4, Int8, @@ -224,10 +226,12 @@ impl Arbitrary for SqlType { (1..255u16).prop_map(|p| BigInt(Some(p))).boxed(), (1..255u16).prop_map(|p| SmallInt(Some(p))).boxed(), option::of(1..255u16).prop_map(TinyInt).boxed(), + option::of(1..255u16).prop_map(MediumInt).boxed(), option::of(1..255u16).prop_map(UnsignedInt).boxed(), option::of(1..255u16).prop_map(UnsignedSmallInt).boxed(), option::of(1..255u16).prop_map(UnsignedBigInt).boxed(), option::of(1..255u16).prop_map(UnsignedTinyInt).boxed(), + option::of(1..255u16).prop_map(UnsignedMediumInt).boxed(), Just(TinyText).boxed(), Just(MediumText).boxed(), Just(LongText).boxed(), @@ -335,6 +339,11 @@ impl DialectDisplay for SqlType { write_with_len(f, "SMALLINT", len)?; write!(f, " UNSIGNED") } + SqlType::MediumInt(len) => write_with_len(f, "MEDIUMINT", len), + SqlType::UnsignedMediumInt(len) => { + write_with_len(f, "MEDIUMINT", len)?; + write!(f, " UNSIGNED") + } SqlType::Int2 => write!(f, "INT2"), SqlType::Int4 => write!(f, "INT4"), SqlType::Int8 => write!(f, "INT8"), @@ -773,6 +782,14 @@ fn type_identifier_part1( value(SqlType::Int8, tag_no_case("int8")), |i| int_type("tinyint", SqlType::UnsignedTinyInt, SqlType::TinyInt, i), |i| int_type("smallint", SqlType::UnsignedSmallInt, SqlType::SmallInt, i), + cond_fail(dialect == Dialect::MySQL, |i| { + int_type( + "mediumint", + SqlType::UnsignedMediumInt, + SqlType::MediumInt, + i, + ) + }), |i| int_type("integer", SqlType::UnsignedInt, SqlType::Int, i), |i| int_type("int", SqlType::UnsignedInt, SqlType::Int, i), |i| int_type("bigint", SqlType::UnsignedBigInt, SqlType::BigInt, i), @@ -1022,6 +1039,27 @@ pub fn mysql_int_cast_targets() -> impl Fn(LocatedSpan<&[u8]>) -> NomSqlResult<& } } +/// Calls the parser if the condition is met; fails otherwise. +/// +/// This mirrors the intent of [`nom::combinator::cond`], but for use within [`nom::branch::alt`]: +/// instead of resolving to [`None`] when the condition is not met, it returns a parser error so +/// that this branch fails and another alternative can be selected. +fn cond_fail(pred: bool, f: F) -> impl FnMut(LocatedSpan<&[u8]>) -> NomSqlResult<&[u8], SqlType> +where + F: Fn(LocatedSpan<&[u8]>) -> NomSqlResult<&[u8], SqlType>, +{ + move |i| { + if pred { + f(i) + } else { + Err(nom::Err::Error(ParseError::from_error_kind( + i, + ErrorKind::Fail, + ))) + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -1146,6 +1184,18 @@ mod tests { assert!(res.is_ok()); assert_eq!(res.unwrap().1, SqlType::Double); } + + #[test] + fn mediumint() { + assert_eq!( + test_parse!(type_identifier(Dialect::MySQL), b"mediumint(8)"), + SqlType::MediumInt(Some(8)) + ); + assert_eq!( + test_parse!(type_identifier(Dialect::MySQL), b"mediumint"), + SqlType::MediumInt(None) + ); + } } mod postgres { @@ -1432,5 +1482,16 @@ mod tests { } ); } + + #[test] + fn mediumint_not_recognized() { + assert_eq!( + test_parse!(type_identifier(Dialect::PostgreSQL), b"mediumint"), + SqlType::Other(Relation { + schema: None, + name: "mediumint".into() + }) + ); + } } } diff --git a/readyset-data/src/float.rs b/readyset-data/src/float.rs index 5cfbb870cb..6216a56a3e 100644 --- a/readyset-data/src/float.rs +++ b/readyset-data/src/float.rs @@ -82,6 +82,14 @@ pub(crate) fn coerce_f64(val: f64, to_ty: &DfType, from_ty: &DfType) -> ReadySet DfType::UnsignedSmallInt => coerce_f64_to_uint::(val) .ok_or_else(bounds_err) .map(DfValue::from), + DfType::MediumInt => coerce_f64_to_int::(val) + .filter(|i| ((-1 << 23)..(1 << 23)).contains(i)) + .ok_or_else(bounds_err) + .map(DfValue::from), + DfType::UnsignedMediumInt => coerce_f64_to_uint::(val) + .filter(|&i| i < (1 << 24)) + .ok_or_else(bounds_err) + .map(DfValue::from), DfType::Int => coerce_f64_to_int::(val) .ok_or_else(bounds_err) .map(DfValue::from), @@ -184,6 +192,16 @@ pub(crate) fn coerce_decimal( DfType::UnsignedTinyInt => val.to_u8().ok_or_else(err).map(DfValue::from), DfType::SmallInt => val.to_i16().ok_or_else(err).map(DfValue::from), DfType::UnsignedSmallInt => val.to_u16().ok_or_else(err).map(DfValue::from), + DfType::MediumInt => val + .to_i32() + .filter(|i| ((-1 << 23)..(1 << 23)).contains(i)) + .ok_or_else(err) + .map(DfValue::from), + DfType::UnsignedMediumInt => val + .to_u32() + .filter(|&i| i < (1 << 24)) + .ok_or_else(err) + .map(DfValue::from), DfType::Int => val.to_i32().ok_or_else(err).map(DfValue::from), DfType::UnsignedInt => val.to_u32().ok_or_else(err).map(DfValue::from), DfType::BigInt => val.to_i64().ok_or_else(err).map(DfValue::from), @@ -314,6 +332,30 @@ mod tests { } } + #[proptest] + fn float_to_mediumint(val: f32) { + if val < (-1i32 << 23) as f32 - 0.5 || val >= ((1i32 << 23) - 1) as f32 + 0.5 { + DfValue::Double(val as _) + .coerce_to(&DfType::MediumInt, &DfType::Unknown) + .expect_err("OOB"); + } else { + assert_eq!( + DfValue::Double(val as f64).coerce_to(&DfType::MediumInt, &DfType::Unknown), + Ok(DfValue::Int(val.round() as i64)) + ); + } + if val < -0.5f32 || val >= ((1u32 << 24) - 1) as f32 + 0.5 { + DfValue::Double(val as _) + .coerce_to(&DfType::UnsignedMediumInt, &DfType::Unknown) + .expect_err("OOB"); + } else { + assert_eq!( + DfValue::Double(val as f64).coerce_to(&DfType::UnsignedMediumInt, &DfType::Unknown), + Ok(DfValue::UnsignedInt(val.round() as u64)) + ); + } + } + #[proptest] fn float_to_int(val: f64) { if val < i32::MIN as f64 - 0.5 || val >= i32::MAX as f64 + 0.5 { @@ -494,5 +536,32 @@ mod tests { .coerce_to(&DfType::UnsignedBigInt, &DfType::Unknown), Ok(DfValue::UnsignedInt(17946744073709551616)) ); + + assert_eq!( + DfValue::Double(-8388608.49).coerce_to(&DfType::MediumInt, &DfType::Unknown), + Ok(DfValue::Int(-8388608)) + ); + + DfValue::Double(-8388608.51) + .coerce_to(&DfType::MediumInt, &DfType::Unknown) + .unwrap_err(); + + assert_eq!( + DfValue::Double(8388607.49).coerce_to(&DfType::MediumInt, &DfType::Unknown), + Ok(DfValue::Int(8388607)) + ); + + DfValue::Double(8388607.51) + .coerce_to(&DfType::MediumInt, &DfType::Unknown) + .unwrap_err(); + + assert_eq!( + DfValue::Double(16777215.49).coerce_to(&DfType::UnsignedMediumInt, &DfType::Unknown), + Ok(DfValue::UnsignedInt(16777215)) + ); + + DfValue::Double(16777215.51) + .coerce_to(&DfType::UnsignedMediumInt, &DfType::Unknown) + .unwrap_err(); } } diff --git a/readyset-data/src/integer.rs b/readyset-data/src/integer.rs index cc55fdfc53..aa851fd237 100644 --- a/readyset-data/src/integer.rs +++ b/readyset-data/src/integer.rs @@ -73,6 +73,16 @@ where DfType::UnsignedTinyInt => u8::try_from(val).map_err(|_| err()).map(DfValue::from), DfType::SmallInt => i16::try_from(val).map_err(|_| err()).map(DfValue::from), DfType::UnsignedSmallInt => u16::try_from(val).map_err(|_| err()).map(DfValue::from), + DfType::MediumInt => i32::try_from(val) + .ok() + .filter(|i| ((-1 << 23)..(1 << 23)).contains(i)) + .ok_or_else(err) + .map(DfValue::from), + DfType::UnsignedMediumInt => u32::try_from(val) + .ok() + .filter(|&i| i < (1 << 24)) + .ok_or_else(err) + .map(DfValue::from), DfType::Int => i32::try_from(val).map_err(|_| err()).map(DfValue::from), DfType::UnsignedInt => u32::try_from(val).map_err(|_| err()).map(DfValue::from), DfType::BigInt => i64::try_from(val).map_err(|_| err()).map(DfValue::from), diff --git a/readyset-data/src/lib.rs b/readyset-data/src/lib.rs index e9b04ee290..df6fc08d70 100644 --- a/readyset-data/src/lib.rs +++ b/readyset-data/src/lib.rs @@ -2243,6 +2243,9 @@ mod arbitrary { .boxed(), Some(DfType::TinyInt) => any::().prop_map(|i| DfValue::Int(i as i64)).boxed(), Some(DfType::SmallInt) => any::().prop_map(|i| DfValue::Int(i as i64)).boxed(), + Some(DfType::MediumInt) => ((-1i32 << 23)..(1i32 << 23)) + .prop_map(|i| DfValue::Int(i as i64)) + .boxed(), Some(DfType::Int) => any::().prop_map(|i| DfValue::Int(i as i64)).boxed(), Some(DfType::BigInt) => any::().prop_map(DfValue::Int).boxed(), Some(DfType::UnsignedTinyInt) => any::() @@ -2251,6 +2254,9 @@ mod arbitrary { Some(DfType::UnsignedSmallInt) => any::() .prop_map(|u| DfValue::UnsignedInt(u as u64)) .boxed(), + Some(DfType::UnsignedMediumInt) => (0..(1u32 << 24)) + .prop_map(|u| DfValue::UnsignedInt(u as u64)) + .boxed(), Some(DfType::UnsignedInt) => any::() .prop_map(|u| DfValue::UnsignedInt(u as u64)) .boxed(), diff --git a/readyset-data/src/text.rs b/readyset-data/src/text.rs index e3482029c8..c180820d12 100644 --- a/readyset-data/src/text.rs +++ b/readyset-data/src/text.rs @@ -336,7 +336,7 @@ pub(crate) trait TextCoerce: Sized + Clone + Into { /// Print the DfValue name for error reporting fn type_name() -> String; - /// A convenience constructor for a coerction error from this type + /// A convenience constructor for a coercion error from this type fn coerce_err(ty: &DfType, deets: D) -> ReadySetError { ReadySetError::DfValueConversionError { src_type: Self::type_name(), @@ -345,8 +345,8 @@ pub(crate) trait TextCoerce: Sized + Clone + Into { } } - /// A convenience integer parser that diffirentiates between out of bounds errors and other - /// parse errors + /// A convenience integer parser that ignores non-numeric suffixes and differentiates between + /// out of bounds errors and other parse errors fn parse_int(str: &str, ty: &DfType) -> ReadySetResult where I: FromStr + Into, @@ -372,6 +372,26 @@ pub(crate) trait TextCoerce: Sized + Clone + Into { } } + fn check_mediumint_bounds(v: DfValue, ty: &DfType) -> ReadySetResult { + match v { + DfValue::Int(i) => { + if ((-1 << 23)..(1 << 23)).contains(&i) { + Ok(v) + } else { + Err(Self::coerce_err(ty, "out of bounds")) + } + } + DfValue::UnsignedInt(i) => { + if i < (1 << 24) { + Ok(v) + } else { + Err(Self::coerce_err(ty, "out of bounds")) + } + } + _ => Err(Self::coerce_err(ty, "unsupported")), + } + } + /// Coerce this type to a different DfValue. fn coerce_to(&self, to_ty: &DfType, from_ty: &DfType) -> ReadySetResult { let str = self.try_str()?; @@ -451,6 +471,10 @@ pub(crate) trait TextCoerce: Sized + Clone + Into { DfType::UnsignedTinyInt => Self::parse_int::(str, to_ty), DfType::SmallInt => Self::parse_int::(str, to_ty), DfType::UnsignedSmallInt => Self::parse_int::(str, to_ty), + DfType::MediumInt => Self::parse_int::(str, to_ty) + .and_then(|v| Self::check_mediumint_bounds(v, to_ty)), + DfType::UnsignedMediumInt => Self::parse_int::(str, to_ty) + .and_then(|v| Self::check_mediumint_bounds(v, to_ty)), DfType::Int => Self::parse_int::(str, to_ty), DfType::UnsignedInt => Self::parse_int::(str, to_ty), DfType::BigInt => Self::parse_int::(str, to_ty), @@ -878,4 +902,40 @@ mod tests { assert_eq!(result.unwrap().collation(), Some(Collation::Citext)); } + + #[test] + fn mediumint_bounds_checks() { + assert_eq!( + DfValue::from("-8388608") + .coerce_to(&DfType::MediumInt, &DfType::DEFAULT_TEXT) + .unwrap(), + DfValue::Int(-8388608), + ); + + DfValue::from("-8388609") + .coerce_to(&DfType::MediumInt, &DfType::DEFAULT_TEXT) + .unwrap_err(); + + assert_eq!( + DfValue::from("8388607") + .coerce_to(&DfType::MediumInt, &DfType::DEFAULT_TEXT) + .unwrap(), + DfValue::UnsignedInt(8388607), + ); + + DfValue::from("8388608") + .coerce_to(&DfType::MediumInt, &DfType::DEFAULT_TEXT) + .unwrap_err(); + + assert_eq!( + DfValue::from("16777215") + .coerce_to(&DfType::UnsignedMediumInt, &DfType::DEFAULT_TEXT) + .unwrap(), + DfValue::UnsignedInt(16777215), + ); + + DfValue::from("16777216") + .coerce_to(&DfType::UnsignedMediumInt, &DfType::DEFAULT_TEXT) + .unwrap_err(); + } } diff --git a/readyset-data/src/timestamp.rs b/readyset-data/src/timestamp.rs index 1b1dbea64d..0e174d5499 100644 --- a/readyset-data/src/timestamp.rs +++ b/readyset-data/src/timestamp.rs @@ -390,6 +390,8 @@ impl TimestampTz { DfType::Int | DfType::UnsignedInt + | DfType::MediumInt + | DfType::UnsignedMediumInt | DfType::SmallInt | DfType::UnsignedSmallInt | DfType::TinyInt diff --git a/readyset-data/src/type.rs b/readyset-data/src/type.rs index 310041b62b..9ae9d89eec 100644 --- a/readyset-data/src/type.rs +++ b/readyset-data/src/type.rs @@ -73,6 +73,12 @@ pub enum DfType { /// [`u16`]. UnsignedSmallInt, + /// Notionally an `i24`, but really an [`i32`] that we bounds check. + MediumInt, + + /// Notionally an `u24`, but really an [`u32`] that we bounds check. + UnsignedMediumInt, + /// [`f32`]: a IEEE 754 floating-point 32-bit real value. /// /// This is either: @@ -248,10 +254,12 @@ impl DfType { Int(_) | Int4 => Self::Int, TinyInt(_) => Self::TinyInt, SmallInt(_) | Int2 => Self::SmallInt, + MediumInt(_) => Self::MediumInt, BigInt(_) | Int8 => Self::BigInt, UnsignedInt(_) => Self::UnsignedInt, UnsignedTinyInt(_) => Self::UnsignedTinyInt, UnsignedSmallInt(_) => Self::UnsignedSmallInt, + UnsignedMediumInt(_) => Self::UnsignedMediumInt, UnsignedBigInt(_) => Self::UnsignedBigInt, Double => Self::Double, @@ -343,6 +351,9 @@ impl DfType { | DfType::UnsignedTinyInt | DfType::SmallInt | DfType::UnsignedSmallInt + // XXX(mvzink): MEDIUMINT isn't implemented by PostgreSQL, but this seems better than making this fn fail + | DfType::MediumInt + | DfType::UnsignedMediumInt | DfType::Float | DfType::Double | DfType::Numeric { .. } => PgTypeCategory::Numeric, @@ -670,6 +681,8 @@ impl fmt::Display for DfType { | Self::UnsignedTinyInt | Self::SmallInt | Self::UnsignedSmallInt + | Self::MediumInt + | Self::UnsignedMediumInt | Self::Int | Self::UnsignedInt | Self::BigInt diff --git a/readyset-mysql/src/schema.rs b/readyset-mysql/src/schema.rs index a0e8eeff90..25b13fb63c 100644 --- a/readyset-mysql/src/schema.rs +++ b/readyset-mysql/src/schema.rs @@ -35,6 +35,11 @@ pub(crate) fn convert_column(col: &ColumnSchema) -> ReadySetResult MYSQL_TYPE_INT24, + DfType::UnsignedMediumInt => { + colflags |= mysql_srv::ColumnFlags::UNSIGNED_FLAG; + MYSQL_TYPE_INT24 + } DfType::Bool => MYSQL_TYPE_BIT, DfType::DateTime { .. } => MYSQL_TYPE_DATETIME, DfType::Blob => MYSQL_TYPE_BLOB, diff --git a/readyset-psql/src/schema.rs b/readyset-psql/src/schema.rs index b23ff8bb9b..120e62eb82 100644 --- a/readyset-psql/src/schema.rs +++ b/readyset-psql/src/schema.rs @@ -90,6 +90,8 @@ pub fn type_to_pgsql(col_type: &DfType) -> Result { DfType::DateTime { .. } => unsupported_type!(), DfType::Binary(_) => unsupported_type!(), DfType::VarBinary(_) => unsupported_type!(), + DfType::MediumInt => unsupported_type!(), + DfType::UnsignedMediumInt => unsupported_type!(), DfType::Enum { metadata: Some(PgEnumMetadata { name, schema, oid, .. @@ -141,6 +143,8 @@ pub fn type_to_pgsql(col_type: &DfType) -> Result { DfType::Array(box DfType::TinyInt) => Ok(Type::CHAR_ARRAY), DfType::Array(box DfType::UnsignedTinyInt) => unsupported_type!(), DfType::Array(box DfType::UnsignedSmallInt) => unsupported_type!(), + DfType::Array(box DfType::MediumInt) => unsupported_type!(), + DfType::Array(box DfType::UnsignedMediumInt) => unsupported_type!(), DfType::Array(box DfType::Blob) => unsupported_type!(), DfType::Array(box DfType::DateTime { .. }) => unsupported_type!(), DfType::Array(box DfType::Binary(_)) => unsupported_type!(),