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

Function query_ndjson

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

POST /v1/query/stream — execute SQL and return results as NDJSON (newline-delimited JSON). Each result row is a separate JSON line terminated by `\n`. Content-Type: application/x-ndjson This is suitable for streaming large result sets without buffering the entire response. Clients can process each line as it arrives.

(
    State(state): State<AppState>,
    headers: HeaderMap,
    QueryParams(db_param): QueryParams<DatabaseQueryParam>,
    axum::Json(body): axum::Json<crate::control::server::http::types::HttpQuery

Source from the content-addressed store, hash-verified

323/// This is suitable for streaming large result sets without buffering
324/// the entire response. Clients can process each line as it arrives.
325pub async fn query_ndjson(
326 State(state): State<AppState>,
327 headers: HeaderMap,
328 QueryParams(db_param): QueryParams<DatabaseQueryParam>,
329 axum::Json(body): axum::Json<crate::control::server::http::types::HttpQueryStreamRequest>,
330) -> impl IntoResponse {
331 use axum::response::Response;
332
333 let identity = match resolve_identity(&headers, &state, "http") {
334 Ok(id) => id,
335 Err(e) => return e.into_response(),
336 };
337 let database_id = match resolve_database_id(&headers, &db_param, &state) {
338 Ok(id) => id,
339 Err(e) => return e.into_response(),
340 };
341
342 let sql = body.sql.trim();
343 if sql.is_empty() {
344 return (StatusCode::BAD_REQUEST, "empty SQL").into_response();
345 }
346
347 let tenant_id = identity.tenant_id;
348
349 // Quota enforcement — reject before any planning or dispatch.
350 if let Err(e) = state.shared.check_tenant_quota(tenant_id) {
351 let body = serde_json::json!({ "error": e.to_string() });
352 return Response::builder()
353 .status(StatusCode::TOO_MANY_REQUESTS)
354 .header("Retry-After", "1")
355 .header("Content-Type", "application/json")
356 .body(axum::body::Body::from(body.to_string()))
357 .unwrap_or_else(|_| {
358 (StatusCode::INTERNAL_SERVER_ERROR, "encoding error").into_response()
359 });
360 }
361
362 let query_ctx = &state.query_ctx;
363
364 let auth_ctx = crate::control::server::session_auth::build_auth_context(&identity);
365 let perm_cache = state.shared.permission_cache.read().await;
366 let sec = crate::control::planner::context::PlanSecurityContext {
367 identity: &identity,
368 auth: &auth_ctx,
369 rls_store: &state.shared.rls,
370 permissions: &state.shared.permissions,
371 roles: &state.shared.roles,
372 permission_cache: Some(&*perm_cache),
373 };
374 let tasks = match query_ctx
375 .plan_sql_with_rls(sql, tenant_id, database_id, &sec)
376 .await
377 {
378 Ok(t) => t,
379 Err(e) => return (StatusCode::BAD_REQUEST, e.to_string()).into_response(),
380 };
381
382 state.shared.tenant_request_start(tenant_id);

Callers

nothing calls this directly

Calls 15

resolve_identityFunction · 0.85
resolve_database_idFunction · 0.85
build_auth_contextFunction · 0.85
generate_trace_idFunction · 0.85
into_responseMethod · 0.80
check_tenant_quotaMethod · 0.80
statusMethod · 0.80
to_stringMethod · 0.80
plan_sql_with_rlsMethod · 0.80
tenant_request_startMethod · 0.80
tenant_request_endMethod · 0.80
dispatch_to_data_planeFunction · 0.70

Tested by

no test coverage detected