From 830b0cf6a9137f83d89286fbe1f11a8bd21ffceb Mon Sep 17 00:00:00 2001 From: TCeason Date: Tue, 7 Jan 2025 10:56:46 +0800 Subject: [PATCH 1/3] feat: binding interval convert to python timedelta --- bindings/python/src/types.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bindings/python/src/types.rs b/bindings/python/src/types.rs index 1afde17f2..76782e71d 100644 --- a/bindings/python/src/types.rs +++ b/bindings/python/src/types.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use chrono::Duration; use std::sync::Arc; use chrono::{NaiveDate, NaiveDateTime}; @@ -102,7 +103,14 @@ impl<'py> IntoPyObject<'py> for Value { databend_driver::Value::Variant(s) => s.into_bound_py_any(py)?, databend_driver::Value::Geometry(s) => s.into_bound_py_any(py)?, databend_driver::Value::Geography(s) => s.into_bound_py_any(py)?, - databend_driver::Value::Interval(s) => s.into_bound_py_any(py)?, + databend_driver::Value::Interval(s) => { + let value = databend_driver::Interval::from_string(&s).unwrap(); + let total_micros = (value.months as i64) * 30 * 86400000000 + + (value.days as i64) * 86400000000 + + value.micros; + let s = Duration::microseconds(total_micros); + s.into_bound_py_any(py)? + } }; Ok(val) } From 348edc009a399eec8a5d0a47b6e724d2d8f64ef8 Mon Sep 17 00:00:00 2001 From: TCeason Date: Tue, 7 Jan 2025 12:11:13 +0800 Subject: [PATCH 2/3] add docs and tests --- bindings/python/README.md | 29 ++++++++++--------- .../python/tests/asyncio/steps/binding.py | 6 +++- .../python/tests/blocking/steps/binding.py | 6 +++- bindings/python/tests/cursor/steps/binding.py | 7 ++++- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/bindings/python/README.md b/bindings/python/README.md index abbd23adb..4c065fc11 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -97,20 +97,21 @@ asyncio.run(main()) ### General Data Types -| Databend | Python | -| ----------- | ------------------- | -| `BOOLEAN` | `bool` | -| `TINYINT` | `int` | -| `SMALLINT` | `int` | -| `INT` | `int` | -| `BIGINT` | `int` | -| `FLOAT` | `float` | -| `DOUBLE` | `float` | -| `DECIMAL` | `decimal.Decimal` | -| `DATE` | `datetime.date` | -| `TIMESTAMP` | `datetime.datetime` | -| `VARCHAR` | `str` | -| `BINARY` | `bytes` | +| Databend | Python | +|-------------|----------------------| +| `BOOLEAN` | `bool` | +| `TINYINT` | `int` | +| `SMALLINT` | `int` | +| `INT` | `int` | +| `BIGINT` | `int` | +| `FLOAT` | `float` | +| `DOUBLE` | `float` | +| `DECIMAL` | `decimal.Decimal` | +| `DATE` | `datetime.date` | +| `TIMESTAMP` | `datetime.datetime` | +| `INTERVAL` | `datetime.timedelta` | +| `VARCHAR` | `str` | +| `BINARY` | `bytes` | ### Semi-Structured Data Types diff --git a/bindings/python/tests/asyncio/steps/binding.py b/bindings/python/tests/asyncio/steps/binding.py index a23368123..0144d41ed 100644 --- a/bindings/python/tests/asyncio/steps/binding.py +++ b/bindings/python/tests/asyncio/steps/binding.py @@ -13,7 +13,7 @@ # limitations under the License. import os -from datetime import datetime, date +from datetime import datetime, date, timedelta from decimal import Decimal from behave import given, when, then @@ -66,6 +66,10 @@ async def _(context): row = await context.conn.query_row("select to_binary('xyz')") assert row.values() == (b"xyz",), f"Binary: {row.values()}" + # Interval + row = await context.conn.query_row("select to_interval('1 hours')") + assert row.values() == (timedelta(hours=1),), f"Interval: {row.values()}" + # Decimal row = await context.conn.query_row("SELECT 15.7563::Decimal(8,4), 2.0+3.0") assert row.values() == ( diff --git a/bindings/python/tests/blocking/steps/binding.py b/bindings/python/tests/blocking/steps/binding.py index 6ff9e7349..15248c905 100644 --- a/bindings/python/tests/blocking/steps/binding.py +++ b/bindings/python/tests/blocking/steps/binding.py @@ -13,7 +13,7 @@ # limitations under the License. import os -from datetime import datetime, date +from datetime import datetime, date, timedelta from decimal import Decimal from behave import given, when, then @@ -61,6 +61,10 @@ async def _(context): row = context.conn.query_row("select to_binary('xyz')") assert row.values() == (b"xyz",), f"Binary: {row.values()}" + # Interval + row = context.conn.query_row("select to_interval('1 microseconds')") + assert row.values() == (timedelta(microseconds=1),), f"Interval: {row.values()}" + # Decimal row = context.conn.query_row("SELECT 15.7563::Decimal(8,4), 2.0+3.0") assert row.values() == ( diff --git a/bindings/python/tests/cursor/steps/binding.py b/bindings/python/tests/cursor/steps/binding.py index 21f48173c..4b5981bbc 100644 --- a/bindings/python/tests/cursor/steps/binding.py +++ b/bindings/python/tests/cursor/steps/binding.py @@ -13,7 +13,7 @@ # limitations under the License. import os -from datetime import datetime, date +from datetime import datetime, date, timedelta from decimal import Decimal from behave import given, when, then @@ -62,6 +62,11 @@ async def _(context): row = context.cursor.fetchone() assert row[0] == b"xyz", f"Binary: {row.values()}" + # Interval + context.cursor.execute("select to_interval('1 days')") + row = context.cursor.fetch_one() + assert row.values() == (timedelta(1),), f"Interval: {row.values()}" + # Decimal context.cursor.execute("SELECT 15.7563::Decimal(8,4), 2.0+3.0") row = context.cursor.fetchone() From b37db0da3cfc208c7e43d7c2607dbd19029ca8ce Mon Sep 17 00:00:00 2001 From: TCeason Date: Tue, 7 Jan 2025 12:36:07 +0800 Subject: [PATCH 3/3] fix --- bindings/python/src/types.rs | 3 +-- bindings/python/tests/cursor/steps/binding.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bindings/python/src/types.rs b/bindings/python/src/types.rs index 76782e71d..097b3ad1f 100644 --- a/bindings/python/src/types.rs +++ b/bindings/python/src/types.rs @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use chrono::Duration; use std::sync::Arc; -use chrono::{NaiveDate, NaiveDateTime}; +use chrono::{Duration, NaiveDate, NaiveDateTime}; use once_cell::sync::Lazy; use pyo3::exceptions::{PyAttributeError, PyException, PyStopAsyncIteration, PyStopIteration}; use pyo3::sync::GILOnceCell; diff --git a/bindings/python/tests/cursor/steps/binding.py b/bindings/python/tests/cursor/steps/binding.py index 4b5981bbc..5900abf0c 100644 --- a/bindings/python/tests/cursor/steps/binding.py +++ b/bindings/python/tests/cursor/steps/binding.py @@ -64,7 +64,7 @@ async def _(context): # Interval context.cursor.execute("select to_interval('1 days')") - row = context.cursor.fetch_one() + row = context.cursor.fetchone() assert row.values() == (timedelta(1),), f"Interval: {row.values()}" # Decimal