pub trait SqlConnector:
Send
+ Sync
+ 'static {
// Required methods
fn dialect(&self) -> Dialect;
fn execute<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [(String, Value)],
) -> Pin<Box<dyn Future<Output = Result<Vec<Value>, ConnectorError>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
fn schema_text<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<String, ConnectorError>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
}Expand description
Three-method SQL connector trait — Phase 84 ships the full trait surface.
Phase 83 shipped a 2-method MVP (dialect() + schema_text()); Phase 84
(CONN-01) lands execute() between them now that the per-backend connectors
validate the row/error/parameter shape. The trait is the stable contract the
per-backend crates (pmcp-toolkit-postgres, pmcp-toolkit-mysql,
pmcp-toolkit-athena, plus the sqlite feature SqliteConnector) implement.
§Semver-evolution plan
This trait WILL grow additively in a future minor release with:
execute_stream(sql, params) -> impl Stream<Item = Result<Value>>, shipped with a default body backed byexecute(...).map(stream::iter)so it is semver-compatible on aSend + Sync + 'statictrait — for the large-result-scan case (e.g. an Athena warehousing tool). Deferred per D-02 because no v2.2 reference scenario needs it.- Transaction support as a separate
SqlTransactionaltrait extension, when a real consumer needs it. Deferred per D-02 — the v2.2 reference scenarios are read-only and Athena has no real transaction model.
The variants on Dialect and ConnectorError are #[non_exhaustive]
so they can be extended additively without a semver break.
§Example
A minimal connector implementing all three methods. The example defines a
LOCAL dummy struct — it deliberately does NOT reference any downstream
per-backend crate, because those depend on pmcp-server-toolkit and would
create a circular doctest dependency (REVIEWS H6).
use pmcp_server_toolkit::sql::{SqlConnector, Dialect, ConnectorError};
use async_trait::async_trait;
use serde_json::Value;
struct Dummy;
#[async_trait]
impl SqlConnector for Dummy {
fn dialect(&self) -> Dialect { Dialect::Sqlite }
async fn execute(&self, _sql: &str, _params: &[(String, Value)])
-> Result<Vec<Value>, ConnectorError> {
Ok(vec![])
}
async fn schema_text(&self) -> Result<String, ConnectorError> {
Ok(String::new())
}
}Required Methods§
Sourcefn dialect(&self) -> Dialect
fn dialect(&self) -> Dialect
Identify the dialect for prompt assembly + placeholder translation.
Sourcefn execute<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [(String, Value)],
) -> Pin<Box<dyn Future<Output = Result<Vec<Value>, ConnectorError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn execute<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [(String, Value)],
) -> Pin<Box<dyn Future<Output = Result<Vec<Value>, ConnectorError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Execute a query and return one serde_json::Value per result row.
sql is the canonical statement (placeholders in the toolkit’s :name
form); params is a slice of named (name, value) pairs the caller
controls the order of (D-03). Per-backend impls translate placeholders
to their dialect via translate_placeholders and bind from params,
then convert driver-native rows into JSON objects (D-01).
Each returned Value is typically a JSON object keyed by column name —
the exact shape MCP transport needs to populate the tools/call
response’s structuredContent field (D-06).
§Errors
Returns a ConnectorError when the backend cannot connect
(ConnectorError::Connection), the driver fails
(ConnectorError::Driver), the query is rejected
(ConnectorError::Query), or a parameter cannot be bound
(ConnectorError::ParameterBind).
Sourcefn schema_text<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<String, ConnectorError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn schema_text<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<String, ConnectorError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Render the backend’s schema as DDL or equivalent text for inclusion in
the code-mode prompt. Phase 84 impls drive this from information_schema,
the Glue catalog, or sqlite_master per dialect.
Implementations should keep output BOUNDED — token-budget the schema before returning. The toolkit does not truncate (T-83-07-03).
§Errors
Returns a ConnectorError when the backend cannot enumerate its
schema (I/O failure, permission denied, missing catalog, etc.).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".