Skip to content

Commit

Permalink
implement several mysql time related types json
Browse files Browse the repository at this point in the history
Signed-off-by: YangKeao <yangkeao@chunibyo.icu>
  • Loading branch information
YangKeao committed Sep 14, 2022
1 parent 8c93b91 commit ead53a0
Show file tree
Hide file tree
Showing 11 changed files with 638 additions and 69 deletions.
12 changes: 6 additions & 6 deletions components/tidb_query_datatype/src/codec/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,14 +510,14 @@ impl<'a> ToInt for JsonRef<'a> {
// TiDB: 5
// MySQL: 4
let val = match self.get_type() {
JsonType::Object | JsonType::Array | JsonType::Opaque => Ok(ctx
.handle_truncate_err(Error::truncated_wrong_val("Integer", self.to_string()))
.map(|_| 0)?),
JsonType::Literal => Ok(self.get_literal().map_or(0, |x| x as i64)),
JsonType::I64 => Ok(self.get_i64()),
JsonType::U64 => Ok(self.get_u64() as i64),
JsonType::Double => self.get_double().to_int(ctx, tp),
JsonType::String => self.get_str_bytes()?.to_int(ctx, tp),
_ => Ok(ctx
.handle_truncate_err(Error::truncated_wrong_val("Integer", self.to_string()))
.map(|_| 0)?),
}?;
val.to_int(ctx, tp)
}
Expand All @@ -526,14 +526,14 @@ impl<'a> ToInt for JsonRef<'a> {
#[inline]
fn to_uint(&self, ctx: &mut EvalContext, tp: FieldTypeTp) -> Result<u64> {
let val = match self.get_type() {
JsonType::Object | JsonType::Array | JsonType::Opaque => Ok(ctx
.handle_truncate_err(Error::truncated_wrong_val("Integer", self.to_string()))
.map(|_| 0)?),
JsonType::Literal => Ok(self.get_literal().map_or(0, |x| x as u64)),
JsonType::I64 => Ok(self.get_i64() as u64),
JsonType::U64 => Ok(self.get_u64()),
JsonType::Double => self.get_double().to_uint(ctx, tp),
JsonType::String => self.get_str_bytes()?.to_uint(ctx, tp),
_ => Ok(ctx
.handle_truncate_err(Error::truncated_wrong_val("Integer", self.to_string()))
.map(|_| 0)?),
}?;
val.to_uint(ctx, tp)
}
Expand Down
249 changes: 248 additions & 1 deletion components/tidb_query_datatype/src/codec/mysql/json/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ impl<'a> JsonRef<'a> {
&self.value()[val_offset..val_offset + opaque_bytes_len as usize + len_len + 1],
)
}
JsonType::Date | JsonType::Datetime | JsonType::Timestamp => {
JsonRef::new(val_type, &self.value()[val_offset..val_offset + TIME_LEN])
}
JsonType::Time => JsonRef::new(
val_type,
&self.value()[val_offset..val_offset + DURATION_LEN],
),
_ => {
let data_size =
NumberCodec::decode_u32_le(&self.value()[val_offset + ELEMENT_COUNT_LEN..])
Expand Down Expand Up @@ -122,7 +129,16 @@ impl<'a> JsonRef<'a> {

#[cfg(test)]
mod tests {
use super::{super::Json, *};
use std::collections::BTreeMap;

use super::*;
use crate::{
codec::{
data_type::Duration,
mysql::{Json, Time, TimeType},
},
expr::EvalContext,
};

#[test]
fn test_type() {
Expand All @@ -143,4 +159,235 @@ mod tests {
assert_eq!(json.as_ref().get_type(), tp, "{:?}", json_str);
}
}

#[test]
fn test_array_get_elem() {
let mut ctx = EvalContext::default();

let time = Time::parse(
&mut ctx,
"1998-06-13 12:13:14",
TimeType::DateTime,
0,
false,
)
.unwrap();
let duration = Duration::parse(&mut ctx, "12:13:14", 0).unwrap();
let array = vec![
Json::from_u64(1).unwrap(),
Json::from_str_val("abcdefg").unwrap(),
];
let object = BTreeMap::from([
("key1".to_string(), Json::from_u64(1).unwrap()),
("key2".to_string(), Json::from_str_val("abcdefg").unwrap()),
]);

let json_array = Json::from_array(vec![
Json::from_u64(1).unwrap(),
Json::from_time(time).unwrap(),
Json::from_duration(duration).unwrap(),
Json::from_array(array).unwrap(),
Json::from_str_val("abcdefg").unwrap(),
Json::from_bool(false).unwrap(),
Json::from_object(object).unwrap(),
])
.unwrap();
let json_array_ref = json_array.as_ref();

assert_eq!(json_array_ref.array_get_elem(0).unwrap().get_u64(), 1);
assert_eq!(
json_array_ref
.array_get_elem(1)
.unwrap()
.get_time()
.unwrap(),
time
);
assert_eq!(
json_array_ref
.array_get_elem(2)
.unwrap()
.get_duration()
.unwrap(),
duration
);
assert_eq!(
json_array_ref
.array_get_elem(3)
.unwrap()
.array_get_elem(0)
.unwrap()
.get_u64(),
1
);
assert_eq!(
json_array_ref
.array_get_elem(3)
.unwrap()
.array_get_elem(1)
.unwrap()
.get_str()
.unwrap(),
"abcdefg"
);
assert_eq!(
json_array_ref.array_get_elem(4).unwrap().get_str().unwrap(),
"abcdefg"
);
assert_eq!(
json_array_ref
.array_get_elem(5)
.unwrap()
.get_literal()
.unwrap(),
false
);
assert_eq!(
json_array_ref.array_get_elem(6).unwrap().object_get_key(0),
b"key1"
);
assert_eq!(
json_array_ref.array_get_elem(6).unwrap().object_get_key(1),
b"key2"
);
assert_eq!(
json_array_ref
.array_get_elem(6)
.unwrap()
.object_get_val(0)
.unwrap()
.get_u64(),
1
);
assert_eq!(
json_array_ref
.array_get_elem(6)
.unwrap()
.object_get_val(1)
.unwrap()
.get_str()
.unwrap(),
"abcdefg"
);
}

#[test]
fn test_object_get_val() {
let mut ctx = EvalContext::default();

let time = Time::parse(
&mut ctx,
"1998-06-13 12:13:14",
TimeType::DateTime,
0,
false,
)
.unwrap();
let duration = Duration::parse(&mut ctx, "12:13:14", 0).unwrap();
let array = vec![
Json::from_u64(1).unwrap(),
Json::from_str_val("abcdefg").unwrap(),
];
let object = BTreeMap::from([
("key1".to_string(), Json::from_u64(1).unwrap()),
("key2".to_string(), Json::from_str_val("abcdefg").unwrap()),
]);

let json_object = Json::from_object(BTreeMap::from([
("0".to_string(), Json::from_u64(1).unwrap()),
("1".to_string(), Json::from_time(time).unwrap()),
("2".to_string(), Json::from_duration(duration).unwrap()),
("3".to_string(), Json::from_array(array).unwrap()),
("4".to_string(), Json::from_str_val("abcdefg").unwrap()),
("5".to_string(), Json::from_bool(false).unwrap()),
("6".to_string(), Json::from_object(object).unwrap()),
]))
.unwrap();
let json_object_ref = json_object.as_ref();

assert_eq!(json_object_ref.object_get_key(0), b"0");
assert_eq!(json_object_ref.object_get_key(1), b"1");
assert_eq!(json_object_ref.object_get_key(2), b"2");
assert_eq!(json_object_ref.object_get_key(3), b"3");

assert_eq!(json_object_ref.object_get_val(0).unwrap().get_u64(), 1);
assert_eq!(
json_object_ref
.object_get_val(1)
.unwrap()
.get_time()
.unwrap(),
time
);
assert_eq!(
json_object_ref
.object_get_val(2)
.unwrap()
.get_duration()
.unwrap(),
duration
);
assert_eq!(
json_object_ref
.object_get_val(3)
.unwrap()
.array_get_elem(0)
.unwrap()
.get_u64(),
1
);
assert_eq!(
json_object_ref
.object_get_val(3)
.unwrap()
.array_get_elem(1)
.unwrap()
.get_str()
.unwrap(),
"abcdefg"
);
assert_eq!(
json_object_ref
.object_get_val(4)
.unwrap()
.get_str()
.unwrap(),
"abcdefg"
);
assert_eq!(
json_object_ref
.object_get_val(5)
.unwrap()
.get_literal()
.unwrap(),
false
);
assert_eq!(
json_object_ref.object_get_val(6).unwrap().object_get_key(0),
b"key1"
);
assert_eq!(
json_object_ref.object_get_val(6).unwrap().object_get_key(1),
b"key2"
);
assert_eq!(
json_object_ref
.object_get_val(6)
.unwrap()
.object_get_val(0)
.unwrap()
.get_u64(),
1
);
assert_eq!(
json_object_ref
.object_get_val(6)
.unwrap()
.object_get_val(1)
.unwrap()
.get_str()
.unwrap(),
"abcdefg"
);
}
}
21 changes: 21 additions & 0 deletions components/tidb_query_datatype/src/codec/mysql/json/comparison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ impl<'a> JsonRef<'a> {
JsonType::I64 | JsonType::U64 | JsonType::Double => PRECEDENCE_NUMBER,
JsonType::String => PRECEDENCE_STRING,
JsonType::Opaque => PRECEDENCE_OPAQUE,
JsonType::Date => PRECEDENCE_DATE,
JsonType::Datetime => PRECEDENCE_DATETIME,
JsonType::Timestamp => PRECEDENCE_DATETIME,
JsonType::Time => PRECEDENCE_TIME,
}
}

Expand Down Expand Up @@ -150,6 +154,23 @@ impl<'a> PartialOrd for JsonRef<'a> {
return None;
}
}
JsonType::Date | JsonType::Datetime | JsonType::Timestamp => {
// The jsonTypePrecedences guarantees that the DATE is only comparable with the
// DATE, and the DATETIME and TIMESTAMP will compare with
// each other
if let (Ok(left), Ok(right)) = (self.get_time(), right.get_time()) {
left.partial_cmp(&right)
} else {
return None;
}
}
JsonType::Time => {
if let (Ok(left), Ok(right)) = (self.get_duration(), right.get_duration()) {
left.partial_cmp(&right)
} else {
return None;
}
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub const LITERAL_LEN: usize = 1;
pub const U16_LEN: usize = 2;
pub const U32_LEN: usize = 4;
pub const NUMBER_LEN: usize = 8;
pub const TIME_LEN: usize = NUMBER_LEN;
pub const DURATION_LEN: usize = NUMBER_LEN + U32_LEN;
pub const HEADER_LEN: usize = ELEMENT_COUNT_LEN + SIZE_LEN; // element size + data size
pub const KEY_OFFSET_LEN: usize = U32_LEN;
pub const KEY_LEN_LEN: usize = U16_LEN;
Expand Down
4 changes: 4 additions & 0 deletions components/tidb_query_datatype/src/codec/mysql/json/jcodec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ pub trait JsonDecoder: NumberDecoder {
let (opaque_bytes_len, len_len) = NumberCodec::try_decode_var_u64(&value[1..])?;
self.read_bytes(opaque_bytes_len as usize + len_len + 1)?
}
JsonType::Date | JsonType::Datetime | JsonType::Timestamp => {
self.read_bytes(TIME_LEN)?
}
JsonType::Time => self.read_bytes(DURATION_LEN)?,
};
Ok(Json::new(tp, Vec::from(value)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const JSON_TYPE_ARRAY: &[u8] = b"ARRAY";
const JSON_TYPE_BIT: &[u8] = b"BIT";
const JSON_TYPE_BLOB: &[u8] = b"BLOB";
const JSON_TYPE_OPAQUE: &[u8] = b"OPAQUE";
const JSON_TYPE_DATE: &[u8] = b"DATE";
const JSON_TYPE_DATETIME: &[u8] = b"DATETIME";
const JSON_TYPE_TIME: &[u8] = b"TIME";

impl<'a> JsonRef<'a> {
/// `json_type` is the implementation for
Expand Down Expand Up @@ -43,6 +46,10 @@ impl<'a> JsonRef<'a> {
Ok(FieldTypeTp::Bit) => JSON_TYPE_BIT,
_ => JSON_TYPE_OPAQUE,
},
JsonType::Date => JSON_TYPE_DATE,
JsonType::Datetime => JSON_TYPE_DATETIME,
JsonType::Timestamp => JSON_TYPE_DATETIME,
JsonType::Time => JSON_TYPE_TIME,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ impl<'a> JsonRef<'a> {
let s = self.get_str()?;
unquote_string(s)
}
JsonType::Date
| JsonType::Datetime
| JsonType::Timestamp
| JsonType::Time
| JsonType::Opaque => {
let s = self.to_string();
// Remove the quotes of output
assert!(s.len() > 2);
Ok(s[1..s.len() - 1].to_string())
}
_ => Ok(self.to_string()),
}
}
Expand Down

0 comments on commit ead53a0

Please sign in to comment.