use serde::{Deserialize, Serialize};
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ColumnStorageMode {
#[default]
Default,
Dictionary,
ContentAddressed,
Columnar,
}
impl fmt::Display for ColumnStorageMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ColumnStorageMode::Default => write!(f, "DEFAULT"),
ColumnStorageMode::Dictionary => write!(f, "DICTIONARY"),
ColumnStorageMode::ContentAddressed => write!(f, "CONTENT_ADDRESSED"),
ColumnStorageMode::Columnar => write!(f, "COLUMNAR"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataType {
Boolean,
Int2,
Int4,
Int8,
Float4,
Float8,
Numeric,
Varchar(Option<usize>),
Text,
Char(usize),
Bytea,
Date,
Time,
Timestamp,
Timestamptz,
Interval,
Uuid,
Json,
Jsonb,
Array(Box<DataType>),
Vector(usize),
}
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataType::Boolean => write!(f, "BOOLEAN"),
DataType::Int2 => write!(f, "INT2"),
DataType::Int4 => write!(f, "INT4"),
DataType::Int8 => write!(f, "INT8"),
DataType::Float4 => write!(f, "FLOAT4"),
DataType::Float8 => write!(f, "FLOAT8"),
DataType::Numeric => write!(f, "NUMERIC"),
DataType::Varchar(Some(n)) => write!(f, "VARCHAR({})", n),
DataType::Varchar(None) => write!(f, "VARCHAR"),
DataType::Text => write!(f, "TEXT"),
DataType::Char(n) => write!(f, "CHAR({})", n),
DataType::Bytea => write!(f, "BYTEA"),
DataType::Date => write!(f, "DATE"),
DataType::Time => write!(f, "TIME"),
DataType::Timestamp => write!(f, "TIMESTAMP"),
DataType::Timestamptz => write!(f, "TIMESTAMPTZ"),
DataType::Interval => write!(f, "INTERVAL"),
DataType::Uuid => write!(f, "UUID"),
DataType::Json => write!(f, "JSON"),
DataType::Jsonb => write!(f, "JSONB"),
DataType::Array(inner) => write!(f, "{}[]", inner),
DataType::Vector(dim) => write!(f, "VECTOR({})", dim),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Null,
Boolean(bool),
Int2(i16),
Int4(i32),
Int8(i64),
Float4(f32),
Float8(f64),
Numeric(String),
String(String),
Bytes(Vec<u8>),
Uuid(uuid::Uuid),
Timestamp(chrono::DateTime<chrono::Utc>),
Date(chrono::NaiveDate),
Time(chrono::NaiveTime),
Interval(i64),
Json(String),
Array(Vec<Value>),
Vector(Vec<f32>),
DictRef {
dict_id: u32,
},
CasRef {
hash: [u8; 32],
},
ColumnarRef,
}
impl Value {
pub fn data_type(&self) -> DataType {
match self {
Value::Null => DataType::Text, Value::Boolean(_) => DataType::Boolean,
Value::Int2(_) => DataType::Int2,
Value::Int4(_) => DataType::Int4,
Value::Int8(_) => DataType::Int8,
Value::Float4(_) => DataType::Float4,
Value::Float8(_) => DataType::Float8,
Value::Numeric(_) => DataType::Numeric,
Value::String(_) => DataType::Text,
Value::Bytes(_) => DataType::Bytea,
Value::Uuid(_) => DataType::Uuid,
Value::Timestamp(_) => DataType::Timestamp,
Value::Date(_) => DataType::Date,
Value::Time(_) => DataType::Time,
Value::Interval(_) => DataType::Interval,
Value::Json(_) => DataType::Jsonb,
Value::Array(arr) => {
if let Some(first) = arr.first() {
DataType::Array(Box::new(first.data_type()))
} else {
DataType::Array(Box::new(DataType::Text))
}
}
Value::Vector(vec) => DataType::Vector(vec.len()),
Value::DictRef { .. } => DataType::Text,
Value::CasRef { .. } => DataType::Text,
Value::ColumnarRef => DataType::Text,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Null => write!(f, "NULL"),
Value::Boolean(b) => write!(f, "{}", b),
Value::Int2(i) => write!(f, "{}", i),
Value::Int4(i) => write!(f, "{}", i),
Value::Int8(i) => write!(f, "{}", i),
Value::Float4(fl) => write!(f, "{}", fl),
Value::Float8(fl) => write!(f, "{}", fl),
Value::Numeric(n) => write!(f, "{}", n),
Value::String(s) => write!(f, "'{}'", s),
Value::Bytes(b) => write!(f, "\\x{}", hex::encode(b)),
Value::Uuid(u) => write!(f, "'{}'", u),
Value::Timestamp(ts) => write!(f, "'{}'", ts.to_rfc3339()),
Value::Date(d) => write!(f, "'{}'", d.format("%Y-%m-%d")),
Value::Time(t) => write!(f, "'{}'", t.format("%H:%M:%S%.f")),
Value::Interval(micros) => {
let total_secs = micros / 1_000_000;
let days = total_secs / 86400;
let hours = (total_secs % 86400) / 3600;
let mins = (total_secs % 3600) / 60;
let secs = total_secs % 60;
if days > 0 {
write!(f, "{} days {:02}:{:02}:{:02}", days, hours, mins, secs)
} else {
write!(f, "{:02}:{:02}:{:02}", hours, mins, secs)
}
}
Value::Json(j) => write!(f, "'{}'", j),
Value::Array(arr) => {
write!(f, "{{")?;
for (i, v) in arr.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match v {
Value::Int2(n) => write!(f, "{}", n)?,
Value::Int4(n) => write!(f, "{}", n)?,
Value::Int8(n) => write!(f, "{}", n)?,
Value::Float4(n) => write!(f, "{}", n)?,
Value::Float8(n) => write!(f, "{}", n)?,
Value::String(s) => write!(f, "\"{}\"", s)?,
Value::Boolean(b) => write!(f, "{}", b)?,
Value::Null => write!(f, "NULL")?,
other => write!(f, "{}", other)?,
}
}
write!(f, "}}")
}
Value::Vector(vec) => write!(f, "[{}]", vec.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", ")),
Value::DictRef { dict_id } => write!(f, "<dict:{}>", dict_id),
Value::CasRef { hash } => write!(f, "<cas:{}>", hex::encode(&hash[..8])),
Value::ColumnarRef => write!(f, "<columnar>"),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Tuple {
pub values: Vec<Value>,
pub row_id: Option<u64>,
#[serde(skip)]
pub branch_id: Option<u64>,
}
impl Default for Tuple {
fn default() -> Self {
Self { values: vec![], row_id: None, branch_id: None }
}
}
impl Tuple {
pub fn new(values: Vec<Value>) -> Self {
Self { values, row_id: None, branch_id: None }
}
pub fn with_row_id(values: Vec<Value>, row_id: u64) -> Self {
Self { values, row_id: Some(row_id), branch_id: None }
}
pub fn with_row_and_branch_id(values: Vec<Value>, row_id: u64, branch_id: u64) -> Self {
Self { values, row_id: Some(row_id), branch_id: Some(branch_id) }
}
pub fn get(&self, index: usize) -> Option<&Value> {
self.values.get(index)
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn schema(&self) -> Schema {
let columns: Vec<Column> = self.values
.iter()
.enumerate()
.map(|(i, val)| {
Column {
name: format!("column_{}", i),
data_type: val.data_type(),
nullable: matches!(val, Value::Null),
primary_key: false,
source_table: None,
source_table_name: None,
default_expr: None,
unique: false,
storage_mode: ColumnStorageMode::Default,
}
})
.collect();
Schema::new(columns)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Column {
pub name: String,
pub data_type: DataType,
pub nullable: bool,
pub primary_key: bool,
#[serde(default)]
pub source_table: Option<String>,
#[serde(default)]
pub source_table_name: Option<String>,
#[serde(default)]
pub default_expr: Option<String>,
#[serde(default)]
pub unique: bool,
#[serde(default)]
pub storage_mode: ColumnStorageMode,
}
impl Column {
pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
Self {
name: name.into(),
data_type,
nullable: true,
primary_key: false,
source_table: None,
source_table_name: None,
default_expr: None,
unique: false,
storage_mode: ColumnStorageMode::Default,
}
}
pub fn with_source_table(mut self, table: impl Into<String>) -> Self {
self.source_table = Some(table.into());
self
}
pub fn not_null(mut self) -> Self {
self.nullable = false;
self
}
pub fn primary_key(mut self) -> Self {
self.primary_key = true;
self.nullable = false;
self
}
pub fn with_default(mut self, default_expr: impl Into<String>) -> Self {
self.default_expr = Some(default_expr.into());
self
}
pub fn unique(mut self) -> Self {
self.unique = true;
self
}
pub fn with_storage(mut self, mode: ColumnStorageMode) -> Self {
self.storage_mode = mode;
self
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Schema {
pub columns: Vec<Column>,
}
impl Schema {
pub fn new(columns: Vec<Column>) -> Self {
Self { columns }
}
pub fn get_column(&self, name: &str) -> Option<&Column> {
self.columns.iter().find(|c| c.name == name)
}
pub fn get_column_index(&self, name: &str) -> Option<usize> {
self.columns.iter().position(|c| c.name == name)
}
pub fn get_qualified_column_index(&self, table: Option<&str>, name: &str) -> Option<usize> {
if let Some(table_name) = table {
self.columns.iter().position(|c| {
(c.source_table.as_deref() == Some(table_name)
|| c.source_table_name.as_deref() == Some(table_name))
&& c.name == name
})
} else {
self.get_column_index(name)
}
}
pub fn get_column_at(&self, index: usize) -> Option<&Column> {
self.columns.get(index)
}
pub fn get_column_at_mut(&mut self, index: usize) -> Option<&mut Column> {
self.columns.get_mut(index)
}
pub fn len(&self) -> usize {
self.columns.len()
}
pub fn is_empty(&self) -> bool {
self.columns.is_empty()
}
pub fn merge(&self, other: &Schema) -> Self {
let mut columns = self.columns.clone();
columns.extend(other.columns.clone());
Self { columns }
}
#[must_use]
pub fn with_source_table_name(mut self, table: &str) -> Self {
for col in &mut self.columns {
if col.source_table.is_none() {
col.source_table = Some(table.to_string());
}
if col.source_table_name.is_none() {
col.source_table_name = Some(table.to_string());
}
}
self
}
pub fn project(&self, indices: &[usize]) -> Self {
let columns = indices
.iter()
.filter_map(|&i| self.columns.get(i).cloned())
.collect();
Self { columns }
}
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Value::Null => {
0u8.hash(state); }
Value::Boolean(b) => {
1u8.hash(state);
b.hash(state);
}
Value::Int2(i) => {
2u8.hash(state);
(*i as i64).hash(state);
}
Value::Int4(i) => {
2u8.hash(state);
(*i as i64).hash(state);
}
Value::Int8(i) => {
2u8.hash(state);
i.hash(state);
}
Value::Float4(f) => {
3u8.hash(state);
(*f as f64).to_bits().hash(state);
}
Value::Float8(f) => {
3u8.hash(state);
f.to_bits().hash(state);
}
Value::Numeric(n) => {
n.hash(state);
}
Value::String(s) => s.hash(state),
Value::Bytes(b) => b.hash(state),
Value::Uuid(u) => u.hash(state),
Value::Timestamp(ts) => {
ts.timestamp_nanos_opt().hash(state);
}
Value::Date(d) => {
d.to_string().hash(state);
}
Value::Time(t) => {
t.to_string().hash(state);
}
Value::Json(j) => {
j.to_string().hash(state);
}
Value::Array(arr) => {
arr.len().hash(state);
for val in arr {
val.hash(state);
}
}
Value::Vector(vec) => {
vec.len().hash(state);
for f in vec {
f.to_bits().hash(state);
}
}
Value::DictRef { dict_id } => {
dict_id.hash(state);
}
Value::CasRef { hash } => {
hash.hash(state);
}
Value::ColumnarRef => {
255u8.hash(state);
}
Value::Interval(microseconds) => {
microseconds.hash(state);
}
}
}
}
impl Eq for Value {}
mod hex {
use std::fmt::Write;
pub fn encode(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
let _ = write!(s, "{:02x}", b);
}
s
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorStoreInfo {
pub name: String,
pub dimensions: u32,
pub vector_count: u64,
pub created_at: String,
pub metric: String,
pub index_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentSession {
pub id: String,
pub name: String,
pub created_at: String,
pub updated_at: String,
pub session_id: String,
pub message_count: u32,
pub token_count: u32,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMessage {
pub id: String,
pub role: String,
pub content: String,
pub created_at: String,
pub name: String,
pub function_call: Option<String>,
pub tool_calls: Option<serde_json::Value>,
pub metadata: serde_json::Value,
pub timestamp: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentData {
pub id: String,
pub content: String,
pub metadata: Option<serde_json::Value>,
pub created_at: String,
pub updated_at: String,
pub chunks: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentMetadata {
pub id: String,
pub size: usize,
pub created_at: String,
pub updated_at: String,
pub content: String,
pub metadata: Option<serde_json::Value>,
}