use std::sync::Arc;
use futures::stream;
use pgwire::api::results::{DataRowEncoder, QueryResponse, Response, Tag};
use pgwire::error::{ErrorInfo, PgWireError, PgWireResult};
use sonic_rs;
use crate::bridge::envelope::PhysicalPlan;
use crate::data::executor::response_codec::decode_payload_to_json;
use nodedb_physical::physical_plan::DocumentOp;
use crate::control::server::shared::sql::staging_predicates::{
StagedTagKind, extract_affected_count,
};
use super::super::types::text_field;
pub(super) use crate::control::server::response_shape::types::{PlanKind, describe_plan};
pub(super) fn is_calvin_foldable(plan: &PhysicalPlan) -> bool {
use nodedb_physical::physical_plan::KvOp;
match plan {
PhysicalPlan::Document(DocumentOp::PointPut { .. })
| PhysicalPlan::Document(DocumentOp::PointInsert { .. }) => true,
PhysicalPlan::Document(DocumentOp::PointUpdate {
returning: None, ..
})
| PhysicalPlan::Document(DocumentOp::PointDelete {
returning: None, ..
}) => true,
PhysicalPlan::Kv(KvOp::Put { .. })
| PhysicalPlan::Kv(KvOp::Insert { .. })
| PhysicalPlan::Kv(KvOp::InsertIfAbsent { .. })
| PhysicalPlan::Kv(KvOp::Delete { .. }) => true,
PhysicalPlan::Document(_)
| PhysicalPlan::Kv(_)
| PhysicalPlan::Vector(_)
| PhysicalPlan::Graph(_)
| PhysicalPlan::Text(_)
| PhysicalPlan::Columnar(_)
| PhysicalPlan::Timeseries(_)
| PhysicalPlan::Spatial(_)
| PhysicalPlan::Crdt(_)
| PhysicalPlan::Query(_)
| PhysicalPlan::Meta(_)
| PhysicalPlan::Array(_)
| PhysicalPlan::ClusterArray(_) => false,
}
}
pub(super) fn tag_from_staged(kind: StagedTagKind, affected: usize) -> Tag {
match kind {
StagedTagKind::Insert => Tag::new("INSERT").with_rows(affected),
StagedTagKind::Update => Tag::new("UPDATE").with_rows(affected),
StagedTagKind::Delete => Tag::new("DELETE").with_rows(affected),
StagedTagKind::KvUpsert { updated: true } => Tag::new("UPDATE").with_rows(affected),
StagedTagKind::KvUpsert { updated: false } => Tag::new("INSERT").with_rows(affected),
StagedTagKind::DocUpsert => Tag::new("UPSERT").with_rows(affected),
StagedTagKind::Merge => Tag::new("MERGE").with_rows(affected),
StagedTagKind::UpdateFromJoin => Tag::new("UPDATE").with_rows(affected),
StagedTagKind::RawPayload => Tag::new("SELECT").with_rows(affected),
}
}
pub(super) fn calvin_tag_for_plan(plan: &PhysicalPlan) -> PgWireResult<Tag> {
use nodedb_physical::physical_plan::KvOp;
match plan {
PhysicalPlan::Document(DocumentOp::PointPut { .. })
| PhysicalPlan::Document(DocumentOp::PointInsert { .. })
| PhysicalPlan::Kv(KvOp::Put { .. })
| PhysicalPlan::Kv(KvOp::Insert { .. })
| PhysicalPlan::Kv(KvOp::InsertIfAbsent { .. }) => Ok(Tag::new("INSERT").with_rows(1)),
PhysicalPlan::Document(DocumentOp::PointUpdate {
returning: None, ..
}) => Ok(Tag::new("UPDATE").with_rows(1)),
PhysicalPlan::Document(DocumentOp::PointDelete {
returning: None, ..
})
| PhysicalPlan::Kv(KvOp::Delete { .. }) => Ok(Tag::new("DELETE").with_rows(1)),
other => Err(invalid_plan_shape(format!(
"calvin_tag_for_plan called on non-foldable plan: {other:?}"
))),
}
}
pub(super) struct ShapedResponse {
pub response: Response,
pub notice: Option<String>,
}
impl From<Response> for ShapedResponse {
fn from(response: Response) -> Self {
Self {
response,
notice: None,
}
}
}
pub(super) fn payload_to_response(payload: &[u8], kind: PlanKind) -> PgWireResult<ShapedResponse> {
match kind {
PlanKind::Execution => Ok(Response::Execution(Tag::new("OK")).into()),
PlanKind::DmlResult(tag) => {
let count = if payload.is_empty() {
1
} else {
extract_affected_count(payload).unwrap_or(1) as usize
};
Ok(Response::Execution(Tag::new(tag).with_rows(count)).into())
}
PlanKind::ArraySlice | PlanKind::ReturningRows | PlanKind::SingleDocument => {
Err(invalid_plan_shape(format!(
"payload_to_response cannot handle plan kind {kind:?}"
)))
}
PlanKind::MultiRow => Ok(multirow_payload_to_response(payload)),
}
}
pub(super) fn multirow_payload_to_response(payload: &[u8]) -> ShapedResponse {
let schema = Arc::new(vec![text_field("result")]);
if payload.is_empty() {
return Response::Query(QueryResponse::new(schema, stream::empty())).into();
}
let text = decode_payload_to_json(payload);
if let Ok(serde_json::Value::Array(items)) = sonic_rs::from_str::<serde_json::Value>(&text) {
let row_schema = schema.clone();
let rows: Vec<_> = items
.iter()
.map(|item| {
let mut encoder = DataRowEncoder::new(row_schema.clone());
let _ = encoder.encode_field(&item.to_string());
Ok(encoder.take_row())
})
.collect();
return Response::Query(QueryResponse::new(schema, stream::iter(rows))).into();
}
let mut encoder = DataRowEncoder::new(schema.clone());
if let Err(error) = encoder.encode_field(&text) {
tracing::error!(%error, "failed to encode field");
return Response::Execution(Tag::new("ERROR")).into();
}
let row = encoder.take_row();
Response::Query(QueryResponse::new(schema, stream::iter(vec![Ok(row)]))).into()
}
fn invalid_plan_shape(message: String) -> PgWireError {
PgWireError::UserError(Box::new(ErrorInfo::new(
"ERROR".to_owned(),
"XX000".to_owned(),
message,
)))
}
#[cfg(test)]
mod tests {
use super::*;
use nodedb_physical::physical_plan::KvOp;
#[test]
fn calvin_tag_rejects_non_foldable_plan() {
let plan = PhysicalPlan::Kv(KvOp::Get {
collection: "items".into(),
key: Vec::new(),
rls_filters: Vec::new(),
surrogate_ceiling: None,
});
assert!(calvin_tag_for_plan(&plan).is_err());
}
#[test]
fn passthrough_rejects_precomposed_shapes() {
assert!(payload_to_response(&[], PlanKind::ArraySlice).is_err());
assert!(payload_to_response(&[], PlanKind::ReturningRows).is_err());
assert!(payload_to_response(&[], PlanKind::SingleDocument).is_err());
}
#[test]
fn multirow_helper_remains_infallible() {
let shaped = multirow_payload_to_response(&[]);
assert!(matches!(shaped.response, Response::Query(_)));
}
#[test]
fn foldable_tag_still_matches_operation() {
let plan = PhysicalPlan::Kv(KvOp::Delete {
collection: "items".into(),
keys: Vec::new(),
});
assert!(calvin_tag_for_plan(&plan).is_ok());
}
}