use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
use vibesql_types::SqlValue;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryRequest {
pub sql: String,
#[serde(default)]
pub params: Vec<JsonValue>,
#[serde(default)]
pub limit: Option<usize>,
#[serde(default)]
pub offset: Option<usize>,
}
impl QueryRequest {
pub fn to_sql_values(&self) -> Result<Vec<SqlValue>, String> {
self.params.iter().map(json_to_sql_value).collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
pub columns: Vec<String>,
pub rows: Vec<Vec<JsonValue>>,
pub row_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MutationResponse {
pub rows_affected: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableInfo {
pub name: String,
pub columns: Vec<ColumnInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnInfo {
pub name: String,
pub data_type: String,
pub nullable: bool,
pub primary_key: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
pub error: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
}
impl ErrorResponse {
pub fn new(error: impl Into<String>) -> Self {
Self { error: error.into(), code: None }
}
pub fn with_code(error: impl Into<String>, code: impl Into<String>) -> Self {
Self { error: error.into(), code: Some(code.into()) }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
pub status: String,
pub version: String,
}
pub fn sql_value_to_json(val: &SqlValue) -> JsonValue {
match val {
SqlValue::Null => JsonValue::Null,
SqlValue::Boolean(b) => JsonValue::Bool(*b),
SqlValue::Integer(i) => json!(*i),
SqlValue::Smallint(i) => json!(*i as i64),
SqlValue::Bigint(i) => json!(*i),
SqlValue::Unsigned(u) => json!(*u),
SqlValue::Numeric(f) => {
if f.is_nan() || f.is_infinite() {
JsonValue::Null
} else {
json!(*f)
}
}
SqlValue::Float(f) => {
if f.is_nan() || f.is_infinite() {
JsonValue::Null
} else {
json!(*f as f64)
}
}
SqlValue::Real(f) => {
if f.is_nan() || f.is_infinite() {
JsonValue::Null
} else {
json!(*f as f64)
}
}
SqlValue::Double(f) => {
if f.is_nan() || f.is_infinite() {
JsonValue::Null
} else {
json!(*f)
}
}
SqlValue::Character(s) | SqlValue::Varchar(s) => JsonValue::String(s.to_string()),
SqlValue::Timestamp(ts) => JsonValue::String(format!("{:?}", ts)),
SqlValue::Date(d) => JsonValue::String(format!("{:?}", d)),
SqlValue::Time(t) => JsonValue::String(format!("{:?}", t)),
SqlValue::Interval(_) => JsonValue::Null, SqlValue::Vector(v) => json!(v), }
}
pub fn json_to_sql_value(val: &JsonValue) -> Result<SqlValue, String> {
match val {
JsonValue::Null => Ok(SqlValue::Null),
JsonValue::Bool(b) => Ok(SqlValue::Boolean(*b)),
JsonValue::Number(n) => {
if let Some(i) = n.as_i64() {
Ok(SqlValue::Integer(i))
} else if let Some(f) = n.as_f64() {
Ok(SqlValue::Numeric(f))
} else {
Err("Invalid number".to_string())
}
}
JsonValue::String(s) => Ok(SqlValue::Varchar(arcstr::ArcStr::from(s.clone()))),
JsonValue::Array(_) => Err("Arrays not yet supported".to_string()),
JsonValue::Object(_) => Err("Objects not yet supported".to_string()),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlobUploadResponse {
pub id: String,
pub size: i64,
pub content_type: String,
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlobMetadataResponse {
pub id: String,
pub size: i64,
pub content_type: String,
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartialUpdateFallbacks {
pub disabled: u64,
pub threshold_exceeded: u64,
pub row_count_mismatch: u64,
pub pk_mismatch: u64,
pub no_changes: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscriptionEfficiencyStats {
pub partial_update_efficiency: f64,
pub total_bytes_saved: u64,
pub fallbacks: PartialUpdateFallbacks,
pub partial_updates_sent: u64,
pub full_updates_sent: u64,
}