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

Function query

nodedb/src/control/server/http/routes/query.rs:82–236  ·  view source on GitHub ↗

POST /v1/query — execute a SQL/DDL statement. Request body: `{ "sql": "..." }` Response: `{ "status": "ok", "rows": [...] }` or `{ "error": "..." }` Database context (optional): - `X-NodeDB-Database: ` header (highest priority) - `?database= ` query parameter (fallback)

(
    headers: HeaderMap,
    QueryParams(db_param): QueryParams<DatabaseQueryParam>,
    State(state): State<AppState>,
    axum::Json(body): axum::Json<HttpQueryRequest>,
)

Source from the content-addressed store, hash-verified

80/// - `X-NodeDB-Database: <name>` header (highest priority)
81/// - `?database=<name>` query parameter (fallback)
82pub async fn query(
83 headers: HeaderMap,
84 QueryParams(db_param): QueryParams<DatabaseQueryParam>,
85 State(state): State<AppState>,
86 axum::Json(body): axum::Json<HttpQueryRequest>,
87) -> Result<impl IntoResponse, ApiError> {
88 let identity = resolve_identity(&headers, &state, "http")?;
89 let database_id = resolve_database_id(&headers, &db_param, &state)?;
90 let trace_id = crate::control::trace_context::extract_from_headers(&headers);
91
92 let sql = body.sql.as_str();
93
94 // Try DDL commands first (same as pgwire handler).
95 if let Some(result) = crate::control::server::pgwire::ddl::dispatch(
96 &state.shared,
97 &identity,
98 sql.trim(),
99 database_id,
100 )
101 .await
102 {
103 return match result {
104 Ok(responses) => {
105 let json_rows = responses_to_json(responses);
106 Ok(axum::Json(HttpQueryResponse::ok(json_rows)))
107 }
108 Err(e) => Err(ApiError::BadRequest(e.to_string())),
109 };
110 }
111
112 // Extract per-query ON DENY override + plan SQL with RLS injection.
113 let tenant_id = identity.tenant_id;
114
115 // Quota enforcement — reject before any planning or dispatch.
116 state
117 .shared
118 .check_tenant_quota(tenant_id)
119 .map_err(|e| ApiError::RateLimited {
120 message: e.to_string(),
121 retry_after_secs: 1,
122 })?;
123
124 let mut auth_ctx = crate::control::server::session_auth::build_auth_context(&identity);
125 let clean_sql =
126 crate::control::server::session_auth::extract_and_apply_on_deny(sql, &mut auth_ctx);
127 let perm_cache = state.shared.permission_cache.read().await;
128 let sec = crate::control::planner::context::PlanSecurityContext {
129 identity: &identity,
130 auth: &auth_ctx,
131 rls_store: &state.shared.rls,
132 permissions: &state.shared.permissions,
133 roles: &state.shared.roles,
134 permission_cache: Some(&*perm_cache),
135 };
136 let tasks = state
137 .query_ctx
138 .plan_sql_with_rls(&clean_sql, tenant_id, database_id, &sec)
139 .await

Callers

nothing calls this directly

Calls 15

resolve_identityFunction · 0.85
resolve_database_idFunction · 0.85
extract_from_headersFunction · 0.85
responses_to_jsonFunction · 0.85
build_auth_contextFunction · 0.85
required_permissionFunction · 0.85
role_grants_permissionFunction · 0.85
to_stringMethod · 0.80
check_tenant_quotaMethod · 0.80
plan_sql_with_rlsMethod · 0.80
tenant_request_startMethod · 0.80

Tested by

no test coverage detected