mod binary;
mod comparison;
#[allow(dead_code)]
mod constants;
mod jcodec;
mod modifier;
mod path_expr;
mod serde;
mod json_depth;
mod json_extract;
mod json_keys;
mod json_length;
mod json_merge;
mod json_modify;
mod json_remove;
mod json_type;
mod json_unquote;
pub use self::jcodec::{JsonDatumPayloadChunkEncoder, JsonDecoder, JsonEncoder};
pub use self::json_modify::ModifyType;
pub use self::path_expr::{parse_json_path_expr, PathExpression};
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::str;
use tikv_util::is_even;
use super::super::datum::Datum;
use super::super::{Error, Result};
use crate::codec::convert::ConvertTo;
use crate::codec::data_type::{Decimal, Real};
use crate::codec::mysql;
use crate::codec::mysql::{Duration, Time, TimeType};
use crate::expr::EvalContext;
use codec::number::{NumberCodec, F64_SIZE, I64_SIZE};
use constants::{JSON_LITERAL_FALSE, JSON_LITERAL_NIL, JSON_LITERAL_TRUE};
const ERR_CONVERT_FAILED: &str = "Can not covert from ";
#[derive(Eq, PartialEq, FromPrimitive, Clone, Debug, Copy)]
pub enum JsonType {
Object = 0x01,
Array = 0x03,
Literal = 0x04,
I64 = 0x09,
U64 = 0x0a,
Double = 0x0b,
String = 0x0c,
}
impl TryFrom<u8> for JsonType {
type Error = Error;
fn try_from(src: u8) -> Result<JsonType> {
num_traits::FromPrimitive::from_u8(src)
.ok_or_else(|| Error::InvalidDataType("unexpected JSON type".to_owned()))
}
}
#[derive(Clone, Copy, Debug)]
pub struct JsonRef<'a> {
type_code: JsonType,
value: &'a [u8],
}
impl<'a> JsonRef<'a> {
pub fn new(type_code: JsonType, value: &[u8]) -> JsonRef<'_> {
JsonRef { type_code, value }
}
pub fn to_owned(&self) -> Json {
Json {
type_code: self.type_code,
value: self.value.to_owned(),
}
}
pub fn get_type(&self) -> JsonType {
self.type_code
}
pub fn value(&self) -> &'a [u8] {
&self.value
}
pub(crate) fn get_u64(&self) -> u64 {
assert_eq!(self.type_code, JsonType::U64);
NumberCodec::decode_u64_le(self.value())
}
pub(crate) fn get_i64(&self) -> i64 {
assert_eq!(self.type_code, JsonType::I64);
NumberCodec::decode_i64_le(self.value())
}
pub(crate) fn get_double(&self) -> f64 {
assert_eq!(self.type_code, JsonType::Double);
NumberCodec::decode_f64_le(self.value())
}
pub(crate) fn get_elem_count(&self) -> usize {
assert!((self.type_code == JsonType::Object) | (self.type_code == JsonType::Array));
NumberCodec::decode_u32_le(self.value()) as usize
}
pub(crate) fn get_literal(&self) -> Option<bool> {
assert_eq!(self.type_code, JsonType::Literal);
match self.value()[0] {
JSON_LITERAL_FALSE => Some(false),
JSON_LITERAL_TRUE => Some(true),
_ => None,
}
}
pub(crate) fn get_str_bytes(&self) -> Result<&'a [u8]> {
assert_eq!(self.type_code, JsonType::String);
let val = self.value();
let (str_len, len_len) = NumberCodec::try_decode_var_u64(val)?;
Ok(&val[len_len..len_len + str_len as usize])
}
pub(crate) fn get_str(&self) -> Result<&'a str> {
Ok(str::from_utf8(self.get_str_bytes()?)?)
}
pub(crate) fn is_zero(&self) -> bool {
match self.get_type() {
JsonType::Object => false,
JsonType::Array => false,
JsonType::Literal => false,
JsonType::I64 => self.get_i64() == 0,
JsonType::U64 => self.get_u64() == 0,
JsonType::Double => self.get_double() == 0f64,
JsonType::String => false,
}
}
}
#[derive(Clone, Debug)]
pub struct Json {
type_code: JsonType,
pub value: Vec<u8>,
}
use std::fmt::{Display, Formatter};
impl Display for Json {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let s = serde_json::to_string(&self.as_ref()).unwrap();
write!(f, "{}", s)
}
}
impl Json {
pub fn new(tp: JsonType, value: Vec<u8>) -> Self {
Self {
type_code: tp,
value,
}
}
pub fn get_type(&self) -> JsonType {
self.type_code
}
pub fn from_string(s: String) -> Result<Self> {
let mut value = vec![];
value.write_json_str(s.as_str())?;
Ok(Self::new(JsonType::String, value))
}
pub fn from_str_val(s: &str) -> Result<Self> {
let mut value = vec![];
value.write_json_str(s)?;
Ok(Self::new(JsonType::String, value))
}
pub fn from_bool(b: bool) -> Result<Self> {
let mut value = vec![];
value.write_json_literal(if b {
JSON_LITERAL_TRUE
} else {
JSON_LITERAL_FALSE
})?;
Ok(Self::new(JsonType::Literal, value))
}
pub fn from_u64(v: u64) -> Result<Self> {
let mut value = vec![];
value.write_json_u64(v)?;
Ok(Self::new(JsonType::U64, value))
}
pub fn from_f64(v: f64) -> Result<Self> {
let mut value = vec![];
value.write_json_f64(v)?;
Ok(Self::new(JsonType::Double, value))
}
pub fn from_i64(v: i64) -> Result<Self> {
let mut value = vec![];
value.write_json_i64(v)?;
Ok(Self::new(JsonType::I64, value))
}
pub fn from_ref_array(array: Vec<JsonRef<'_>>) -> Result<Self> {
let mut value = vec![];
value.write_json_ref_array(&array)?;
Ok(Self::new(JsonType::Array, value))
}
pub fn from_array(array: Vec<Json>) -> Result<Self> {
let mut value = vec![];
value.write_json_array(&array)?;
Ok(Self::new(JsonType::Array, value))
}
pub fn from_kv_pairs(entries: Vec<(&[u8], JsonRef)>) -> Result<Self> {
let mut value = vec![];
value.write_json_obj_from_keys_values(entries)?;
Ok(Self::new(JsonType::Object, value))
}
pub fn from_object(map: BTreeMap<String, Json>) -> Result<Self> {
let mut value = vec![];
value.write_json_obj(&map)?;
Ok(Self::new(JsonType::Object, value))
}
pub fn none() -> Result<Self> {
let mut value = vec![];
value.write_json_literal(JSON_LITERAL_NIL)?;
Ok(Self::new(JsonType::Literal, value))
}
pub fn as_ref(&self) -> JsonRef<'_> {
JsonRef {
type_code: self.type_code,
value: self.value.as_slice(),
}
}
}
pub fn json_array(elems: Vec<Datum>) -> Result<Json> {
let mut a = Vec::with_capacity(elems.len());
for elem in elems {
a.push(elem.into_json()?);
}
Json::from_array(a)
}
pub fn json_object(kvs: Vec<Datum>) -> Result<Json> {
let len = kvs.len();
if !is_even(len) {
return Err(Error::Other(box_err!(
"Incorrect parameter count in the call to native \
function 'JSON_OBJECT'"
)));
}
let mut map = BTreeMap::new();
let mut key = None;
for elem in kvs {
if key.is_none() {
if elem == Datum::Null {
return Err(invalid_type!(
"JSON documents may not contain NULL member names"
));
}
key = Some(elem.into_string()?);
} else {
let val = elem.into_json()?;
map.insert(key.take().unwrap(), val);
}
}
Json::from_object(map)
}
impl ConvertTo<f64> for Json {
#[inline]
fn convert(&self, ctx: &mut EvalContext) -> Result<f64> {
self.as_ref().convert(ctx)
}
}
impl<'a> ConvertTo<f64> for JsonRef<'a> {
#[inline]
fn convert(&self, ctx: &mut EvalContext) -> Result<f64> {
let d = match self.get_type() {
JsonType::Array | JsonType::Object => ctx
.handle_truncate_err(Error::truncated_wrong_val("Float", self.to_string()))
.map(|_| 0f64)?,
JsonType::U64 => self.get_u64() as f64,
JsonType::I64 => self.get_i64() as f64,
JsonType::Double => self.get_double(),
JsonType::Literal => self
.get_literal()
.map_or(0f64, |x| if x { 1f64 } else { 0f64 }),
JsonType::String => self.get_str_bytes()?.convert(ctx)?,
};
Ok(d)
}
}
impl ConvertTo<Json> for i64 {
#[inline]
fn convert(&self, _: &mut EvalContext) -> Result<Json> {
let mut value = vec![0; I64_SIZE];
NumberCodec::encode_i64_le(&mut value, *self);
Ok(Json {
type_code: JsonType::I64,
value,
})
}
}
impl ConvertTo<Json> for f64 {
#[inline]
fn convert(&self, _: &mut EvalContext) -> Result<Json> {
let mut value = vec![0; F64_SIZE];
NumberCodec::encode_f64_le(&mut value, *self);
Ok(Json {
type_code: JsonType::Double,
value,
})
}
}
impl ConvertTo<Json> for Real {
#[inline]
fn convert(&self, _: &mut EvalContext) -> Result<Json> {
let mut value = vec![0; F64_SIZE];
NumberCodec::encode_f64_le(&mut value, self.into_inner());
Ok(Json {
type_code: JsonType::Double,
value,
})
}
}
impl ConvertTo<Json> for Decimal {
#[inline]
fn convert(&self, ctx: &mut EvalContext) -> Result<Json> {
let val: f64 = self.convert(ctx)?;
val.convert(ctx)
}
}
impl ConvertTo<Json> for Time {
#[inline]
fn convert(&self, ctx: &mut EvalContext) -> Result<Json> {
let tp = self.get_time_type();
let s = if tp == TimeType::DateTime || tp == TimeType::Timestamp {
self.round_frac(ctx, mysql::MAX_FSP)?
} else {
*self
};
Json::from_string(s.to_string())
}
}
impl ConvertTo<Json> for Duration {
#[inline]
fn convert(&self, _: &mut EvalContext) -> Result<Json> {
let d = self.maximize_fsp();
Json::from_string(d.to_string())
}
}
impl crate::codec::data_type::AsMySQLBool for Json {
#[inline]
fn as_mysql_bool(&self, _context: &mut crate::expr::EvalContext) -> crate::codec::Result<bool> {
Ok(false)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use crate::codec::error::ERR_TRUNCATE_WRONG_VALUE;
use crate::expr::{EvalConfig, EvalContext};
#[test]
fn test_json_array() {
let cases = vec![
(
vec![
Datum::I64(1),
Datum::Bytes(b"sdf".to_vec()),
Datum::U64(2),
Datum::Json(r#"[3,4]"#.parse().unwrap()),
],
r#"[1,"sdf",2,[3,4]]"#.parse().unwrap(),
),
(vec![], "[]".parse().unwrap()),
];
for (d, ep_json) in cases {
assert_eq!(json_array(d).unwrap(), ep_json);
}
}
#[test]
fn test_json_object() {
let cases = vec![
vec![Datum::I64(1)],
vec![
Datum::I64(1),
Datum::Bytes(b"sdf".to_vec()),
Datum::Null,
Datum::U64(2),
],
];
for d in cases {
assert!(json_object(d).is_err());
}
let cases = vec![
(
vec![
Datum::I64(1),
Datum::Bytes(b"sdf".to_vec()),
Datum::Bytes(b"asd".to_vec()),
Datum::Bytes(b"qwe".to_vec()),
Datum::I64(2),
Datum::Json(r#"{"3":4}"#.parse().unwrap()),
],
r#"{"1":"sdf","2":{"3":4},"asd":"qwe"}"#.parse().unwrap(),
),
(vec![], "{}".parse().unwrap()),
];
for (d, ep_json) in cases {
assert_eq!(json_object(d).unwrap(), ep_json);
}
}
#[test]
fn test_cast_to_real() {
let test_cases = vec![
("{}", 0f64),
("[]", 0f64),
("3", 3f64),
("-3", -3f64),
("4.5", 4.5),
("true", 1f64),
("false", 0f64),
("null", 0f64),
(r#""hello""#, 0f64),
(r#""1234""#, 1234f64),
];
let mut ctx = EvalContext::new(Arc::new(EvalConfig::default_for_test()));
for (jstr, exp) in test_cases {
let json: Json = jstr.parse().unwrap();
let get: f64 = json.convert(&mut ctx).unwrap();
assert!(
(get - exp).abs() < std::f64::EPSILON,
"json.as_f64 get: {}, exp: {}",
get,
exp
);
}
}
#[test]
fn test_cast_err_when_json_array_or_object_to_real() {
let test_cases = vec![
("{}", ERR_TRUNCATE_WRONG_VALUE),
("[]", ERR_TRUNCATE_WRONG_VALUE),
];
let mut ctx = EvalContext::new(Arc::new(EvalConfig::new()));
for (jstr, exp) in test_cases {
let json: Json = jstr.parse().unwrap();
let result: Result<f64> = json.convert(&mut ctx);
let err = result.unwrap_err();
assert_eq!(
err.code(),
exp,
"json.as_f64 get: {}, exp: {}",
err.code(),
exp
);
}
}
}