use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use crate::sql::QueryExecutionError;
use crate::types::{DEFAULT_JSON_SIZE, DEFAULT_TEXT_SIZE};
pub fn parse_data_type_with_precision(
type_str: &str,
) -> Result<(String, u16, Option<crate::types::DistanceType>), QueryExecutionError> {
#[cfg(feature = "log")]
crate::log::debug!("parse_data_type_with_precision called with: {}", type_str);
let type_str = type_str.to_uppercase();
if let Some(open_paren) = type_str.find('(') {
let close_paren = type_str
.find(')')
.ok_or(QueryExecutionError::TypeMismatch)?;
let param_str = type_str[open_paren + 1..close_paren].trim();
let param = param_str
.parse::<u16>()
.map_err(|_| QueryExecutionError::TypeMismatch)?;
let base_type = type_str[..open_paren].trim();
if base_type == "VECTOR" {
if param < 1 || param > 4096 {
return Err(QueryExecutionError::TypeMismatch);
}
}
let mut distance_type = None;
if base_type == "VECTOR" {
if type_str.contains("WITH DISTANCE=L2") {
distance_type = Some(crate::types::DistanceType::L2);
} else if type_str.contains("WITH DISTANCE=INNER_PRODUCT")
|| type_str.contains("WITH DISTANCE=IP")
{
distance_type = Some(crate::types::DistanceType::InnerProduct);
} else if type_str.contains("WITH DISTANCE=COSINE") {
distance_type = Some(crate::types::DistanceType::Cosine);
}
}
match base_type {
"INT" | "INTEGER" | "BIGINT" | "TINYINT" | "SMALLINT" | "INT16" | "INT32" | "INT64"
| "UINT" | "UINTEGER" | "UBIGINT" | "UTINYINT" | "USMALLINT" | "UINT16" | "UINT32"
| "UINT64" | "FLOAT" | "DOUBLE" | "REAL" | "FLOAT32" | "FLOAT64" | "VARCHAR"
| "CHAR" | "TEXT" | "BOOL" | "BOOLEAN" | "TIMESTAMP" | "TIMESTAMPTZ" | "JSON"
| "VECTOR" => Ok((base_type.to_string(), param, distance_type)),
_ => Err(QueryExecutionError::TypeMismatch),
}
} else {
let base_type = type_str.trim();
match base_type {
"INT" | "INTEGER" | "BIGINT" | "TINYINT" | "SMALLINT" | "INT16" | "INT32" | "INT64"
| "UINT" | "UINTEGER" | "UBIGINT" | "UTINYINT" | "USMALLINT" | "UINT16" | "UINT32"
| "UINT64" | "FLOAT" | "DOUBLE" | "REAL" | "FLOAT32" | "FLOAT64" | "BOOL"
| "BOOLEAN" => Ok((base_type.to_string(), 8, None)),
"VARCHAR" | "CHAR" => Ok((base_type.to_string(), 64, None)),
"TEXT" => Ok((base_type.to_string(), DEFAULT_TEXT_SIZE as u16, None)),
"JSON" => Ok((base_type.to_string(), DEFAULT_JSON_SIZE as u16, None)),
"TIMESTAMP" | "TIMESTAMPTZ" => Ok((base_type.to_string(), 6, None)),
_ => Err(QueryExecutionError::TypeMismatch),
}
}
}
pub fn check_memory_limit(
estimated_usage: usize,
max_memory_mb: Option<u32>,
) -> Result<(), QueryExecutionError> {
if let Some(max_mb) = max_memory_mb {
let max_bytes = (max_mb as usize) * 1024 * 1024;
if estimated_usage > max_bytes {
return Err(QueryExecutionError::ResourceLimitExceeded(format!(
"Query exceeds memory limit: {}MB estimated, {}MB allowed",
estimated_usage / (1024 * 1024),
max_mb
)));
}
}
Ok(())
}
pub fn process_at_time_zone(
timestamp: &crate::types::db_timestamp,
timezone_spec: &str,
) -> Result<crate::types::db_timestamp, QueryExecutionError> {
let tz_offset = if timezone_spec.starts_with('+') || timezone_spec.starts_with('-') {
let parts: Vec<&str> = timezone_spec.split(':').collect();
if parts.len() == 2 {
let hours = parts[0]
.parse::<i32>()
.map_err(|_| QueryExecutionError::TypeMismatch)?;
let minutes = parts[1]
.parse::<i32>()
.map_err(|_| QueryExecutionError::TypeMismatch)?;
((hours * 3600) + (minutes * 60)) as i16
} else {
return Err(QueryExecutionError::TypeMismatch);
}
} else {
crate::types::get_timezone_offset(timezone_spec).ok_or(QueryExecutionError::TypeMismatch)?
};
Ok(crate::types::convert_timezone(timestamp, tz_offset))
}
pub fn process_timezone_function(timezone_spec: &str) -> Result<i16, QueryExecutionError> {
if timezone_spec.starts_with('+') || timezone_spec.starts_with('-') {
let parts: Vec<&str> = timezone_spec.split(':').collect();
if parts.len() == 2 {
let hours = parts[0]
.parse::<i32>()
.map_err(|_| QueryExecutionError::TypeMismatch)?;
let minutes = parts[1]
.parse::<i32>()
.map_err(|_| QueryExecutionError::TypeMismatch)?;
Ok(((hours * 3600) + (minutes * 60)) as i16)
} else {
Err(QueryExecutionError::TypeMismatch)
}
} else {
crate::types::get_timezone_offset(timezone_spec).ok_or(QueryExecutionError::TypeMismatch)
}
}
pub fn process_to_char(
timestamp: &crate::types::db_timestamp,
format: &str,
) -> Result<String, QueryExecutionError> {
Ok(crate::types::time_format::to_char(timestamp, format))
}
pub fn process_to_iso8601(
timestamp: &crate::types::db_timestamp,
) -> Result<String, QueryExecutionError> {
Ok(crate::types::time_format::to_iso8601(timestamp))
}
pub fn process_to_epoch(
timestamp: &crate::types::db_timestamp,
) -> Result<f64, QueryExecutionError> {
Ok(crate::types::time_format::to_epoch(timestamp))
}