Skip to content

Commit c6bad5b

Browse files
authored
feat: support bind variant into params in cursor api (#700)
* feat: support bind variant into params in cursor api * feat: support bind variant into params in cursor api * feat: support bind variant into params in cursor api * feat: support bind variant into params in cursor api * feat: support bind variant into params in cursor api
1 parent 461ffe0 commit c6bad5b

File tree

3 files changed

+121
-20
lines changed

3 files changed

+121
-20
lines changed

bindings/python/package/databend_driver/__init__.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ class BlockingDatabendCursor:
124124
def rowcount(self) -> int: ...
125125
def close(self) -> None: ...
126126
def execute(
127-
self, operation: str, params: list[str] | tuple[str] = None
127+
self, operation: str, params: list[any] | tuple[any] = None
128128
) -> None | int: ...
129129
def executemany(
130-
self, operation: str, params: list[list[str] | tuple[str]]
130+
self, operation: str, params: list[list[any] | tuple[any]]
131131
) -> None | int: ...
132132
def fetchone(self) -> Row | None: ...
133133
def fetchmany(self, size: int = 1) -> list[Row]: ...

bindings/python/src/blocking.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,35 @@ fn to_csv_field(v: Bound<PyAny>) -> PyResult<String> {
501501
} else if let Ok(v) = v.extract::<f64>() {
502502
Ok(v.to_string())
503503
} else {
504-
Err(PyAttributeError::new_err(format!(
505-
"Invalid parameter type for: {v:?}, expected str, bool, int or float"
506-
)))
504+
// Try to convert dict-like objects to JSON string
505+
if let Ok(dict) = v.downcast::<pyo3::types::PyDict>() {
506+
// Convert Python dict to JSON string
507+
let json_string = Python::with_gil(|py| {
508+
let json_module = py.import("json")?;
509+
let dumps = json_module.getattr("dumps")?;
510+
let result = dumps.call1((dict,))?;
511+
result.extract::<String>()
512+
})?;
513+
Ok(json_string)
514+
} else {
515+
// Try to convert any object to string using its __str__ method
516+
match v.call_method0("__str__") {
517+
Ok(str_obj) => {
518+
if let Ok(s) = str_obj.extract::<String>() {
519+
Ok(s)
520+
} else {
521+
Err(PyAttributeError::new_err(format!(
522+
"Failed to convert object to string: {v:?}"
523+
)))
524+
}
525+
}
526+
Err(_) => {
527+
Err(PyAttributeError::new_err(format!(
528+
"Invalid parameter type for: {v:?}, expected str, bool, int, float, dict, or object with __str__ method"
529+
)))
530+
}
531+
}
532+
}
507533
}
508534
}
509535
Err(e) => Err(e.into()),

bindings/python/tests/cursor/steps/binding.py

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def _(context):
6262
s String,
6363
s2 String,
6464
d Date,
65-
t DateTime
65+
t DateTime,
66+
v Variant
6667
)
6768
"""
6869
)
@@ -167,18 +168,49 @@ def _(context):
167168

168169
@then("Insert and Select should be equal")
169170
def _(context):
171+
values = []
172+
if DRIVER_VERSION <= (0, 30, 3):
173+
print("SKIP")
174+
return
170175
context.cursor.execute(
171176
r"""
172177
INSERT INTO test VALUES
173-
(-1, 1, 1.0, '\'', NULL, '2011-03-06', '2011-03-06 06:20:00'),
174-
(-2, 2, 2.0, '"', '', '2012-05-31', '2012-05-31 11:20:00'),
175-
(-3, 3, 3.0, '\\', 'NULL', '2016-04-04', '2016-04-04 11:30:00')
178+
(-1, 1, 1.0, '\'', NULL, '2011-03-06', '2011-03-06 06:20:00', {'a': 1}),
179+
(-2, 2, 2.0, '"', '', '2012-05-31', '2012-05-31 11:20:00', {'a': 2}),
180+
(-3, 3, 3.0, '\\', 'NULL', '2016-04-04', '2016-04-04 11:30:00', {'a': 3})
176181
"""
177182
)
178183
expected = [
179-
(-1, 1, 1.0, "'", None, date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)),
180-
(-2, 2, 2.0, '"', "", date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)),
181-
(-3, 3, 3.0, "\\", "NULL", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)),
184+
(
185+
-1,
186+
1,
187+
1.0,
188+
"'",
189+
None,
190+
date(2011, 3, 6),
191+
datetime(2011, 3, 6, 6, 20),
192+
'{"a":1}',
193+
),
194+
(
195+
-2,
196+
2,
197+
2.0,
198+
'"',
199+
"",
200+
date(2012, 5, 31),
201+
datetime(2012, 5, 31, 11, 20),
202+
'{"a":2}',
203+
),
204+
(
205+
-3,
206+
3,
207+
3.0,
208+
"\\",
209+
"NULL",
210+
date(2016, 4, 4),
211+
datetime(2016, 4, 4, 11, 30),
212+
'{"a":3}',
213+
),
182214
]
183215

184216
# fetchall
@@ -204,21 +236,64 @@ def _(context):
204236

205237
@then("Stream load and Select should be equal")
206238
def _(context):
207-
values = [
208-
[-1, 1, 1.0, "'", None, "2011-03-06", "2011-03-06T06:20:00Z"],
209-
(-2, "2", 2.0, '"', "", "2012-05-31", "2012-05-31T11:20:00Z"),
210-
["-3", 3, 3.0, "\\", "NULL", "2016-04-04", "2016-04-04T11:30:00Z"],
211-
]
239+
# Skip dictionary parameters for old driver versions that don't support them
240+
values = []
241+
if DRIVER_VERSION <= (0, 30, 3):
242+
print("SKIP")
243+
return
244+
else:
245+
# For new versions, use actual dict objects
246+
values = [
247+
[-1, 1, 1.0, "'", None, "2011-03-06", "2011-03-06T06:20:00Z", {"a": 1}],
248+
(-2, "2", 2.0, '"', "", "2012-05-31", "2012-05-31T11:20:00Z", {"a": 2}),
249+
[
250+
"-3",
251+
3,
252+
3.0,
253+
"\\",
254+
"NULL",
255+
"2016-04-04",
256+
"2016-04-04T11:30:00Z",
257+
{"a": 3},
258+
],
259+
]
212260
count = context.cursor.executemany("INSERT INTO test VALUES", values)
213261
assert count == 3, f"count: {count}"
214262

215263
context.cursor.execute("SELECT * FROM test")
216264
rows = context.cursor.fetchall()
217265
ret = [row.values() for row in rows]
218266
expected = [
219-
(-1, 1, 1.0, "'", None, date(2011, 3, 6), datetime(2011, 3, 6, 6, 20)),
220-
(-2, 2, 2.0, '"', None, date(2012, 5, 31), datetime(2012, 5, 31, 11, 20)),
221-
(-3, 3, 3.0, "\\", "NULL", date(2016, 4, 4), datetime(2016, 4, 4, 11, 30)),
267+
(
268+
-1,
269+
1,
270+
1.0,
271+
"'",
272+
None,
273+
date(2011, 3, 6),
274+
datetime(2011, 3, 6, 6, 20),
275+
'{"a":1}',
276+
),
277+
(
278+
-2,
279+
2,
280+
2.0,
281+
'"',
282+
None,
283+
date(2012, 5, 31),
284+
datetime(2012, 5, 31, 11, 20),
285+
'{"a":2}',
286+
),
287+
(
288+
-3,
289+
3,
290+
3.0,
291+
"\\",
292+
"NULL",
293+
date(2016, 4, 4),
294+
datetime(2016, 4, 4, 11, 30),
295+
'{"a":3}',
296+
),
222297
]
223298
assert ret == expected, f"ret: {ret}"
224299

0 commit comments

Comments
 (0)