mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 00:24:21 +01:00
feat: add CAST to fuzzer
This commit is contained in:
@@ -500,7 +500,7 @@ pub fn str_to_i64(input: impl AsRef<str>) -> Option<i64> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum StrToF64 {
|
pub enum StrToF64 {
|
||||||
Fractional(NonNan),
|
Fractional(NonNan),
|
||||||
Decimal(NonNan),
|
Decimal(NonNan),
|
||||||
|
|||||||
@@ -1318,7 +1318,8 @@ impl From<&ColumnDefinition> for Column {
|
|||||||
///
|
///
|
||||||
/// Note that the order of the rules for determining column affinity is important. A column whose declared type is "CHARINT" will match both rules 1 and 2 but the first rule takes precedence and so the column affinity will be INTEGER.
|
/// Note that the order of the rules for determining column affinity is important. A column whose declared type is "CHARINT" will match both rules 1 and 2 but the first rule takes precedence and so the column affinity will be INTEGER.
|
||||||
pub fn affinity(datatype: &str) -> Affinity {
|
pub fn affinity(datatype: &str) -> Affinity {
|
||||||
// Note: callers of this function must ensure that the datatype is uppercase.
|
let datatype = datatype.to_ascii_uppercase();
|
||||||
|
|
||||||
// Rule 1: INT -> INTEGER affinity
|
// Rule 1: INT -> INTEGER affinity
|
||||||
if datatype.contains("INT") {
|
if datatype.contains("INT") {
|
||||||
return Affinity::Integer;
|
return Affinity::Integer;
|
||||||
|
|||||||
@@ -662,7 +662,7 @@ pub fn translate_expr(
|
|||||||
ast::Expr::Cast { expr, type_name } => {
|
ast::Expr::Cast { expr, type_name } => {
|
||||||
let type_name = type_name.as_ref().unwrap(); // TODO: why is this optional?
|
let type_name = type_name.as_ref().unwrap(); // TODO: why is this optional?
|
||||||
translate_expr(program, referenced_tables, expr, target_register, resolver)?;
|
translate_expr(program, referenced_tables, expr, target_register, resolver)?;
|
||||||
let type_affinity = affinity(&type_name.name.to_uppercase());
|
let type_affinity = affinity(&type_name.name);
|
||||||
program.emit_insn(Insn::Cast {
|
program.emit_insn(Insn::Cast {
|
||||||
reg: target_register,
|
reg: target_register,
|
||||||
affinity: type_affinity,
|
affinity: type_affinity,
|
||||||
@@ -3433,6 +3433,9 @@ pub fn get_expr_affinity(
|
|||||||
Affinity::Blob
|
Affinity::Blob
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ast::Expr::Parenthesized(exprs) if exprs.len() == 1 => {
|
||||||
|
get_expr_affinity(exprs.first().unwrap(), referenced_tables)
|
||||||
|
}
|
||||||
ast::Expr::Collate(expr, _) => get_expr_affinity(expr, referenced_tables),
|
ast::Expr::Collate(expr, _) => get_expr_affinity(expr, referenced_tables),
|
||||||
// Literals have NO affinity in SQLite!
|
// Literals have NO affinity in SQLite!
|
||||||
ast::Expr::Literal(_) => Affinity::Blob, // No affinity!
|
ast::Expr::Literal(_) => Affinity::Blob, // No affinity!
|
||||||
|
|||||||
@@ -55,10 +55,7 @@ use crate::{
|
|||||||
AggContext, Cursor, ExternalAggState, IOResult, SeekKey, SeekOp, SumAggState, Value,
|
AggContext, Cursor, ExternalAggState, IOResult, SeekKey, SeekOp, SumAggState, Value,
|
||||||
ValueType,
|
ValueType,
|
||||||
},
|
},
|
||||||
util::{
|
util::{cast_real_to_integer, checked_cast_text_to_numeric, parse_schema_rows},
|
||||||
cast_real_to_integer, cast_text_to_integer, cast_text_to_numeric, cast_text_to_real,
|
|
||||||
checked_cast_text_to_numeric, parse_schema_rows,
|
|
||||||
},
|
|
||||||
vdbe::{
|
vdbe::{
|
||||||
builder::CursorType,
|
builder::CursorType,
|
||||||
insn::{IdxInsertFlags, Insn},
|
insn::{IdxInsertFlags, Insn},
|
||||||
@@ -8180,11 +8177,16 @@ impl Value {
|
|||||||
}
|
}
|
||||||
Affinity::Real => match self {
|
Affinity::Real => match self {
|
||||||
Value::Blob(b) => {
|
Value::Blob(b) => {
|
||||||
// Convert BLOB to TEXT first
|
|
||||||
let text = String::from_utf8_lossy(b);
|
let text = String::from_utf8_lossy(b);
|
||||||
cast_text_to_real(&text)
|
Value::Float(
|
||||||
|
crate::numeric::str_to_f64(&text)
|
||||||
|
.map(f64::from)
|
||||||
|
.unwrap_or(0.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Value::Text(t) => {
|
||||||
|
Value::Float(crate::numeric::str_to_f64(t).map(f64::from).unwrap_or(0.0))
|
||||||
}
|
}
|
||||||
Value::Text(t) => cast_text_to_real(t.as_str()),
|
|
||||||
Value::Integer(i) => Value::Float(*i as f64),
|
Value::Integer(i) => Value::Float(*i as f64),
|
||||||
Value::Float(f) => Value::Float(*f),
|
Value::Float(f) => Value::Float(*f),
|
||||||
_ => Value::Float(0.0),
|
_ => Value::Float(0.0),
|
||||||
@@ -8193,9 +8195,9 @@ impl Value {
|
|||||||
Value::Blob(b) => {
|
Value::Blob(b) => {
|
||||||
// Convert BLOB to TEXT first
|
// Convert BLOB to TEXT first
|
||||||
let text = String::from_utf8_lossy(b);
|
let text = String::from_utf8_lossy(b);
|
||||||
cast_text_to_integer(&text)
|
Value::Integer(crate::numeric::str_to_i64(&text).unwrap_or(0))
|
||||||
}
|
}
|
||||||
Value::Text(t) => cast_text_to_integer(t.as_str()),
|
Value::Text(t) => Value::Integer(crate::numeric::str_to_i64(t).unwrap_or(0)),
|
||||||
Value::Integer(i) => Value::Integer(*i),
|
Value::Integer(i) => Value::Integer(*i),
|
||||||
// A cast of a REAL value into an INTEGER results in the integer between the REAL value and zero
|
// A cast of a REAL value into an INTEGER results in the integer between the REAL value and zero
|
||||||
// that is closest to the REAL value. If a REAL is greater than the greatest possible signed integer (+9223372036854775807)
|
// that is closest to the REAL value. If a REAL is greater than the greatest possible signed integer (+9223372036854775807)
|
||||||
@@ -8214,14 +8216,31 @@ impl Value {
|
|||||||
_ => Value::Integer(0),
|
_ => Value::Integer(0),
|
||||||
},
|
},
|
||||||
Affinity::Numeric => match self {
|
Affinity::Numeric => match self {
|
||||||
Value::Blob(b) => {
|
Value::Null => Value::Null,
|
||||||
let text = String::from_utf8_lossy(b);
|
Value::Integer(v) => Value::Integer(*v),
|
||||||
cast_text_to_numeric(&text)
|
Value::Float(v) => Self::Float(*v),
|
||||||
|
_ => {
|
||||||
|
let s = match self {
|
||||||
|
Value::Text(text) => text.to_string(),
|
||||||
|
Value::Blob(blob) => String::from_utf8_lossy(blob.as_slice()).to_string(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match crate::numeric::str_to_f64(&s) {
|
||||||
|
Some(parsed) => {
|
||||||
|
let Some(int) = crate::numeric::str_to_i64(&s) else {
|
||||||
|
return Value::Integer(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if f64::from(parsed) == int as f64 {
|
||||||
|
return Value::Integer(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Float(parsed.into())
|
||||||
|
}
|
||||||
|
None => Value::Integer(0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::Text(t) => cast_text_to_numeric(t.as_str()),
|
|
||||||
Value::Integer(i) => Value::Integer(*i),
|
|
||||||
Value::Float(f) => Value::Float(*f),
|
|
||||||
_ => self.clone(), // TODO probably wrong
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,11 +165,21 @@ str_enum! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
str_enum! {
|
||||||
|
enum CastType {
|
||||||
|
Text => "text",
|
||||||
|
Real => "real",
|
||||||
|
Integer => "integer",
|
||||||
|
Numeric => "numeric",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Arbitrary)]
|
#[derive(Debug, Arbitrary)]
|
||||||
enum Expr {
|
enum Expr {
|
||||||
Value(Value),
|
Value(Value),
|
||||||
Binary(Binary, Box<Expr>, Box<Expr>),
|
Binary(Binary, Box<Expr>, Box<Expr>),
|
||||||
Unary(Unary, Box<Expr>),
|
Unary(Unary, Box<Expr>),
|
||||||
|
Cast(Box<Expr>, CastType),
|
||||||
UnaryFunc(UnaryFunc, Box<Expr>),
|
UnaryFunc(UnaryFunc, Box<Expr>),
|
||||||
BinaryFunc(BinaryFunc, Box<Expr>, Box<Expr>),
|
BinaryFunc(BinaryFunc, Box<Expr>, Box<Expr>),
|
||||||
}
|
}
|
||||||
@@ -229,6 +239,14 @@ impl Expr {
|
|||||||
depth: expr.depth + 1,
|
depth: expr.depth + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Cast(expr, cast_type) => {
|
||||||
|
let expr = expr.lower();
|
||||||
|
Output {
|
||||||
|
query: format!("cast({} as {cast_type})", expr.query),
|
||||||
|
parameters: expr.parameters,
|
||||||
|
depth: expr.depth + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user