Skip to content

Commit 6b3d478

Browse files
committed
Move _convert_named_to_positional to cursor module and delete params module
Docs: Update CHANGES.rst based on new implementation
1 parent 9e06065 commit 6b3d478

4 files changed

Lines changed: 44 additions & 154 deletions

File tree

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ Changes for crate
55
Unreleased
66
==========
77

8+
- Added named parameter support (``pyformat`` paramstyle). Passing a
9+
:class:`py:dict` as ``parameters`` to ``cursor.execute()`` now accepts
10+
``%(name)s`` placeholders and converts them to positional ``?`` markers
11+
client-side. Positional parameters using ``?`` continue to work unchanged.
12+
813
2026/03/09 2.1.2
914
================
1015

src/crate/client/cursor.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,50 @@
1818
# However, if you have executed another commercial license agreement
1919
# with Crate these terms will supersede the license and you may use the
2020
# software solely pursuant to the terms of the relevant commercial agreement.
21+
import re
2122
import typing as t
2223
import warnings
2324
from datetime import datetime, timedelta, timezone
2425

2526
from .converter import Converter, DataType
2627
from .exceptions import ProgrammingError
27-
from .params import convert_named_to_positional
28+
29+
_NAMED_PARAM_RE = re.compile(r"%\((\w+)\)s")
30+
31+
32+
def _convert_named_to_positional(
33+
sql: str, params: t.Dict[str, t.Any]
34+
) -> t.Tuple[str, t.List[t.Any]]:
35+
"""Convert pyformat-style named parameters to positional qmark parameters.
36+
37+
Converts ``%(name)s`` placeholders to ``?`` and returns an ordered list
38+
of corresponding values extracted from ``params``.
39+
40+
The same name may appear multiple times; each occurrence appends the
41+
value to the positional list independently.
42+
43+
Raises ``ProgrammingError`` if a placeholder name is absent from ``params``.
44+
Extra keys in ``params`` are silently ignored.
45+
46+
Example::
47+
48+
sql = "SELECT * FROM t WHERE a = %(a)s AND b = %(b)s"
49+
params = {"a": 1, "b": 2}
50+
# returns: ("SELECT * FROM t WHERE a = ? AND b = ?", [1, 2])
51+
"""
52+
positional: t.List[t.Any] = []
53+
54+
def _replace(match: "re.Match[str]") -> str:
55+
name = match.group(1)
56+
if name not in params:
57+
raise ProgrammingError(
58+
f"Named parameter '{name}' not found in the parameters dict"
59+
)
60+
positional.append(params[name])
61+
return "?"
62+
63+
converted_sql = _NAMED_PARAM_RE.sub(_replace, sql)
64+
return converted_sql, positional
2865

2966

3067
class Cursor:
@@ -56,7 +93,7 @@ def execute(self, sql, parameters=None, bulk_parameters=None):
5693
raise ProgrammingError("Cursor closed")
5794

5895
if isinstance(parameters, dict):
59-
sql, parameters = convert_named_to_positional(sql, parameters)
96+
sql, parameters = _convert_named_to_positional(sql, parameters)
6097

6198
self._result = self.connection.client.sql(
6299
sql, parameters, bulk_parameters

src/crate/client/params.py

Lines changed: 0 additions & 61 deletions
This file was deleted.

tests/client/test_params.py

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)