Skip to content

Commit 44525e4

Browse files
authored
feat: add interval type support for RowBinaryWithNamesAndTypes (#277)
1 parent 9802ea5 commit 44525e4

File tree

3 files changed

+286
-1
lines changed

3 files changed

+286
-1
lines changed

src/rowbinary/validation.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,8 @@ fn validate_impl<'de, 'cursor, R: Row>(
467467
|| matches!(
468468
data_type,
469469
DataTypeNode::Decimal(_, _, DecimalType::Decimal64)
470-
) =>
470+
)
471+
|| matches!(data_type, DataTypeNode::Interval(_)) =>
471472
{
472473
None
473474
}

tests/it/rbwnat.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,131 @@ async fn date_and_time() {
996996
);
997997
}
998998

999+
#[tokio::test]
1000+
async fn interval() {
1001+
#[derive(Debug, Row, Serialize, Deserialize, PartialEq)]
1002+
struct Data {
1003+
id: u32,
1004+
interval_nanosecond: i64,
1005+
interval_microsecond: i64,
1006+
interval_millisecond: i64,
1007+
interval_second: i64,
1008+
interval_minute: i64,
1009+
interval_hour: i64,
1010+
interval_day: i64,
1011+
interval_week: i64,
1012+
interval_month: i64,
1013+
interval_quarter: i64,
1014+
interval_year: i64,
1015+
}
1016+
1017+
let client = get_client();
1018+
let mut cursor = client
1019+
.query(
1020+
"
1021+
SELECT * FROM (
1022+
SELECT
1023+
0 :: UInt32 AS id,
1024+
toIntervalNanosecond (0) AS interval_nanosecond,
1025+
toIntervalMicrosecond (0) AS interval_microsecond,
1026+
toIntervalMillisecond (0) AS interval_millisecond,
1027+
toIntervalSecond (0) AS interval_second,
1028+
toIntervalMinute (0) AS interval_minute,
1029+
toIntervalHour (0) AS interval_hour,
1030+
toIntervalDay (0) AS interval_day,
1031+
toIntervalWeek (0) AS interval_week,
1032+
toIntervalMonth (0) AS interval_month,
1033+
toIntervalQuarter (0) AS interval_quarter,
1034+
toIntervalYear (0) AS interval_year
1035+
UNION ALL
1036+
SELECT
1037+
1 :: UInt32 AS id,
1038+
toIntervalNanosecond (-9223372036854775808) AS interval_nanosecond,
1039+
toIntervalMicrosecond (-9223372036854775808) AS interval_microsecond,
1040+
toIntervalMillisecond (-9223372036854775808) AS interval_millisecond,
1041+
toIntervalSecond (-9223372036854775808) AS interval_second,
1042+
toIntervalMinute (-9223372036854775808) AS interval_minute,
1043+
toIntervalHour (-9223372036854775808) AS interval_hour,
1044+
toIntervalDay (-9223372036854775808) AS interval_day,
1045+
toIntervalWeek (-9223372036854775808) AS interval_week,
1046+
toIntervalMonth (-9223372036854775808) AS interval_month,
1047+
toIntervalQuarter (-9223372036854775808) AS interval_quarter,
1048+
toIntervalYear (-9223372036854775808) AS interval_year
1049+
UNION ALL
1050+
SELECT
1051+
2 :: UInt32 AS id,
1052+
toIntervalNanosecond (9223372036854775807) AS interval_nanosecond,
1053+
toIntervalMicrosecond (9223372036854775807) AS interval_microsecond,
1054+
toIntervalMillisecond (9223372036854775807) AS interval_millisecond,
1055+
toIntervalSecond (9223372036854775807) AS interval_second,
1056+
toIntervalMinute (9223372036854775807) AS interval_minute,
1057+
toIntervalHour (9223372036854775807) AS interval_hour,
1058+
toIntervalDay (9223372036854775807) AS interval_day,
1059+
toIntervalWeek (9223372036854775807) AS interval_week,
1060+
toIntervalMonth (9223372036854775807) AS interval_month,
1061+
toIntervalQuarter (9223372036854775807) AS interval_quarter,
1062+
toIntervalYear (9223372036854775807) AS interval_year
1063+
) ORDER BY id ASC
1064+
",
1065+
)
1066+
.fetch::<Data>()
1067+
.unwrap();
1068+
1069+
assert_eq!(
1070+
cursor.next().await.unwrap().unwrap(),
1071+
Data {
1072+
id: 0,
1073+
interval_nanosecond: 0,
1074+
interval_microsecond: 0,
1075+
interval_millisecond: 0,
1076+
interval_second: 0,
1077+
interval_minute: 0,
1078+
interval_hour: 0,
1079+
interval_day: 0,
1080+
interval_week: 0,
1081+
interval_month: 0,
1082+
interval_quarter: 0,
1083+
interval_year: 0,
1084+
}
1085+
);
1086+
1087+
assert_eq!(
1088+
cursor.next().await.unwrap().unwrap(),
1089+
Data {
1090+
id: 1,
1091+
interval_nanosecond: i64::MIN,
1092+
interval_microsecond: i64::MIN,
1093+
interval_millisecond: i64::MIN,
1094+
interval_second: i64::MIN,
1095+
interval_minute: i64::MIN,
1096+
interval_hour: i64::MIN,
1097+
interval_day: i64::MIN,
1098+
interval_week: i64::MIN,
1099+
interval_month: i64::MIN,
1100+
interval_quarter: i64::MIN,
1101+
interval_year: i64::MIN,
1102+
}
1103+
);
1104+
1105+
assert_eq!(
1106+
cursor.next().await.unwrap().unwrap(),
1107+
Data {
1108+
id: 2,
1109+
interval_nanosecond: i64::MAX,
1110+
interval_microsecond: i64::MAX,
1111+
interval_millisecond: i64::MAX,
1112+
interval_second: i64::MAX,
1113+
interval_minute: i64::MAX,
1114+
interval_hour: i64::MAX,
1115+
interval_day: i64::MAX,
1116+
interval_week: i64::MAX,
1117+
interval_month: i64::MAX,
1118+
interval_quarter: i64::MAX,
1119+
interval_year: i64::MAX,
1120+
}
1121+
);
1122+
}
1123+
9991124
#[tokio::test]
10001125
#[cfg(feature = "uuid")]
10011126
async fn uuid() {

types/src/data_types.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub enum DataTypeNode {
7171
/// Precision and optional timezone (timezone is ignored in value operations)
7272
Time64(DateTimePrecision),
7373

74+
Interval(IntervalType),
75+
7476
IPv4,
7577
IPv6,
7678

@@ -143,6 +145,7 @@ impl DataTypeNode {
143145
str if str.starts_with("DateTime") => parse_datetime(str),
144146
str if str.starts_with("Time64") => parse_time64(str),
145147
str if str.starts_with("Time") => Ok(Self::Time),
148+
str if str.starts_with("Interval") => Ok(Self::Interval(str[8..].parse()?)),
146149

147150
str if str.starts_with("Nullable") => parse_nullable(str),
148151
str if str.starts_with("LowCardinality") => parse_low_cardinality(str),
@@ -208,6 +211,7 @@ impl Display for DataTypeNode {
208211
DateTime64(precision, Some(tz)) => write!(f, "DateTime64({precision}, '{tz}')"),
209212
Time => write!(f, "Time"),
210213
Time64(precision) => write!(f, "Time64({precision})"),
214+
Interval(interval) => write!(f, "Interval{interval}"),
211215
IPv4 => write!(f, "IPv4"),
212216
IPv6 => write!(f, "IPv6"),
213217
Bool => write!(f, "Bool"),
@@ -392,6 +396,65 @@ impl Display for DateTimePrecision {
392396
}
393397
}
394398

399+
/// Represents the type of an interval.
400+
/// See also: <https://clickhouse.com/docs/sql-reference/data-types/special-data-types/interval>
401+
#[derive(Debug, Clone, PartialEq)]
402+
#[allow(missing_docs)]
403+
pub enum IntervalType {
404+
Nanosecond,
405+
Microsecond,
406+
Millisecond,
407+
Second,
408+
Minute,
409+
Hour,
410+
Day,
411+
Week,
412+
Month,
413+
Quarter,
414+
Year,
415+
}
416+
417+
impl std::str::FromStr for IntervalType {
418+
type Err = TypesError;
419+
420+
fn from_str(s: &str) -> Result<Self, Self::Err> {
421+
match s {
422+
"Nanosecond" => Ok(IntervalType::Nanosecond),
423+
"Microsecond" => Ok(IntervalType::Microsecond),
424+
"Millisecond" => Ok(IntervalType::Millisecond),
425+
"Second" => Ok(IntervalType::Second),
426+
"Minute" => Ok(IntervalType::Minute),
427+
"Hour" => Ok(IntervalType::Hour),
428+
"Day" => Ok(IntervalType::Day),
429+
"Week" => Ok(IntervalType::Week),
430+
"Month" => Ok(IntervalType::Month),
431+
"Quarter" => Ok(IntervalType::Quarter),
432+
"Year" => Ok(IntervalType::Year),
433+
_ => Err(TypesError::TypeParsingError(format!(
434+
"Unknown interval type: {s}"
435+
))),
436+
}
437+
}
438+
}
439+
440+
impl Display for IntervalType {
441+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
442+
match self {
443+
Self::Nanosecond => write!(f, "Nanosecond"),
444+
Self::Microsecond => write!(f, "Microsecond"),
445+
Self::Millisecond => write!(f, "Millisecond"),
446+
Self::Second => write!(f, "Second"),
447+
Self::Minute => write!(f, "Minute"),
448+
Self::Hour => write!(f, "Hour"),
449+
Self::Day => write!(f, "Day"),
450+
Self::Week => write!(f, "Week"),
451+
Self::Month => write!(f, "Month"),
452+
Self::Quarter => write!(f, "Quarter"),
453+
Self::Year => write!(f, "Year"),
454+
}
455+
}
456+
}
457+
395458
fn parse_fixed_string(input: &str) -> Result<DataTypeNode, TypesError> {
396459
if input.len() >= 14 {
397460
let size_str = &input[12..input.len() - 1];
@@ -1136,6 +1199,54 @@ mod tests {
11361199
assert!(DataTypeNode::new("Time64(x)").is_err());
11371200
}
11381201

1202+
#[test]
1203+
fn test_data_type_new_interval() {
1204+
assert_eq!(
1205+
DataTypeNode::new("IntervalNanosecond").unwrap(),
1206+
DataTypeNode::Interval(IntervalType::Nanosecond)
1207+
);
1208+
assert_eq!(
1209+
DataTypeNode::new("IntervalMicrosecond").unwrap(),
1210+
DataTypeNode::Interval(IntervalType::Microsecond)
1211+
);
1212+
assert_eq!(
1213+
DataTypeNode::new("IntervalMillisecond").unwrap(),
1214+
DataTypeNode::Interval(IntervalType::Millisecond)
1215+
);
1216+
assert_eq!(
1217+
DataTypeNode::new("IntervalSecond").unwrap(),
1218+
DataTypeNode::Interval(IntervalType::Second)
1219+
);
1220+
assert_eq!(
1221+
DataTypeNode::new("IntervalMinute").unwrap(),
1222+
DataTypeNode::Interval(IntervalType::Minute)
1223+
);
1224+
assert_eq!(
1225+
DataTypeNode::new("IntervalHour").unwrap(),
1226+
DataTypeNode::Interval(IntervalType::Hour)
1227+
);
1228+
assert_eq!(
1229+
DataTypeNode::new("IntervalDay").unwrap(),
1230+
DataTypeNode::Interval(IntervalType::Day)
1231+
);
1232+
assert_eq!(
1233+
DataTypeNode::new("IntervalWeek").unwrap(),
1234+
DataTypeNode::Interval(IntervalType::Week)
1235+
);
1236+
assert_eq!(
1237+
DataTypeNode::new("IntervalMonth").unwrap(),
1238+
DataTypeNode::Interval(IntervalType::Month)
1239+
);
1240+
assert_eq!(
1241+
DataTypeNode::new("IntervalQuarter").unwrap(),
1242+
DataTypeNode::Interval(IntervalType::Quarter)
1243+
);
1244+
assert_eq!(
1245+
DataTypeNode::new("IntervalYear").unwrap(),
1246+
DataTypeNode::Interval(IntervalType::Year)
1247+
);
1248+
}
1249+
11391250
#[test]
11401251
fn test_data_type_new_low_cardinality() {
11411252
assert_eq!(
@@ -1542,6 +1653,54 @@ mod tests {
15421653
}
15431654
}
15441655

1656+
#[test]
1657+
fn test_interval_to_string() {
1658+
assert_eq!(
1659+
DataTypeNode::Interval(IntervalType::Nanosecond).to_string(),
1660+
"IntervalNanosecond"
1661+
);
1662+
assert_eq!(
1663+
DataTypeNode::Interval(IntervalType::Microsecond).to_string(),
1664+
"IntervalMicrosecond"
1665+
);
1666+
assert_eq!(
1667+
DataTypeNode::Interval(IntervalType::Millisecond).to_string(),
1668+
"IntervalMillisecond"
1669+
);
1670+
assert_eq!(
1671+
DataTypeNode::Interval(IntervalType::Second).to_string(),
1672+
"IntervalSecond"
1673+
);
1674+
assert_eq!(
1675+
DataTypeNode::Interval(IntervalType::Minute).to_string(),
1676+
"IntervalMinute"
1677+
);
1678+
assert_eq!(
1679+
DataTypeNode::Interval(IntervalType::Hour).to_string(),
1680+
"IntervalHour"
1681+
);
1682+
assert_eq!(
1683+
DataTypeNode::Interval(IntervalType::Day).to_string(),
1684+
"IntervalDay"
1685+
);
1686+
assert_eq!(
1687+
DataTypeNode::Interval(IntervalType::Week).to_string(),
1688+
"IntervalWeek"
1689+
);
1690+
assert_eq!(
1691+
DataTypeNode::Interval(IntervalType::Month).to_string(),
1692+
"IntervalMonth"
1693+
);
1694+
assert_eq!(
1695+
DataTypeNode::Interval(IntervalType::Quarter).to_string(),
1696+
"IntervalQuarter"
1697+
);
1698+
assert_eq!(
1699+
DataTypeNode::Interval(IntervalType::Year).to_string(),
1700+
"IntervalYear"
1701+
);
1702+
}
1703+
15451704
#[test]
15461705
fn test_data_type_node_into_string() {
15471706
let data_type = DataTypeNode::new("Array(Int32)").unwrap();

0 commit comments

Comments
 (0)