MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / handle_sql

Function handle_sql

nodedb/src/control/server/native/dispatch/sql.rs:29–139  ·  view source on GitHub ↗

Handle a SQL statement: transaction control, SET/SHOW, DDL, or DataFusion. `sql_params`, when present, carries the caller's bound values for `$1`, `$2`, … placeholders in `sql`. The handler renders each value as a SQL literal via `value_to_sql_literal` and substitutes the placeholders before any other dispatch — DDL routing, planner, transaction buffer — so every downstream sees one canonical SQL

(
    ctx: &DispatchCtx<'_>,
    seq: u64,
    sql: &str,
    sql_params: Option<&[Value]>,
)

Source from the content-addressed store, hash-verified

27/// string with literal values in place of placeholders. `None` (the
28/// common case) routes the SQL through unmodified.
29pub(crate) async fn handle_sql(
30 ctx: &DispatchCtx<'_>,
31 seq: u64,
32 sql: &str,
33 sql_params: Option<&[Value]>,
34) -> NativeResponse {
35 // Inline bound parameters before any dispatch — keeps the
36 // substitution invariant in one place so the DDL router, planner,
37 // and transaction buffer all see the same SQL shape regardless of
38 // whether the caller sent params or inlined values directly.
39 let substituted: Option<String> = match sql_params {
40 Some(params) if !params.is_empty() => match inline_params(sql, params) {
41 Ok(s) => Some(s),
42 Err(msg) => return NativeResponse::error(seq, "42P02", msg),
43 },
44 _ => None,
45 };
46 let sql = substituted.as_deref().unwrap_or(sql);
47 let sql_trimmed = sql.trim();
48 let upper = sql_trimmed.to_uppercase();
49
50 ctx.sessions.ensure_session(*ctx.peer_addr);
51
52 if sql_trimmed.is_empty() || sql_trimmed == ";" {
53 return NativeResponse::ok(seq);
54 }
55
56 // Transaction control.
57 if upper == "BEGIN" || upper == "BEGIN TRANSACTION" || upper == "START TRANSACTION" {
58 return handle_begin(ctx, seq);
59 }
60 if upper == "COMMIT" || upper == "END" || upper == "END TRANSACTION" {
61 return handle_commit(ctx, seq).await;
62 }
63 if upper == "ROLLBACK" || upper == "ABORT" {
64 return handle_rollback(ctx, seq);
65 }
66 if upper.starts_with("SAVEPOINT ") {
67 return NativeResponse::status_row(seq, "SAVEPOINT");
68 }
69 if upper.starts_with("RELEASE SAVEPOINT ") || upper.starts_with("RELEASE ") {
70 return NativeResponse::status_row(seq, "RELEASE");
71 }
72 if upper.starts_with("ROLLBACK TO ") {
73 return NativeResponse::status_row(seq, "ROLLBACK");
74 }
75
76 if ctx.sessions.transaction_state(ctx.peer_addr) == TransactionState::Failed {
77 return NativeResponse::error(
78 seq,
79 "25P02",
80 "current transaction is aborted, commands ignored until end of transaction block",
81 );
82 }
83
84 // SET / SHOW / RESET.
85 if upper.starts_with("SET ") {
86 return handle_set_sql(ctx, seq, sql_trimmed);

Callers 1

handle_requestMethod · 0.85

Calls 15

inline_paramsFunction · 0.85
handle_beginFunction · 0.85
handle_commitFunction · 0.85
handle_rollbackFunction · 0.85
handle_set_sqlFunction · 0.85
is_session_showFunction · 0.85
handle_show_sqlFunction · 0.85
handle_explainFunction · 0.85
pgwire_result_to_nativeFunction · 0.85
error_to_nativeFunction · 0.85
execute_plannedFunction · 0.85
ensure_sessionMethod · 0.80

Tested by

no test coverage detected