Skip to content

Add native RPC calls for arbitrary stored procedures - #7

Open
Tawxyn wants to merge 2 commits into
Synergex:mainfrom
Tawxyn:generic-rpc
Open

Add native RPC calls for arbitrary stored procedures#7
Tawxyn wants to merge 2 commits into
Synergex:mainfrom
Tawxyn:generic-rpc

Conversation

@Tawxyn

@Tawxyn Tawxyn commented Aug 25, 2026

Copy link
Copy Markdown
  • Adds Client::call_procedure for invoking named stored procedures through native TDS RPC with positional or named input, output, and input/output parameters.

  • Preserves declared wire types independently from values so typed NULL outputs, bounded and MAX strings/binary values, and numeric precision/scale round-trip correctly.

  • Exposes buffered result sets, output values, signed return status, and informational messages while keeping connections reusable after completion, errors, or cancellation.

  • Fixes multi-packet RPC reads and response framing to prevent hangs and corrupted payloads.

Tawxyn and others added 2 commits August 25, 2026 14:04
RPC was previously reachable only for the built-in system procedures
(sp_prepare, sp_cursor*, ...), so invoking a user-defined procedure meant
falling back to a generated EXEC batch and losing typed output
parameters, return status, and server messages.

Add Client::call_procedure, built on the existing RPC send path and
response collectors rather than a parallel one: RpcParam gains an
optional type_info, and send_rpc now accepts a procedure name as well as
a numeric proc id.

Public API:
  - ProcedureParameter carries an explicit TypeInfo independent of its
    value, so a NULL output placeholder can declare a nullable wire type.
    Binds positionally by default; .named() binds by name.
  - ParameterDirection::{Input, Output, InputOutput}
  - ProcedureResult exposes result sets, output values, return status and
    informational messages.
  - OutputValue::type_info() exposes the server's wire type.

The response is fully buffered, so the connection is reusable as soon as
the call returns, and cancellation works via cancellation_token() as it
does for query/execute.

RETURNSTATUS is surfaced as i32 rather than u32 so negative return codes
round-trip correctly.

Also fix two server-module bugs, both triggered by messages spanning more
than one TDS packet:
  - TdsConnection::poll_next returned Poll::Pending after a read that did
    not complete a message, with no waker registered, so any multi-packet
    request hung forever.
  - Multi-packet responses were framed into one accumulating buffer, but
    Packet::encode back-patches the length at absolute offsets 2..4,
    zeroing the second packet's length and corrupting the first.

BREAKING CHANGE: RpcOption, RpcProcId and RpcStatus move from the crate
root to tiberius::server, alongside the types that expose them
(RpcMessage, DecodedRpcParam). They are no longer part of the
client-facing surface.

Tests: 23 in-process (no DB) and 15 live covering parameter directions,
positional binding, bounded and MAX strings/binary, NULL outputs, numeric
precision/scale, multi-packet payloads (112 KB CLOB / 100 KB BLOB),
multiple result sets, errors, cancellation and connection reuse. Each
in-process server now runs on its own thread; smol's global executor
defaults to a single thread shared by the whole test binary, which
deadlocked once a test moved bulk data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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