diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index 0042ff07..ac5f2cf8 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -13,10 +13,11 @@ name = "databend_driver" doc = false [dependencies] +chrono = { version = "0.4.35", default-features = false } ctor = "0.2.5" databend-driver = { workspace = true, features = ["rustls", "flight-sql"] } once_cell = "1.18" -pyo3 = { version = "0.21", features = ["abi3-py37"] } +pyo3 = { version = "0.21", features = ["abi3-py37", "chrono"] } pyo3-asyncio = { version = "0.21", features = ["tokio-runtime"] } tokio = "1.34" tokio-stream = "0.1" diff --git a/bindings/python/src/types.rs b/bindings/python/src/types.rs index 6755433d..dd71ad18 100644 --- a/bindings/python/src/types.rs +++ b/bindings/python/src/types.rs @@ -14,12 +14,13 @@ use std::sync::Arc; +use chrono::{NaiveDate, NaiveDateTime}; use once_cell::sync::Lazy; use pyo3::exceptions::{PyException, PyStopAsyncIteration, PyStopIteration}; use pyo3::intern; use pyo3::prelude::*; use pyo3::sync::GILOnceCell; -use pyo3::types::{PyDict, PyList, PyTuple, PyType}; +use pyo3::types::{PyBytes, PyDict, PyList, PyTuple, PyType}; use pyo3_asyncio::tokio::future_into_py; use tokio::sync::Mutex; use tokio_stream::StreamExt; @@ -58,19 +59,22 @@ impl IntoPy for Value { dict.into_py(py) } databend_driver::Value::Boolean(b) => b.into_py(py), - databend_driver::Value::Binary(b) => b.into_py(py), + databend_driver::Value::Binary(b) => { + let buf = PyBytes::new_bound(py, &b); + buf.into_py(py) + } databend_driver::Value::String(s) => s.into_py(py), databend_driver::Value::Number(n) => { let v = NumberValue(n); v.into_py(py) } databend_driver::Value::Timestamp(_) => { - let s = self.0.to_string(); - s.into_py(py) + let t = NaiveDateTime::try_from(self.0).unwrap(); + t.into_py(py) } databend_driver::Value::Date(_) => { - let s = self.0.to_string(); - s.into_py(py) + let d = NaiveDate::try_from(self.0).unwrap(); + d.into_py(py) } databend_driver::Value::Array(inner) => { let list = PyList::new_bound(py, inner.into_iter().map(|v| Value(v).into_py(py))); diff --git a/bindings/python/tests/asyncio/steps/binding.py b/bindings/python/tests/asyncio/steps/binding.py index 62cb1ddc..6a45ab0b 100644 --- a/bindings/python/tests/asyncio/steps/binding.py +++ b/bindings/python/tests/asyncio/steps/binding.py @@ -13,6 +13,7 @@ # limitations under the License. import os +from datetime import datetime, date from decimal import Decimal from behave import given, when, then @@ -70,13 +71,13 @@ async def _(context): # Map row = await context.conn.query_row("select {'xx':to_date('2020-01-01')}") - assert row.values() == ({"xx": "2020-01-01"},) + assert row.values() == ({"xx": date(2020, 1, 1)},) # Tuple row = await context.conn.query_row( "select (10, '20', to_datetime('2024-04-16 12:34:56.789'))" ) - assert row.values() == ((10, "20", "2024-04-16 12:34:56.789"),) + assert row.values() == ((10, "20", datetime(2024, 4, 16, 12, 34, 56, 789)),) @then("Select numbers should iterate all rows") @@ -106,9 +107,9 @@ async def _(context): async for row in rows: ret.append(row.values()) expected = [ - (-1, 1, 1.0, "1", "1", "2011-03-06", "2011-03-06 06:20:00"), - (-2, 2, 2.0, "2", "2", "2012-05-31", "2012-05-31 11:20:00"), - (-3, 3, 3.0, "3", "2", "2016-04-04", "2016-04-04 11:30:00"), + (-1, 1, 1.0, "1", "1", date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)), + (-2, 2, 2.0, "2", "2", date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)), + (-3, 3, 3.0, "3", "2", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)), ] assert ret == expected @@ -130,8 +131,8 @@ async def _(context): async for row in rows: ret.append(row.values()) expected = [ - (-1, 1, 1.0, "1", "1", "2011-03-06", "2011-03-06 06:20:00"), - (-2, 2, 2.0, "2", "2", "2012-05-31", "2012-05-31 11:20:00"), - (-3, 3, 3.0, "3", "2", "2016-04-04", "2016-04-04 11:30:00"), + (-1, 1, 1.0, "1", "1", date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)), + (-2, 2, 2.0, "2", "2", date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)), + (-3, 3, 3.0, "3", "2", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)), ] assert ret == expected diff --git a/bindings/python/tests/blocking/steps/binding.py b/bindings/python/tests/blocking/steps/binding.py index 61ee9063..040d29b6 100644 --- a/bindings/python/tests/blocking/steps/binding.py +++ b/bindings/python/tests/blocking/steps/binding.py @@ -13,6 +13,7 @@ # limitations under the License. import os +from datetime import datetime, date from decimal import Decimal from behave import given, when, then @@ -65,13 +66,13 @@ async def _(context): # Map row = context.conn.query_row("select {'xx':to_date('2020-01-01')}") - assert row.values() == ({"xx": "2020-01-01"},) + assert row.values() == ({"xx": date(2020, 1, 1)},) # Tuple row = context.conn.query_row( "select (10, '20', to_datetime('2024-04-16 12:34:56.789'))" ) - assert row.values() == ((10, "20", "2024-04-16 12:34:56.789"),) + assert row.values() == ((10, "20", datetime(2024, 4, 16, 12, 34, 56, 789)),) @then("Select numbers should iterate all rows") @@ -99,9 +100,9 @@ def _(context): for row in rows: ret.append(row.values()) expected = [ - (-1, 1, 1.0, "1", "1", "2011-03-06", "2011-03-06 06:20:00"), - (-2, 2, 2.0, "2", "2", "2012-05-31", "2012-05-31 11:20:00"), - (-3, 3, 3.0, "3", "2", "2016-04-04", "2016-04-04 11:30:00"), + (-1, 1, 1.0, "1", "1", date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)), + (-2, 2, 2.0, "2", "2", date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)), + (-3, 3, 3.0, "3", "2", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)), ] assert ret == expected @@ -122,8 +123,8 @@ def _(context): for row in rows: ret.append(row.values()) expected = [ - (-1, 1, 1.0, "1", "1", "2011-03-06", "2011-03-06 06:20:00"), - (-2, 2, 2.0, "2", "2", "2012-05-31", "2012-05-31 11:20:00"), - (-3, 3, 3.0, "3", "2", "2016-04-04", "2016-04-04 11:30:00"), + (-1, 1, 1.0, "1", "1", date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)), + (-2, 2, 2.0, "2", "2", date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)), + (-3, 3, 3.0, "3", "2", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)), ] assert ret == expected