Skip to content

feat(backend/kernel): wire TSparkParameter through to kernel bind_param#789

Open
vikrantpuppala wants to merge 1 commit into
mainfrom
feat/kernel-bind-param
Open

feat(backend/kernel): wire TSparkParameter through to kernel bind_param#789
vikrantpuppala wants to merge 1 commit into
mainfrom
feat/kernel-bind-param

Conversation

@vikrantpuppala
Copy link
Copy Markdown
Contributor

@vikrantpuppala vikrantpuppala commented May 15, 2026

Summary

Lifts the NotSupportedError the kernel backend currently raises for parametrized queries. The kernel-side PyO3 binding (Statement.bind_param) shipped in databricks-sql-kernel#18; this PR wires the connector's TSparkParameter shape through to it.

Before: cursor.execute("SELECT ?", [IntegerParameter(42)]) on use_kernel=True raised NotSupportedError. After: positional parameter binding works end-to-end.

What it does

bind_tspark_params(kernel_stmt, parameters) in type_mapping.py forwards each TSparkParameter to the kernel as (ordinal, value.stringValue, type):

  • ordinal is the 1-based position in the parameters list (matches kernel's SEA-wire convention).
  • value.stringValue is the connector's already-string-encoded value (or None for SQL NULL).
  • type is the SQL type name the connector produces ("INT", "DECIMAL(10,2)", etc.).

The connector's ordinal: bool flag is checked only to reject named bindings — kernel v0 doesn't accept named bindings on the SEA wire, so we surface that at the connector layer with a pointed NotSupportedError rather than letting the server reject.

What's supported

Every type the connector's native parameter classes emit:

  • BOOLEAN, TINYINT / SMALLINT / INT / BIGINT, FLOAT / DOUBLE
  • STRING, DATE, TIMESTAMP / TIMESTAMP_NTZ, INTERVAL
  • DECIMAL(p,s) (precision/scale carried in the SQL type string; kernel parses them)
  • VOID / Python None

What's not supported (known gaps, raises NotSupportedError)

  • Named bindings (cursor.execute(":foo = ?", [param_named("foo", ...)])) — kernel v0 SEA wire is positional-only.
  • Compound types (ARRAY / MAP / STRUCT) — kernel parser rejects; surfaces as KernelError(InvalidArgument).
  • BINARY — SEA wire limitation kernel-side; documented workaround is hex-encode + unhex(?) in SQL.

Test plan

  • 6 new unit tests in tests/unit/test_kernel_type_mapping.py for bind_tspark_params — positional forwarding, None / VOID, named-binding rejection, missing-type defaulting, empty list.
  • Replaced test_execute_command_rejects_parameters (which previously asserted the NotSupportedError) with test_execute_command_forwards_parameters_to_bind_param: stubs the kernel statement, asserts bind_param is called once per TSparkParameter in order with 1-based ordinals, and execute() fires after binding.
  • Full kernel unit suite: 106/106 pass (up from 100 on main).
  • 3 new live e2e tests in tests/e2e/test_kernel_backend.py against dogfood:
    • Mixed-type round-trip (IntegerParameter, StringParameter, BooleanParameter)
    • None parameter
    • DECIMAL parameter (precision/scale flows through the SQL type string)
  • Full kernel e2e suite: 14/14 pass against dogfood (locally with the kernel wheel built from databricks-sql-kernel main).

This pull request and its description were written by Isaac.

Lifts the NotSupportedError that execute_command currently raises
for parametrized queries. The kernel-side PyO3 binding for
Statement.bind_param landed in databricks-sql-kernel#18; this
commit wires the connector's TSparkParameter shape through to it.

Implementation:

- New `bind_tspark_params(kernel_stmt, parameters)` in
  type_mapping.py forwards each TSparkParameter to the kernel as
  `(ordinal, value.stringValue, type)`. ordinal is the 1-based
  position in the parameters list; the connector's `ordinal: bool`
  flag is checked only to reject named bindings (kernel v0 doesn't
  accept them on the wire).
- execute_command no longer raises on `parameters=[...]`. The
  query_tags branch stays — that's a separate gap.

Tests:

- 6 new unit tests in tests/unit/test_kernel_type_mapping.py for
  the mapper:
  - positional forwarding preserves ordering and (ordinal, value, type)
  - None value forwards as SQL NULL
  - VOID passes through verbatim (kernel parser ignores value for VOID)
  - named bindings raise NotSupportedError with a pointed message
  - missing TSparkParameter.type defaults to STRING (defensive)
  - empty parameters list is a no-op
- `test_execute_command_rejects_parameters` (which previously
  asserted the NotSupportedError) replaced with
  `test_execute_command_forwards_parameters_to_bind_param` — stubs
  the kernel statement and verifies bind_param is called once per
  TSparkParameter in order with 1-based ordinals, and execute
  fires after binding.
- 3 new e2e tests in tests/e2e/test_kernel_backend.py against
  dogfood:
  - mixed-type round-trip (INT, STRING, BOOLEAN) via the
    connector's native IntegerParameter/StringParameter/BooleanParameter
  - None parameter (VoidParameter → SQL NULL)
  - DECIMAL parameter with precision/scale carried in the SQL type
    string (auto-inferred — explicit-arg path has a pre-existing
    bug in native.py where format-args are swapped)

106/106 kernel unit tests pass.

Co-authored-by: Isaac
Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
@vikrantpuppala vikrantpuppala force-pushed the feat/kernel-bind-param branch from aa8bd24 to 19c0bb6 Compare May 18, 2026 10:24
@vikrantpuppala vikrantpuppala changed the base branch from feat/kernel-backend to main May 18, 2026 10:24
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.

1 participant