Skip to main content

servers/mysql/
helper.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::ops::ControlFlow;
16use std::time::Duration;
17
18use chrono::NaiveDate;
19use common_query::prelude::ScalarValue;
20use common_sql::convert::sql_value_to_value;
21use common_time::{Date, Timestamp};
22use datatypes::prelude::{ConcreteDataType, DataType};
23use datatypes::schema::ColumnSchema;
24use datatypes::types::TimestampType;
25use datatypes::value::{self, Value};
26#[cfg(test)]
27use itertools::Itertools;
28use opensrv_mysql::{ParamValue, ValueInner, to_naive_datetime};
29use snafu::ResultExt;
30use sql::ast::{Expr, Value as ValueExpr, ValueWithSpan, VisitMut, visit_expressions_mut};
31use sql::statements::statement::Statement;
32
33use crate::error::{self, Result};
34
35/// Location of a prepared-statement placeholder in the original SQL text.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub(crate) struct PlaceholderSpan {
38    /// 1-based placeholder index.
39    pub(crate) index: usize,
40    pub(crate) start_line: u64,
41    pub(crate) start_column: u64,
42    pub(crate) end_line: u64,
43    pub(crate) end_column: u64,
44}
45
46/// Returns the placeholder string "$i".
47pub fn format_placeholder(i: usize) -> String {
48    format!("${}", i)
49}
50
51/// Replace all the "?" placeholder into "$i" in SQL,
52/// returns the new SQL and the last placeholder index.
53#[cfg(test)]
54pub fn replace_placeholders(query: &str) -> (String, usize) {
55    let query_parts = query.split('?').collect::<Vec<_>>();
56    let parts_len = query_parts.len();
57    let mut index = 0;
58    let query = query_parts
59        .into_iter()
60        .enumerate()
61        .map(|(i, part)| {
62            if i == parts_len - 1 {
63                return part.to_string();
64            }
65
66            index += 1;
67            format!("{part}{}", format_placeholder(index))
68        })
69        .join("");
70
71    (query, index + 1)
72}
73
74/// Transform all the "?" placeholders into "$i" and return the number of
75/// transformed placeholders.
76pub fn transform_placeholders_with_count(mut stmt: Statement) -> (Statement, usize) {
77    let count = visit_placeholders(&mut stmt);
78    (stmt, count)
79}
80
81/// Collect spans of "$i" placeholders in a statement.
82pub(crate) fn placeholder_spans(mut stmt: Statement) -> Vec<PlaceholderSpan> {
83    let mut spans = Vec::new();
84    collect_placeholder_spans(&mut stmt, &mut spans);
85    spans
86}
87
88fn collect_placeholder_spans<V>(v: &mut V, spans: &mut Vec<PlaceholderSpan>)
89where
90    V: VisitMut,
91{
92    let _ = visit_expressions_mut(v, |expr| {
93        if let Expr::Value(ValueWithSpan {
94            value: ValueExpr::Placeholder(s),
95            span,
96        }) = expr
97            && let Some(index) = placeholder_index(s)
98        {
99            spans.push(PlaceholderSpan {
100                index,
101                start_line: span.start.line,
102                start_column: span.start.column,
103                end_line: span.end.line,
104                end_column: span.end.column,
105            });
106        }
107        ControlFlow::<()>::Continue(())
108    });
109}
110
111fn placeholder_index(s: &str) -> Option<usize> {
112    s.strip_prefix('$')?
113        .parse::<usize>()
114        .ok()
115        .filter(|i| *i > 0)
116}
117
118fn visit_placeholders<V>(v: &mut V) -> usize
119where
120    V: VisitMut,
121{
122    let mut index = 1;
123    let _ = visit_expressions_mut(v, |expr| {
124        if let Expr::Value(ValueWithSpan {
125            value: ValueExpr::Placeholder(s),
126            ..
127        }) = expr
128            && s == "?"
129        {
130            *s = format_placeholder(index);
131            index += 1;
132        }
133        ControlFlow::<()>::Continue(())
134    });
135    index - 1
136}
137
138/// Convert [`ParamValue`] into [`Value`] according to param type.
139/// It will try it's best to do type conversions if possible
140pub fn convert_value(param: &ParamValue, t: &ConcreteDataType) -> Result<ScalarValue> {
141    if let ConcreteDataType::Dictionary(dictionary) = t {
142        return Ok(ScalarValue::Dictionary(
143            Box::new(dictionary.key_type().as_arrow_type()),
144            Box::new(convert_value(param, dictionary.value_type())?),
145        ));
146    }
147
148    match param.value.into_inner() {
149        ValueInner::Int(i) => match t {
150            ConcreteDataType::Int8(_) => Ok(ScalarValue::Int8(Some(i as i8))),
151            ConcreteDataType::Int16(_) => Ok(ScalarValue::Int16(Some(i as i16))),
152            ConcreteDataType::Int32(_) => Ok(ScalarValue::Int32(Some(i as i32))),
153            ConcreteDataType::Int64(_) => Ok(ScalarValue::Int64(Some(i))),
154            ConcreteDataType::UInt8(_) => Ok(ScalarValue::UInt8(Some(i as u8))),
155            ConcreteDataType::UInt16(_) => Ok(ScalarValue::UInt16(Some(i as u16))),
156            ConcreteDataType::UInt32(_) => Ok(ScalarValue::UInt32(Some(i as u32))),
157            ConcreteDataType::UInt64(_) => Ok(ScalarValue::UInt64(Some(i as u64))),
158            ConcreteDataType::Float32(_) => Ok(ScalarValue::Float32(Some(i as f32))),
159            ConcreteDataType::Float64(_) => Ok(ScalarValue::Float64(Some(i as f64))),
160            ConcreteDataType::Boolean(_) => Ok(ScalarValue::Boolean(Some(i != 0))),
161            ConcreteDataType::Timestamp(ts_type) => Value::Timestamp(ts_type.create_timestamp(i))
162                .try_to_scalar_value(t)
163                .context(error::ConvertScalarValueSnafu),
164
165            _ => error::PreparedStmtTypeMismatchSnafu {
166                expected: t,
167                actual: param.coltype,
168            }
169            .fail(),
170        },
171        ValueInner::UInt(u) => match t {
172            ConcreteDataType::Int8(_) => Ok(ScalarValue::Int8(Some(u as i8))),
173            ConcreteDataType::Int16(_) => Ok(ScalarValue::Int16(Some(u as i16))),
174            ConcreteDataType::Int32(_) => Ok(ScalarValue::Int32(Some(u as i32))),
175            ConcreteDataType::Int64(_) => Ok(ScalarValue::Int64(Some(u as i64))),
176            ConcreteDataType::UInt8(_) => Ok(ScalarValue::UInt8(Some(u as u8))),
177            ConcreteDataType::UInt16(_) => Ok(ScalarValue::UInt16(Some(u as u16))),
178            ConcreteDataType::UInt32(_) => Ok(ScalarValue::UInt32(Some(u as u32))),
179            ConcreteDataType::UInt64(_) => Ok(ScalarValue::UInt64(Some(u))),
180            ConcreteDataType::Float32(_) => Ok(ScalarValue::Float32(Some(u as f32))),
181            ConcreteDataType::Float64(_) => Ok(ScalarValue::Float64(Some(u as f64))),
182            ConcreteDataType::Boolean(_) => Ok(ScalarValue::Boolean(Some(u != 0))),
183            ConcreteDataType::Timestamp(ts_type) => {
184                Value::Timestamp(ts_type.create_timestamp(u as i64))
185                    .try_to_scalar_value(t)
186                    .context(error::ConvertScalarValueSnafu)
187            }
188
189            _ => error::PreparedStmtTypeMismatchSnafu {
190                expected: t,
191                actual: param.coltype,
192            }
193            .fail(),
194        },
195        ValueInner::Double(f) => match t {
196            ConcreteDataType::Int8(_) => Ok(ScalarValue::Int8(Some(f as i8))),
197            ConcreteDataType::Int16(_) => Ok(ScalarValue::Int16(Some(f as i16))),
198            ConcreteDataType::Int32(_) => Ok(ScalarValue::Int32(Some(f as i32))),
199            ConcreteDataType::Int64(_) => Ok(ScalarValue::Int64(Some(f as i64))),
200            ConcreteDataType::UInt8(_) => Ok(ScalarValue::UInt8(Some(f as u8))),
201            ConcreteDataType::UInt16(_) => Ok(ScalarValue::UInt16(Some(f as u16))),
202            ConcreteDataType::UInt32(_) => Ok(ScalarValue::UInt32(Some(f as u32))),
203            ConcreteDataType::UInt64(_) => Ok(ScalarValue::UInt64(Some(f as u64))),
204            ConcreteDataType::Float32(_) => Ok(ScalarValue::Float32(Some(f as f32))),
205            ConcreteDataType::Float64(_) => Ok(ScalarValue::Float64(Some(f))),
206
207            _ => error::PreparedStmtTypeMismatchSnafu {
208                expected: t,
209                actual: param.coltype,
210            }
211            .fail(),
212        },
213        ValueInner::NULL => value::to_null_scalar_value(t).context(error::ConvertScalarValueSnafu),
214        ValueInner::Bytes(b) => match t {
215            ConcreteDataType::String(t) => {
216                let s = String::from_utf8_lossy(b).to_string();
217                if t.is_large() {
218                    Ok(ScalarValue::LargeUtf8(Some(s)))
219                } else {
220                    Ok(ScalarValue::Utf8(Some(s)))
221                }
222            }
223            ConcreteDataType::Binary(_) => Ok(ScalarValue::Binary(Some(b.to_vec()))),
224            ConcreteDataType::Timestamp(ts_type) => convert_bytes_to_timestamp(b, ts_type),
225            ConcreteDataType::Date(_) => convert_bytes_to_date(b),
226            _ => error::PreparedStmtTypeMismatchSnafu {
227                expected: t,
228                actual: param.coltype,
229            }
230            .fail(),
231        },
232        ValueInner::Date(_) => {
233            let date: common_time::Date = NaiveDate::from(param.value).into();
234            Ok(ScalarValue::Date32(Some(date.val())))
235        }
236        ValueInner::Datetime(_) => {
237            let timestamp_millis = to_naive_datetime(param.value)
238                .map_err(|e| {
239                    error::MysqlValueConversionSnafu {
240                        err_msg: e.to_string(),
241                    }
242                    .build()
243                })?
244                .and_utc()
245                .timestamp_millis();
246
247            match t {
248                ConcreteDataType::Timestamp(_) => Ok(ScalarValue::TimestampMillisecond(
249                    Some(timestamp_millis),
250                    None,
251                )),
252                _ => error::PreparedStmtTypeMismatchSnafu {
253                    expected: t,
254                    actual: param.coltype,
255                }
256                .fail(),
257            }
258        }
259        ValueInner::Time(_) => Ok(ScalarValue::Time64Nanosecond(Some(
260            Duration::from(param.value).as_millis() as i64,
261        ))),
262    }
263}
264
265/// Convert an MySQL expression to a scalar value.
266/// It automatically handles the conversion of strings to numeric values.
267pub fn convert_expr_to_scalar_value(param: &Expr, t: &ConcreteDataType) -> Result<ScalarValue> {
268    if let ConcreteDataType::Dictionary(dictionary) = t {
269        return Ok(ScalarValue::Dictionary(
270            Box::new(dictionary.key_type().as_arrow_type()),
271            Box::new(convert_expr_to_scalar_value(
272                param,
273                dictionary.value_type(),
274            )?),
275        ));
276    }
277
278    let column_schema = ColumnSchema::new("", t.clone(), true);
279    match param {
280        Expr::Value(v) => {
281            let v = sql_value_to_value(&column_schema, &v.value, None, None, true);
282            match v {
283                Ok(v) => v
284                    .try_to_scalar_value(t)
285                    .context(error::ConvertScalarValueSnafu),
286                Err(e) => error::InvalidParameterSnafu {
287                    reason: e.to_string(),
288                }
289                .fail(),
290            }
291        }
292        Expr::UnaryOp { op, expr } if let Expr::Value(v) = &**expr => {
293            let v = sql_value_to_value(&column_schema, &v.value, None, Some(*op), true);
294            match v {
295                Ok(v) => v
296                    .try_to_scalar_value(t)
297                    .context(error::ConvertScalarValueSnafu),
298                Err(e) => error::InvalidParameterSnafu {
299                    reason: e.to_string(),
300                }
301                .fail(),
302            }
303        }
304        _ => error::InvalidParameterSnafu {
305            reason: format!("cannot convert {:?} to scalar value of type {}", param, t),
306        }
307        .fail(),
308    }
309}
310
311fn convert_bytes_to_timestamp(bytes: &[u8], ts_type: &TimestampType) -> Result<ScalarValue> {
312    let ts = Timestamp::from_str_utc(&String::from_utf8_lossy(bytes))
313        .map_err(|e| {
314            error::MysqlValueConversionSnafu {
315                err_msg: e.to_string(),
316            }
317            .build()
318        })?
319        .convert_to(ts_type.unit())
320        .ok_or_else(|| {
321            error::MysqlValueConversionSnafu {
322                err_msg: "Overflow when converting timestamp to target unit".to_string(),
323            }
324            .build()
325        })?;
326    match ts_type {
327        TimestampType::Nanosecond(_) => {
328            Ok(ScalarValue::TimestampNanosecond(Some(ts.value()), None))
329        }
330        TimestampType::Microsecond(_) => {
331            Ok(ScalarValue::TimestampMicrosecond(Some(ts.value()), None))
332        }
333        TimestampType::Millisecond(_) => {
334            Ok(ScalarValue::TimestampMillisecond(Some(ts.value()), None))
335        }
336        TimestampType::Second(_) => Ok(ScalarValue::TimestampSecond(Some(ts.value()), None)),
337    }
338}
339
340fn convert_bytes_to_date(bytes: &[u8]) -> Result<ScalarValue> {
341    let date = Date::from_str_utc(&String::from_utf8_lossy(bytes)).map_err(|e| {
342        error::MysqlValueConversionSnafu {
343            err_msg: e.to_string(),
344        }
345        .build()
346    })?;
347
348    Ok(ScalarValue::Date32(Some(date.val())))
349}
350
351#[cfg(test)]
352mod tests {
353    use datatypes::types::{
354        TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType,
355        TimestampSecondType,
356    };
357    use sql::dialect::MySqlDialect;
358    use sql::parser::{ParseOptions, ParserContext};
359
360    use super::*;
361
362    #[test]
363    fn test_format_placeholder() {
364        assert_eq!("$1", format_placeholder(1));
365        assert_eq!("$3", format_placeholder(3));
366    }
367
368    #[test]
369    fn test_replace_placeholders() {
370        let create = "create table demo(host string, ts timestamp time index)";
371        let (sql, index) = replace_placeholders(create);
372        assert_eq!(create, sql);
373        assert_eq!(1, index);
374
375        let insert = "insert into demo values(?,?,?)";
376        let (sql, index) = replace_placeholders(insert);
377        assert_eq!("insert into demo values($1,$2,$3)", sql);
378        assert_eq!(4, index);
379
380        let query = "select from demo where host=? and idc in (select idc from idcs where name=?) and cpu>?";
381        let (sql, index) = replace_placeholders(query);
382        assert_eq!(
383            "select from demo where host=$1 and idc in (select idc from idcs where name=$2) and cpu>$3",
384            sql
385        );
386        assert_eq!(4, index);
387    }
388
389    fn parse_sql(sql: &str) -> Statement {
390        let mut stmts =
391            ParserContext::create_with_dialect(sql, &MySqlDialect {}, ParseOptions::default())
392                .unwrap();
393        stmts.remove(0)
394    }
395
396    #[test]
397    fn test_transform_placeholders() {
398        let insert = parse_sql("insert into demo values(?,?,?)");
399        let (stmt, count) = transform_placeholders_with_count(insert);
400        let Statement::Insert(insert) = stmt else {
401            unreachable!()
402        };
403        assert_eq!(
404            "INSERT INTO demo VALUES ($1, $2, $3)",
405            insert.inner.to_string()
406        );
407        assert_eq!(3, count);
408
409        let delete = parse_sql("delete from demo where host=? and idc=?");
410        let (stmt, count) = transform_placeholders_with_count(delete);
411        let Statement::Delete(delete) = stmt else {
412            unreachable!()
413        };
414        assert_eq!(
415            "DELETE FROM demo WHERE host = $1 AND idc = $2",
416            delete.inner.to_string()
417        );
418        assert_eq!(2, count);
419
420        let select = parse_sql(
421            "select * from demo where host=? and idc in (select idc from idcs where name=?) and cpu>?",
422        );
423        let (stmt, count) = transform_placeholders_with_count(select);
424        let Statement::Query(select) = stmt else {
425            unreachable!()
426        };
427        assert_eq!(
428            "SELECT * FROM demo WHERE host = $1 AND idc IN (SELECT idc FROM idcs WHERE name = $2) AND cpu > $3",
429            select.inner.to_string()
430        );
431        assert_eq!(3, count);
432
433        let select = parse_sql("select '?', ?");
434        let (stmt, count) = transform_placeholders_with_count(select);
435        let Statement::Query(select) = stmt else {
436            unreachable!()
437        };
438        assert_eq!("SELECT '?', $1", select.inner.to_string());
439        assert_eq!(1, count);
440
441        let set = parse_sql("set time_zone = ?");
442        let (stmt, count) = transform_placeholders_with_count(set);
443        assert_eq!("SET time_zone = $1", stmt.to_string());
444        assert_eq!(1, count);
445    }
446
447    #[test]
448    fn test_convert_expr_to_scalar_value() {
449        let expr = Expr::Value(ValueExpr::Number("123".to_string(), false).into());
450        let t = ConcreteDataType::int32_datatype();
451        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
452        assert_eq!(ScalarValue::Int32(Some(123)), v);
453
454        let expr = Expr::Value(ValueExpr::Number("123.456789".to_string(), false).into());
455        let t = ConcreteDataType::float64_datatype();
456        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
457        assert_eq!(ScalarValue::Float64(Some(123.456789)), v);
458
459        let expr = Expr::Value(ValueExpr::SingleQuotedString("2001-01-02".to_string()).into());
460        let t = ConcreteDataType::date_datatype();
461        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
462        let scalar_v = ScalarValue::Utf8(Some("2001-01-02".to_string()))
463            .cast_to(&arrow_schema::DataType::Date32)
464            .unwrap();
465        assert_eq!(scalar_v, v);
466
467        let expr =
468            Expr::Value(ValueExpr::SingleQuotedString("2001-01-02 03:04:05".to_string()).into());
469        let t = ConcreteDataType::timestamp_microsecond_datatype();
470        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
471        let scalar_v = ScalarValue::Utf8(Some("2001-01-02 03:04:05".to_string()))
472            .cast_to(&arrow_schema::DataType::Timestamp(
473                arrow_schema::TimeUnit::Microsecond,
474                None,
475            ))
476            .unwrap();
477        assert_eq!(scalar_v, v);
478
479        let expr = Expr::Value(ValueExpr::SingleQuotedString("hello".to_string()).into());
480        let t = ConcreteDataType::string_datatype();
481        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
482        assert_eq!(ScalarValue::Utf8(Some("hello".to_string())), v);
483
484        let t = ConcreteDataType::dictionary_datatype(
485            ConcreteDataType::uint32_datatype(),
486            ConcreteDataType::string_datatype(),
487        );
488        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
489        assert_eq!(
490            ScalarValue::Dictionary(
491                Box::new(arrow_schema::DataType::UInt32),
492                Box::new(ScalarValue::Utf8(Some("hello".to_string()))),
493            ),
494            v
495        );
496
497        let expr = Expr::Value(ValueExpr::Null.into());
498        let t = ConcreteDataType::time_microsecond_datatype();
499        let v = convert_expr_to_scalar_value(&expr, &t).unwrap();
500        assert_eq!(ScalarValue::Time64Microsecond(None), v);
501    }
502
503    #[test]
504    fn test_convert_bytes_to_timestamp() {
505        let test_cases = vec![
506            // input unix timestamp in seconds -> nanosecond.
507            (
508                "2024-12-26 12:00:00",
509                TimestampType::Nanosecond(TimestampNanosecondType),
510                ScalarValue::TimestampNanosecond(Some(1735214400000000000), None),
511            ),
512            // input unix timestamp in seconds -> microsecond.
513            (
514                "2024-12-26 12:00:00",
515                TimestampType::Microsecond(TimestampMicrosecondType),
516                ScalarValue::TimestampMicrosecond(Some(1735214400000000), None),
517            ),
518            // input unix timestamp in seconds -> millisecond.
519            (
520                "2024-12-26 12:00:00",
521                TimestampType::Millisecond(TimestampMillisecondType),
522                ScalarValue::TimestampMillisecond(Some(1735214400000), None),
523            ),
524            // input unix timestamp in seconds -> second.
525            (
526                "2024-12-26 12:00:00",
527                TimestampType::Second(TimestampSecondType),
528                ScalarValue::TimestampSecond(Some(1735214400), None),
529            ),
530            // input unix timestamp in milliseconds -> nanosecond.
531            (
532                "2024-12-26 12:00:00.123",
533                TimestampType::Nanosecond(TimestampNanosecondType),
534                ScalarValue::TimestampNanosecond(Some(1735214400123000000), None),
535            ),
536            // input unix timestamp in milliseconds -> microsecond.
537            (
538                "2024-12-26 12:00:00.123",
539                TimestampType::Microsecond(TimestampMicrosecondType),
540                ScalarValue::TimestampMicrosecond(Some(1735214400123000), None),
541            ),
542            // input unix timestamp in milliseconds -> millisecond.
543            (
544                "2024-12-26 12:00:00.123",
545                TimestampType::Millisecond(TimestampMillisecondType),
546                ScalarValue::TimestampMillisecond(Some(1735214400123), None),
547            ),
548            // input unix timestamp in milliseconds -> second.
549            (
550                "2024-12-26 12:00:00.123",
551                TimestampType::Second(TimestampSecondType),
552                ScalarValue::TimestampSecond(Some(1735214400), None),
553            ),
554            // input unix timestamp in microseconds -> nanosecond.
555            (
556                "2024-12-26 12:00:00.123456",
557                TimestampType::Nanosecond(TimestampNanosecondType),
558                ScalarValue::TimestampNanosecond(Some(1735214400123456000), None),
559            ),
560            // input unix timestamp in microseconds -> microsecond.
561            (
562                "2024-12-26 12:00:00.123456",
563                TimestampType::Microsecond(TimestampMicrosecondType),
564                ScalarValue::TimestampMicrosecond(Some(1735214400123456), None),
565            ),
566            // input unix timestamp in microseconds -> millisecond.
567            (
568                "2024-12-26 12:00:00.123456",
569                TimestampType::Millisecond(TimestampMillisecondType),
570                ScalarValue::TimestampMillisecond(Some(1735214400123), None),
571            ),
572            // input unix timestamp in milliseconds -> second.
573            (
574                "2024-12-26 12:00:00.123456",
575                TimestampType::Second(TimestampSecondType),
576                ScalarValue::TimestampSecond(Some(1735214400), None),
577            ),
578        ];
579
580        for (input, ts_type, expected) in test_cases {
581            let result = convert_bytes_to_timestamp(input.as_bytes(), &ts_type).unwrap();
582            assert_eq!(result, expected);
583        }
584    }
585
586    #[test]
587    fn test_convert_bytes_to_date() {
588        let test_cases = vec![
589            // Standard date format: YYYY-MM-DD
590            ("1970-01-01", ScalarValue::Date32(Some(0))),
591            ("1969-12-31", ScalarValue::Date32(Some(-1))),
592            ("2024-02-29", ScalarValue::Date32(Some(19782))),
593            ("2024-01-01", ScalarValue::Date32(Some(19723))),
594            ("2024-12-31", ScalarValue::Date32(Some(20088))),
595            ("2001-01-02", ScalarValue::Date32(Some(11324))),
596            ("2050-06-14", ScalarValue::Date32(Some(29384))),
597            ("2020-03-15", ScalarValue::Date32(Some(18336))),
598        ];
599
600        for (input, expected) in test_cases {
601            let result = convert_bytes_to_date(input.as_bytes()).unwrap();
602            assert_eq!(result, expected, "Failed for input: {}", input);
603        }
604    }
605}