Skip to content

Add named parameter support#795

Open
bgunebakan wants to merge 4 commits intocrate:mainfrom
bgunebakan:feature/named-parameter-support
Open

Add named parameter support#795
bgunebakan wants to merge 4 commits intocrate:mainfrom
bgunebakan:feature/named-parameter-support

Conversation

@bgunebakan
Copy link

@bgunebakan bgunebakan commented Mar 23, 2026

Summary of the changes / Why this is an improvement

Adds client-side named parameter support (pyformat paramstyle) to the CrateDB Python client, addressing the request in #774.

Previously, cursor.execute() only accepted positional ? placeholders. Users with complex SQL had to maintain a positional list that was error-prone and hard to read.

With this change, a dict can be passed as the parameters argument using %(name)s placeholders:

cursor.execute(
    "SELECT * FROM t WHERE city = %(city)s AND kiez = %(kiez)s",
    {"city": "Berlin", "kiez": "Kreuzberg"},
)

The same parameter name may appear multiple times in the query, each occurrence is resolved independently:

cursor.execute(
    "SELECT * FROM t WHERE state = %(q)s OR city = %(q)s",
    {"q": "Berlin"},
)

Positional ? queries continue to work unchanged, no breaking change.

Known limitation

Supporting named params in bulk operations are not supported because it requires a different approach from execute(). We need to extract the ordered parameter name list from the SQL. Excluded from this PR to keep this PR focused on the core execute() case.

Checklist

@amotl amotl requested review from mfussenegger and seut March 24, 2026 23:10
@mfussenegger
Copy link
Member

mfussenegger commented Mar 25, 2026

Thank you for the contribution.
Could you also add an entry to the CHANGES.rst? Otherwise lgtm

_NAMED_PARAM_RE = re.compile(r"%\((\w+)\)s")


def convert_named_to_positional(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd tend to move the method into the cursor module and prefix it with _.

It's not supposed to be public API and a dedicated module doesn't seem warranted for this tiny and simple function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I was thinking to isolate the regex logic in its own module to keep cursor.py focused on DB-API concerns and make the function independently testable.

I'm moving to _convert_named_to_positional and _NAMED_PARAM_RE directly into cursor.py at module level (not inside the class, since it doesn't touch self or any class state). And will delete params.py. What do you think about this structure?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm moving to _convert_named_to_positional and _NAMED_PARAM_RE directly into cursor.py at module level (not inside the class, since it doesn't touch self or any class state). And will delete params.py. What do you think about this structure?

Sounds good

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review. I refactored the code based on that.

@mfussenegger mfussenegger changed the title Feature/named parameter support Add named parameter support Mar 25, 2026
…module

Docs: Update CHANGES.rst based on new implementation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parameter binding failure with string containing "{{value}}" and lack of named parameter support

3 participants