mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-25 23:49:58 +00:00
fix: support unary operator in default value, partition rule and prepare statement (#4301)
* handle unary operator Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add sqlness test Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add prepare test Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * add test and context Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * fix rebase error Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * fix merge error Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * fix sqlness Signed-off-by: Ruihang Xia <waynestxia@gmail.com> --------- Signed-off-by: Ruihang Xia <waynestxia@gmail.com> Co-authored-by: dennis zhuang <killme2008@gmail.com>
This commit is contained in:
@@ -121,6 +121,11 @@ impl Decimal128 {
|
||||
let value = (hi | lo) as i128;
|
||||
Self::new(value, precision, scale)
|
||||
}
|
||||
|
||||
pub fn negative(mut self) -> Self {
|
||||
self.value = -self.value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The default value of Decimal128 is 0, and its precision is 1 and scale is 0.
|
||||
|
||||
@@ -159,6 +159,10 @@ impl Date {
|
||||
.checked_sub_days(Days::new(days as u64))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
pub fn negative(&self) -> Self {
|
||||
Self(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -192,6 +192,10 @@ impl DateTime {
|
||||
pub fn to_date(&self) -> Option<Date> {
|
||||
self.to_chrono_datetime().map(|d| Date::from(d.date()))
|
||||
}
|
||||
|
||||
pub fn negative(&self) -> Self {
|
||||
Self(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -92,6 +92,11 @@ impl Duration {
|
||||
pub fn to_std_duration(self) -> std::time::Duration {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn negative(mut self) -> Self {
|
||||
self.value = -self.value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert i64 to Duration Type.
|
||||
|
||||
@@ -281,6 +281,15 @@ impl Interval {
|
||||
pub fn to_i32(&self) -> i32 {
|
||||
self.months
|
||||
}
|
||||
|
||||
pub fn negative(&self) -> Self {
|
||||
Self {
|
||||
months: -self.months,
|
||||
days: -self.days,
|
||||
nsecs: -self.nsecs,
|
||||
unit: self.unit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i128> for Interval {
|
||||
|
||||
@@ -145,6 +145,11 @@ impl Time {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn negative(mut self) -> Self {
|
||||
self.value = -self.value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Time {
|
||||
|
||||
@@ -441,6 +441,11 @@ impl Timestamp {
|
||||
|
||||
ParseTimestampSnafu { raw: s }.fail()
|
||||
}
|
||||
|
||||
pub fn negative(mut self) -> Self {
|
||||
self.value = -self.value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
|
||||
@@ -367,6 +367,56 @@ impl Value {
|
||||
|
||||
Ok(scalar_value)
|
||||
}
|
||||
|
||||
/// Apply `-` unary op if possible
|
||||
pub fn try_negative(&self) -> Option<Self> {
|
||||
match self {
|
||||
Value::Null => Some(Value::Null),
|
||||
Value::UInt8(x) => {
|
||||
if *x == 0 {
|
||||
Some(Value::UInt8(*x))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::UInt16(x) => {
|
||||
if *x == 0 {
|
||||
Some(Value::UInt16(*x))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::UInt32(x) => {
|
||||
if *x == 0 {
|
||||
Some(Value::UInt32(*x))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::UInt64(x) => {
|
||||
if *x == 0 {
|
||||
Some(Value::UInt64(*x))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::Int8(x) => Some(Value::Int8(-*x)),
|
||||
Value::Int16(x) => Some(Value::Int16(-*x)),
|
||||
Value::Int32(x) => Some(Value::Int32(-*x)),
|
||||
Value::Int64(x) => Some(Value::Int64(-*x)),
|
||||
Value::Float32(x) => Some(Value::Float32(-*x)),
|
||||
Value::Float64(x) => Some(Value::Float64(-*x)),
|
||||
Value::Decimal128(x) => Some(Value::Decimal128(x.negative())),
|
||||
Value::Date(x) => Some(Value::Date(x.negative())),
|
||||
Value::DateTime(x) => Some(Value::DateTime(x.negative())),
|
||||
Value::Timestamp(x) => Some(Value::Timestamp(x.negative())),
|
||||
Value::Time(x) => Some(Value::Time(x.negative())),
|
||||
Value::Duration(x) => Some(Value::Duration(x.negative())),
|
||||
Value::Interval(x) => Some(Value::Interval(x.negative())),
|
||||
|
||||
Value::Binary(_) | Value::String(_) | Value::Boolean(_) | Value::List(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryAsPrimitive<T: LogicalPrimitiveType> {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#![feature(assert_matches)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
pub mod delete;
|
||||
pub mod error;
|
||||
|
||||
@@ -202,7 +202,7 @@ fn sql_value_to_grpc_value(
|
||||
column: column.clone(),
|
||||
})?
|
||||
} else {
|
||||
statements::sql_value_to_value(column, &column_schema.data_type, sql_val, timezone)
|
||||
statements::sql_value_to_value(column, &column_schema.data_type, sql_val, timezone, None)
|
||||
.context(ParseSqlSnafu)?
|
||||
};
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ use sql::statements::create::{
|
||||
};
|
||||
use sql::statements::sql_value_to_value;
|
||||
use sql::statements::statement::Statement;
|
||||
use sqlparser::ast::{Expr, Ident, Value as ParserValue};
|
||||
use sqlparser::ast::{Expr, Ident, UnaryOperator, Value as ParserValue};
|
||||
use store_api::metric_engine_consts::{LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME};
|
||||
use substrait::{DFLogicalSubstraitConvertor, SubstraitPlan};
|
||||
use table::dist_table::DistTable;
|
||||
@@ -1329,14 +1329,30 @@ fn convert_one_expr(
|
||||
|
||||
// convert leaf node.
|
||||
let (lhs, op, rhs) = match (left.as_ref(), right.as_ref()) {
|
||||
// col, val
|
||||
(Expr::Identifier(ident), Expr::Value(value)) => {
|
||||
let (column_name, data_type) = convert_identifier(ident, column_name_and_type)?;
|
||||
let value = convert_value(value, data_type, timezone)?;
|
||||
let value = convert_value(value, data_type, timezone, None)?;
|
||||
(Operand::Column(column_name), op, Operand::Value(value))
|
||||
}
|
||||
(Expr::Identifier(ident), Expr::UnaryOp { op: unary_op, expr })
|
||||
if let Expr::Value(v) = &**expr =>
|
||||
{
|
||||
let (column_name, data_type) = convert_identifier(ident, column_name_and_type)?;
|
||||
let value = convert_value(v, data_type, timezone, Some(*unary_op))?;
|
||||
(Operand::Column(column_name), op, Operand::Value(value))
|
||||
}
|
||||
// val, col
|
||||
(Expr::Value(value), Expr::Identifier(ident)) => {
|
||||
let (column_name, data_type) = convert_identifier(ident, column_name_and_type)?;
|
||||
let value = convert_value(value, data_type, timezone)?;
|
||||
let value = convert_value(value, data_type, timezone, None)?;
|
||||
(Operand::Value(value), op, Operand::Column(column_name))
|
||||
}
|
||||
(Expr::UnaryOp { op: unary_op, expr }, Expr::Identifier(ident))
|
||||
if let Expr::Value(v) = &**expr =>
|
||||
{
|
||||
let (column_name, data_type) = convert_identifier(ident, column_name_and_type)?;
|
||||
let value = convert_value(v, data_type, timezone, Some(*unary_op))?;
|
||||
(Operand::Value(value), op, Operand::Column(column_name))
|
||||
}
|
||||
(Expr::BinaryOp { .. }, Expr::BinaryOp { .. }) => {
|
||||
@@ -1372,8 +1388,10 @@ fn convert_value(
|
||||
value: &ParserValue,
|
||||
data_type: ConcreteDataType,
|
||||
timezone: &Timezone,
|
||||
unary_op: Option<UnaryOperator>,
|
||||
) -> Result<Value> {
|
||||
sql_value_to_value("<NONAME>", &data_type, value, Some(timezone)).context(ParseSqlValueSnafu)
|
||||
sql_value_to_value("<NONAME>", &data_type, value, Some(timezone), unary_op)
|
||||
.context(ParseSqlValueSnafu)
|
||||
}
|
||||
|
||||
/// Merge table level table options with schema level table options.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#![feature(try_blocks)]
|
||||
#![feature(exclusive_wrapper)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
use datatypes::schema::Schema;
|
||||
use query::plan::LogicalPlan;
|
||||
|
||||
@@ -205,7 +205,19 @@ pub fn convert_value(param: &ParamValue, t: &ConcreteDataType) -> Result<ScalarV
|
||||
pub fn convert_expr_to_scalar_value(param: &Expr, t: &ConcreteDataType) -> Result<ScalarValue> {
|
||||
match param {
|
||||
Expr::Value(v) => {
|
||||
let v = sql_value_to_value("", t, v, None);
|
||||
let v = sql_value_to_value("", t, v, None, None);
|
||||
match v {
|
||||
Ok(v) => v
|
||||
.try_to_scalar_value(t)
|
||||
.context(error::ConvertScalarValueSnafu),
|
||||
Err(e) => error::InvalidParameterSnafu {
|
||||
reason: e.to_string(),
|
||||
}
|
||||
.fail(),
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { op, expr } if let Expr::Value(v) = &**expr => {
|
||||
let v = sql_value_to_value("", t, v, None, Some(*op));
|
||||
match v {
|
||||
Ok(v) => v
|
||||
.try_to_scalar_value(t)
|
||||
|
||||
@@ -20,6 +20,7 @@ use common_macro::stack_trace_debug;
|
||||
use common_time::timestamp::TimeUnit;
|
||||
use common_time::Timestamp;
|
||||
use datafusion_common::DataFusionError;
|
||||
use datafusion_sql::sqlparser::ast::UnaryOperator;
|
||||
use datatypes::prelude::{ConcreteDataType, Value};
|
||||
use snafu::{Location, Snafu};
|
||||
use sqlparser::ast::Ident;
|
||||
@@ -161,6 +162,21 @@ pub enum Error {
|
||||
source: datatypes::error::Error,
|
||||
},
|
||||
|
||||
#[snafu(display("Invalid unary operator {} for value {}", unary_op, value))]
|
||||
InvalidUnaryOp {
|
||||
unary_op: UnaryOperator,
|
||||
value: Value,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Unsupported unary operator {}", unary_op))]
|
||||
UnsupportedUnaryOp {
|
||||
unary_op: UnaryOperator,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Unrecognized table option key: {}", key))]
|
||||
InvalidTableOption {
|
||||
key: String,
|
||||
@@ -299,7 +315,8 @@ impl ErrorExt for Error {
|
||||
| ConvertToLogicalExpression { .. }
|
||||
| Simplification { .. }
|
||||
| InvalidInterval { .. }
|
||||
| PermissionDenied { .. }
|
||||
| InvalidUnaryOp { .. }
|
||||
| UnsupportedUnaryOp { .. }
|
||||
| FulltextInvalidOption { .. } => StatusCode::InvalidArguments,
|
||||
|
||||
SerializeColumnDefaultConstraint { source, .. } => source.status_code(),
|
||||
@@ -307,6 +324,7 @@ impl ErrorExt for Error {
|
||||
ConvertToDfStatement { .. } => StatusCode::Internal,
|
||||
ConvertSqlValue { .. } | ConvertValue { .. } => StatusCode::Unsupported,
|
||||
|
||||
PermissionDenied { .. } => StatusCode::PermissionDenied,
|
||||
SetFulltextOption { .. } => StatusCode::Unexpected,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,6 +940,10 @@ fn ensure_one_expr(expr: &Expr, columns: &[&Column]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
Expr::Value(_) => Ok(()),
|
||||
Expr::UnaryOp { expr, .. } => {
|
||||
ensure_one_expr(expr, columns)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => error::InvalidSqlSnafu {
|
||||
msg: format!("Partition rule expr {:?} is not a binary expr!", expr),
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ use crate::ast::{
|
||||
};
|
||||
use crate::error::{
|
||||
self, ColumnTypeMismatchSnafu, ConvertSqlValueSnafu, ConvertToGrpcDataTypeSnafu,
|
||||
ConvertValueSnafu, InvalidCastSnafu, InvalidSqlValueSnafu, ParseSqlValueSnafu, Result,
|
||||
SerializeColumnDefaultConstraintSnafu, SetFulltextOptionSnafu, TimestampOverflowSnafu,
|
||||
UnsupportedDefaultValueSnafu,
|
||||
ConvertValueSnafu, InvalidCastSnafu, InvalidSqlValueSnafu, InvalidUnaryOpSnafu,
|
||||
ParseSqlValueSnafu, Result, SerializeColumnDefaultConstraintSnafu, SetFulltextOptionSnafu,
|
||||
TimestampOverflowSnafu, UnsupportedDefaultValueSnafu, UnsupportedUnaryOpSnafu,
|
||||
};
|
||||
use crate::statements::create::Column;
|
||||
pub use crate::statements::option_map::OptionMap;
|
||||
@@ -229,8 +229,9 @@ pub fn sql_value_to_value(
|
||||
data_type: &ConcreteDataType,
|
||||
sql_val: &SqlValue,
|
||||
timezone: Option<&Timezone>,
|
||||
unary_op: Option<UnaryOperator>,
|
||||
) -> Result<Value> {
|
||||
let value = match sql_val {
|
||||
let mut value = match sql_val {
|
||||
SqlValue::Number(n, _) => sql_number_to_value(data_type, n)?,
|
||||
SqlValue::Null => Value::Null,
|
||||
SqlValue::Boolean(b) => {
|
||||
@@ -260,6 +261,60 @@ pub fn sql_value_to_value(
|
||||
.fail()
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(unary_op) = unary_op {
|
||||
match unary_op {
|
||||
UnaryOperator::Plus | UnaryOperator::Minus | UnaryOperator::Not => {}
|
||||
UnaryOperator::PGBitwiseNot
|
||||
| UnaryOperator::PGSquareRoot
|
||||
| UnaryOperator::PGCubeRoot
|
||||
| UnaryOperator::PGPostfixFactorial
|
||||
| UnaryOperator::PGPrefixFactorial
|
||||
| UnaryOperator::PGAbs => {
|
||||
return UnsupportedUnaryOpSnafu { unary_op }.fail();
|
||||
}
|
||||
}
|
||||
|
||||
match value {
|
||||
Value::Null => {}
|
||||
Value::Boolean(bool) => match unary_op {
|
||||
UnaryOperator::Not => value = Value::Boolean(!bool),
|
||||
_ => {
|
||||
return InvalidUnaryOpSnafu { unary_op, value }.fail();
|
||||
}
|
||||
},
|
||||
Value::UInt8(_)
|
||||
| Value::UInt16(_)
|
||||
| Value::UInt32(_)
|
||||
| Value::UInt64(_)
|
||||
| Value::Int8(_)
|
||||
| Value::Int16(_)
|
||||
| Value::Int32(_)
|
||||
| Value::Int64(_)
|
||||
| Value::Float32(_)
|
||||
| Value::Float64(_)
|
||||
| Value::Decimal128(_)
|
||||
| Value::Date(_)
|
||||
| Value::DateTime(_)
|
||||
| Value::Timestamp(_)
|
||||
| Value::Time(_)
|
||||
| Value::Duration(_)
|
||||
| Value::Interval(_) => match unary_op {
|
||||
UnaryOperator::Plus => {}
|
||||
UnaryOperator::Minus => {
|
||||
value = value
|
||||
.try_negative()
|
||||
.with_context(|| InvalidUnaryOpSnafu { unary_op, value })?;
|
||||
}
|
||||
_ => return InvalidUnaryOpSnafu { unary_op, value }.fail(),
|
||||
},
|
||||
|
||||
Value::String(_) | Value::Binary(_) | Value::List(_) => {
|
||||
return InvalidUnaryOpSnafu { unary_op, value }.fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if value.data_type() != *data_type {
|
||||
cast(value, data_type).with_context(|_| InvalidCastSnafu {
|
||||
sql_value: sql_val.clone(),
|
||||
@@ -305,7 +360,7 @@ fn parse_column_default_constraint(
|
||||
{
|
||||
let default_constraint = match &opt.option {
|
||||
ColumnOption::Default(Expr::Value(v)) => ColumnDefaultConstraint::Value(
|
||||
sql_value_to_value(column_name, data_type, v, timezone)?,
|
||||
sql_value_to_value(column_name, data_type, v, timezone, None)?,
|
||||
),
|
||||
ColumnOption::Default(Expr::Function(func)) => {
|
||||
let mut func = format!("{func}").to_lowercase();
|
||||
@@ -316,20 +371,22 @@ fn parse_column_default_constraint(
|
||||
// Always use lowercase for function expression
|
||||
ColumnDefaultConstraint::Function(func.to_lowercase())
|
||||
}
|
||||
ColumnOption::Default(expr) => {
|
||||
if let Expr::UnaryOp { op, expr } = expr {
|
||||
if let (UnaryOperator::Minus, Expr::Value(SqlValue::Number(n, _))) =
|
||||
(op, expr.as_ref())
|
||||
{
|
||||
return Ok(Some(ColumnDefaultConstraint::Value(sql_number_to_value(
|
||||
data_type,
|
||||
&format!("-{n}"),
|
||||
)?)));
|
||||
ColumnOption::Default(Expr::UnaryOp { op, expr }) => {
|
||||
if let Expr::Value(v) = &**expr {
|
||||
let value = sql_value_to_value(column_name, data_type, v, timezone, Some(*op))?;
|
||||
ColumnDefaultConstraint::Value(value)
|
||||
} else {
|
||||
return UnsupportedDefaultValueSnafu {
|
||||
column_name,
|
||||
expr: *expr.clone(),
|
||||
}
|
||||
.fail();
|
||||
}
|
||||
}
|
||||
ColumnOption::Default(others) => {
|
||||
return UnsupportedDefaultValueSnafu {
|
||||
column_name,
|
||||
expr: expr.clone(),
|
||||
expr: others.clone(),
|
||||
}
|
||||
.fail();
|
||||
}
|
||||
@@ -689,28 +746,61 @@ mod tests {
|
||||
let sql_val = SqlValue::Null;
|
||||
assert_eq!(
|
||||
Value::Null,
|
||||
sql_value_to_value("a", &ConcreteDataType::float64_datatype(), &sql_val, None).unwrap()
|
||||
sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::float64_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::Boolean(true);
|
||||
assert_eq!(
|
||||
Value::Boolean(true),
|
||||
sql_value_to_value("a", &ConcreteDataType::boolean_datatype(), &sql_val, None).unwrap()
|
||||
sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::boolean_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::Number("3.0".to_string(), false);
|
||||
assert_eq!(
|
||||
Value::Float64(OrderedFloat(3.0)),
|
||||
sql_value_to_value("a", &ConcreteDataType::float64_datatype(), &sql_val, None).unwrap()
|
||||
sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::float64_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::Number("3.0".to_string(), false);
|
||||
let v = sql_value_to_value("a", &ConcreteDataType::boolean_datatype(), &sql_val, None);
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::boolean_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(v.is_err());
|
||||
assert!(format!("{v:?}").contains("Failed to parse number '3.0' to boolean column type"));
|
||||
|
||||
let sql_val = SqlValue::Boolean(true);
|
||||
let v = sql_value_to_value("a", &ConcreteDataType::float64_datatype(), &sql_val, None);
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::float64_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(v.is_err());
|
||||
assert!(
|
||||
format!("{v:?}").contains(
|
||||
@@ -720,20 +810,38 @@ mod tests {
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::HexStringLiteral("48656c6c6f20776f726c6421".to_string());
|
||||
let v =
|
||||
sql_value_to_value("a", &ConcreteDataType::binary_datatype(), &sql_val, None).unwrap();
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::binary_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(Value::Binary(Bytes::from(b"Hello world!".as_slice())), v);
|
||||
|
||||
let sql_val = SqlValue::DoubleQuotedString("MorningMyFriends".to_string());
|
||||
let v =
|
||||
sql_value_to_value("a", &ConcreteDataType::binary_datatype(), &sql_val, None).unwrap();
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::binary_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
Value::Binary(Bytes::from(b"MorningMyFriends".as_slice())),
|
||||
v
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::HexStringLiteral("9AF".to_string());
|
||||
let v = sql_value_to_value("a", &ConcreteDataType::binary_datatype(), &sql_val, None);
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::binary_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(v.is_err());
|
||||
assert!(
|
||||
format!("{v:?}").contains("odd number of digits"),
|
||||
@@ -741,7 +849,13 @@ mod tests {
|
||||
);
|
||||
|
||||
let sql_val = SqlValue::HexStringLiteral("AG".to_string());
|
||||
let v = sql_value_to_value("a", &ConcreteDataType::binary_datatype(), &sql_val, None);
|
||||
let v = sql_value_to_value(
|
||||
"a",
|
||||
&ConcreteDataType::binary_datatype(),
|
||||
&sql_val,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert!(v.is_err());
|
||||
assert!(format!("{v:?}").contains("invalid character"), "v is {v:?}",);
|
||||
}
|
||||
@@ -753,6 +867,7 @@ mod tests {
|
||||
&ConcreteDataType::date_datatype(),
|
||||
&SqlValue::DoubleQuotedString("2022-02-22".to_string()),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(ConcreteDataType::date_datatype(), value.data_type());
|
||||
@@ -768,6 +883,7 @@ mod tests {
|
||||
&ConcreteDataType::date_datatype(),
|
||||
&SqlValue::DoubleQuotedString("2022-02-22".to_string()),
|
||||
Some(&Timezone::from_tz_string("+07:00").unwrap()),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(ConcreteDataType::date_datatype(), value.data_type());
|
||||
@@ -786,6 +902,7 @@ mod tests {
|
||||
&ConcreteDataType::datetime_datatype(),
|
||||
&SqlValue::DoubleQuotedString("2022-02-22 00:01:03+0800".to_string()),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(ConcreteDataType::datetime_datatype(), value.data_type());
|
||||
@@ -803,6 +920,7 @@ mod tests {
|
||||
&ConcreteDataType::datetime_datatype(),
|
||||
&SqlValue::DoubleQuotedString("2022-02-22 00:01:61".to_string()),
|
||||
None,
|
||||
None
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
@@ -1247,7 +1365,32 @@ mod tests {
|
||||
&ConcreteDataType::string_datatype(),
|
||||
&SqlValue::Placeholder("default".into()),
|
||||
None,
|
||||
None
|
||||
)
|
||||
.is_err());
|
||||
assert!(sql_value_to_value(
|
||||
"test",
|
||||
&ConcreteDataType::string_datatype(),
|
||||
&SqlValue::Placeholder("default".into()),
|
||||
None,
|
||||
Some(UnaryOperator::Minus),
|
||||
)
|
||||
.is_err());
|
||||
assert!(sql_value_to_value(
|
||||
"test",
|
||||
&ConcreteDataType::uint16_datatype(),
|
||||
&SqlValue::Number("3".into(), false),
|
||||
None,
|
||||
Some(UnaryOperator::Minus),
|
||||
)
|
||||
.is_err());
|
||||
assert!(sql_value_to_value(
|
||||
"test",
|
||||
&ConcreteDataType::uint16_datatype(),
|
||||
&SqlValue::Number("3".into(), false),
|
||||
None,
|
||||
None
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,18 +240,23 @@ pub async fn test_mysql_crud(store_type: StorageType) {
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query("insert into demo(i) values(?)")
|
||||
.bind(-99)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let rows = sqlx::query("select * from demo")
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rows.len(), 1);
|
||||
assert_eq!(rows.len(), 2);
|
||||
|
||||
for row in rows {
|
||||
let i: i64 = row.get("i");
|
||||
let ts: DateTime<Utc> = row.get("ts");
|
||||
let now = common_time::util::current_time_millis();
|
||||
assert!(now - ts.timestamp_millis() < 1000);
|
||||
assert_eq!(i, 99);
|
||||
assert_eq!(i.abs(), 99);
|
||||
}
|
||||
|
||||
let _ = fe_mysql_server.shutdown().await;
|
||||
|
||||
@@ -64,3 +64,28 @@ DROP TABLE test2;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
CREATE TABLE test3 (
|
||||
i INTEGER DEFAULT -1,
|
||||
j DOUBLE DEFAULT -2,
|
||||
k TIMESTAMP DEFAULT -3,
|
||||
ts TIMESTAMP TIME INDEX,
|
||||
);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO test3 (ts) VALUES (1);
|
||||
|
||||
Affected Rows: 1
|
||||
|
||||
SELECT * FROM test3;
|
||||
|
||||
+----+------+-------------------------+-------------------------+
|
||||
| i | j | k | ts |
|
||||
+----+------+-------------------------+-------------------------+
|
||||
| -1 | -2.0 | 1969-12-31T23:59:59.997 | 1970-01-01T00:00:00.001 |
|
||||
+----+------+-------------------------+-------------------------+
|
||||
|
||||
DROP TABLE test3;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
|
||||
@@ -20,3 +20,16 @@ SELECT * FROM test2;
|
||||
|
||||
DROP TABLE test1;
|
||||
DROP TABLE test2;
|
||||
|
||||
CREATE TABLE test3 (
|
||||
i INTEGER DEFAULT -1,
|
||||
j DOUBLE DEFAULT -2,
|
||||
k TIMESTAMP DEFAULT -3,
|
||||
ts TIMESTAMP TIME INDEX,
|
||||
);
|
||||
|
||||
INSERT INTO test3 (ts) VALUES (1);
|
||||
|
||||
SELECT * FROM test3;
|
||||
|
||||
DROP TABLE test3;
|
||||
|
||||
@@ -185,3 +185,40 @@ PARTITION ON COLUMNS (a) (
|
||||
|
||||
Error: 1004(InvalidArguments), Unclosed value Int32(10) on column a
|
||||
|
||||
-- Issue https://github.com/GreptimeTeam/greptimedb/issues/4247
|
||||
-- Partition rule with unary operator
|
||||
CREATE TABLE `molestiAe` (
|
||||
`sImiLiQUE` FLOAT NOT NULL,
|
||||
`amEt` TIMESTAMP(6) TIME INDEX,
|
||||
`EXpLICaBo` DOUBLE,
|
||||
PRIMARY KEY (`sImiLiQUE`)
|
||||
) PARTITION ON COLUMNS (`sImiLiQUE`) (
|
||||
`sImiLiQUE` < -1,
|
||||
`sImiLiQUE` >= -1 AND `sImiLiQUE` < -0,
|
||||
`sImiLiQUE` >= 0
|
||||
);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
INSERT INTO `molestiAe` VALUES
|
||||
(-2, 0, 0),
|
||||
(-0.9, 0, 0),
|
||||
(1, 0, 0);
|
||||
|
||||
Affected Rows: 3
|
||||
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
SELECT * FROM `molestiAe`;
|
||||
|
||||
+-----------+---------------------+-----------+
|
||||
| sImiLiQUE | amEt | EXpLICaBo |
|
||||
+-----------+---------------------+-----------+
|
||||
| -0.9 | 1970-01-01T00:00:00 | 0.0 |
|
||||
| -2.0 | 1970-01-01T00:00:00 | 0.0 |
|
||||
| 1.0 | 1970-01-01T00:00:00 | 0.0 |
|
||||
+-----------+---------------------+-----------+
|
||||
|
||||
DROP TABLE `molestiAe`;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
|
||||
@@ -85,3 +85,26 @@ PARTITION ON COLUMNS (a) (
|
||||
a > 10 AND a < 20,
|
||||
a >= 20
|
||||
);
|
||||
|
||||
-- Issue https://github.com/GreptimeTeam/greptimedb/issues/4247
|
||||
-- Partition rule with unary operator
|
||||
CREATE TABLE `molestiAe` (
|
||||
`sImiLiQUE` FLOAT NOT NULL,
|
||||
`amEt` TIMESTAMP(6) TIME INDEX,
|
||||
`EXpLICaBo` DOUBLE,
|
||||
PRIMARY KEY (`sImiLiQUE`)
|
||||
) PARTITION ON COLUMNS (`sImiLiQUE`) (
|
||||
`sImiLiQUE` < -1,
|
||||
`sImiLiQUE` >= -1 AND `sImiLiQUE` < -0,
|
||||
`sImiLiQUE` >= 0
|
||||
);
|
||||
|
||||
INSERT INTO `molestiAe` VALUES
|
||||
(-2, 0, 0),
|
||||
(-0.9, 0, 0),
|
||||
(1, 0, 0);
|
||||
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
SELECT * FROM `molestiAe`;
|
||||
|
||||
DROP TABLE `molestiAe`;
|
||||
|
||||
Reference in New Issue
Block a user