use super::expr::{Expr, Identifier, OrderByExpr, QualifiedName};
use super::pattern::GraphPattern;
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Select(Box<SelectStatement>),
Insert(Box<InsertStatement>),
Update(Box<UpdateStatement>),
Delete(Box<DeleteStatement>),
CreateTable(CreateTableStatement),
CreateIndex(Box<CreateIndexStatement>),
CreateCollection(Box<CreateCollectionStatement>),
DropTable(DropTableStatement),
DropIndex(DropIndexStatement),
DropCollection(DropCollectionStatement),
Match(Box<MatchStatement>),
Explain(Box<Statement>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct WithClause {
pub name: Identifier,
pub columns: Vec<Identifier>,
pub query: Box<SelectStatement>,
}
impl WithClause {
#[must_use]
pub fn new(name: impl Into<Identifier>, query: SelectStatement) -> Self {
Self { name: name.into(), columns: vec![], query: Box::new(query) }
}
#[must_use]
pub fn with_columns(
name: impl Into<Identifier>,
columns: Vec<Identifier>,
query: SelectStatement,
) -> Self {
Self { name: name.into(), columns, query: Box::new(query) }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SelectStatement {
pub with_clauses: Vec<WithClause>,
pub distinct: bool,
pub projection: Vec<SelectItem>,
pub from: Vec<TableRef>,
pub match_clause: Option<GraphPattern>,
pub optional_match_clauses: Vec<GraphPattern>,
pub where_clause: Option<Expr>,
pub group_by: Vec<Expr>,
pub having: Option<Expr>,
pub order_by: Vec<OrderByExpr>,
pub limit: Option<Expr>,
pub offset: Option<Expr>,
pub set_op: Option<Box<SetOperation>>,
}
impl SelectStatement {
#[must_use]
pub const fn new(projection: Vec<SelectItem>) -> Self {
Self {
with_clauses: vec![],
distinct: false,
projection,
from: vec![],
match_clause: None,
optional_match_clauses: vec![],
where_clause: None,
group_by: vec![],
having: None,
order_by: vec![],
limit: None,
offset: None,
set_op: None,
}
}
#[must_use]
pub fn with_cte(mut self, cte: WithClause) -> Self {
self.with_clauses.push(cte);
self
}
#[must_use]
pub fn from(mut self, table: TableRef) -> Self {
self.from.push(table);
self
}
#[must_use]
pub fn where_clause(mut self, condition: Expr) -> Self {
self.where_clause = Some(condition);
self
}
#[must_use]
pub fn match_clause(mut self, pattern: GraphPattern) -> Self {
self.match_clause = Some(pattern);
self
}
#[must_use]
pub fn optional_match_clause(mut self, pattern: GraphPattern) -> Self {
self.optional_match_clauses.push(pattern);
self
}
#[must_use]
pub fn order_by(mut self, orders: Vec<OrderByExpr>) -> Self {
self.order_by = orders;
self
}
#[must_use]
pub fn limit(mut self, limit: Expr) -> Self {
self.limit = Some(limit);
self
}
#[must_use]
pub fn offset(mut self, offset: Expr) -> Self {
self.offset = Some(offset);
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchStatement {
pub pattern: GraphPattern,
pub where_clause: Option<Expr>,
pub return_clause: Vec<ReturnItem>,
pub distinct: bool,
pub order_by: Vec<OrderByExpr>,
pub skip: Option<Expr>,
pub limit: Option<Expr>,
}
impl MatchStatement {
#[must_use]
pub const fn new(pattern: GraphPattern, return_clause: Vec<ReturnItem>) -> Self {
Self {
pattern,
where_clause: None,
return_clause,
distinct: false,
order_by: vec![],
skip: None,
limit: None,
}
}
#[must_use]
pub fn where_clause(mut self, condition: Expr) -> Self {
self.where_clause = Some(condition);
self
}
#[must_use]
pub const fn distinct(mut self) -> Self {
self.distinct = true;
self
}
#[must_use]
pub fn order_by(mut self, orders: Vec<OrderByExpr>) -> Self {
self.order_by = orders;
self
}
#[must_use]
pub fn skip(mut self, skip: Expr) -> Self {
self.skip = Some(skip);
self
}
#[must_use]
pub fn limit(mut self, limit: Expr) -> Self {
self.limit = Some(limit);
self
}
#[must_use]
pub fn to_select(&self) -> SelectStatement {
let projection: Vec<SelectItem> = self
.return_clause
.iter()
.map(|item| match item {
ReturnItem::Wildcard => SelectItem::Wildcard,
ReturnItem::Expr { expr, alias } => {
SelectItem::Expr { expr: expr.clone(), alias: alias.clone() }
}
})
.collect();
SelectStatement {
with_clauses: vec![], distinct: self.distinct,
projection,
from: vec![], match_clause: Some(self.pattern.clone()),
optional_match_clauses: vec![], where_clause: self.where_clause.clone(),
group_by: vec![],
having: None,
order_by: self.order_by.clone(),
limit: self.limit.clone(),
offset: self.skip.clone(), set_op: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ReturnItem {
Wildcard,
Expr {
expr: Expr,
alias: Option<Identifier>,
},
}
impl ReturnItem {
#[must_use]
pub const fn wildcard() -> Self {
Self::Wildcard
}
#[must_use]
pub const fn expr(expr: Expr) -> Self {
Self::Expr { expr, alias: None }
}
#[must_use]
pub fn aliased(expr: Expr, alias: impl Into<Identifier>) -> Self {
Self::Expr { expr, alias: Some(alias.into()) }
}
}
impl From<Expr> for ReturnItem {
fn from(expr: Expr) -> Self {
Self::expr(expr)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SelectItem {
Expr {
expr: Expr,
alias: Option<Identifier>,
},
Wildcard,
QualifiedWildcard(QualifiedName),
}
impl SelectItem {
#[must_use]
pub const fn expr(expr: Expr) -> Self {
Self::Expr { expr, alias: None }
}
#[must_use]
pub fn aliased(expr: Expr, alias: impl Into<Identifier>) -> Self {
Self::Expr { expr, alias: Some(alias.into()) }
}
}
impl From<Expr> for SelectItem {
fn from(expr: Expr) -> Self {
Self::expr(expr)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TableRef {
Table {
name: QualifiedName,
alias: Option<TableAlias>,
},
Subquery {
query: Box<SelectStatement>,
alias: TableAlias,
},
Join(Box<JoinClause>),
TableFunction {
name: QualifiedName,
args: Vec<Expr>,
alias: Option<TableAlias>,
},
}
impl TableRef {
#[must_use]
pub fn table(name: impl Into<QualifiedName>) -> Self {
Self::Table { name: name.into(), alias: None }
}
#[must_use]
pub fn aliased(name: impl Into<QualifiedName>, alias: impl Into<TableAlias>) -> Self {
Self::Table { name: name.into(), alias: Some(alias.into()) }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TableAlias {
pub name: Identifier,
pub columns: Vec<Identifier>,
}
impl TableAlias {
#[must_use]
pub fn new(name: impl Into<Identifier>) -> Self {
Self { name: name.into(), columns: vec![] }
}
#[must_use]
pub fn with_columns(name: impl Into<Identifier>, columns: Vec<Identifier>) -> Self {
Self { name: name.into(), columns }
}
}
impl From<&str> for TableAlias {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for TableAlias {
fn from(s: String) -> Self {
Self::new(s)
}
}
impl From<Identifier> for TableAlias {
fn from(id: Identifier) -> Self {
Self::new(id)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct JoinClause {
pub left: TableRef,
pub right: TableRef,
pub join_type: JoinType,
pub condition: JoinCondition,
}
impl JoinClause {
#[must_use]
pub const fn inner(left: TableRef, right: TableRef, on: Expr) -> Self {
Self { left, right, join_type: JoinType::Inner, condition: JoinCondition::On(on) }
}
#[must_use]
pub const fn left_join(left: TableRef, right: TableRef, on: Expr) -> Self {
Self { left, right, join_type: JoinType::LeftOuter, condition: JoinCondition::On(on) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinType {
Inner,
LeftOuter,
RightOuter,
FullOuter,
Cross,
}
#[derive(Debug, Clone, PartialEq)]
pub enum JoinCondition {
On(Expr),
Using(Vec<Identifier>),
Natural,
None,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SetOperation {
pub op: SetOperator,
pub all: bool,
pub right: SelectStatement,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetOperator {
Union,
Intersect,
Except,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InsertStatement {
pub table: QualifiedName,
pub columns: Vec<Identifier>,
pub source: InsertSource,
pub on_conflict: Option<OnConflict>,
pub returning: Vec<SelectItem>,
}
impl InsertStatement {
#[must_use]
pub fn values(table: impl Into<QualifiedName>, values: Vec<Vec<Expr>>) -> Self {
Self {
table: table.into(),
columns: vec![],
source: InsertSource::Values(values),
on_conflict: None,
returning: vec![],
}
}
#[must_use]
pub fn select(table: impl Into<QualifiedName>, query: SelectStatement) -> Self {
Self {
table: table.into(),
columns: vec![],
source: InsertSource::Query(Box::new(query)),
on_conflict: None,
returning: vec![],
}
}
#[must_use]
pub fn columns(mut self, columns: Vec<Identifier>) -> Self {
self.columns = columns;
self
}
#[must_use]
pub fn returning(mut self, items: Vec<SelectItem>) -> Self {
self.returning = items;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum InsertSource {
Values(Vec<Vec<Expr>>),
Query(Box<SelectStatement>),
DefaultValues,
}
#[derive(Debug, Clone, PartialEq)]
pub struct OnConflict {
pub target: ConflictTarget,
pub action: ConflictAction,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConflictTarget {
Columns(Vec<Identifier>),
Constraint(Identifier),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConflictAction {
DoNothing,
DoUpdate {
assignments: Vec<Assignment>,
where_clause: Option<Expr>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct UpdateStatement {
pub table: QualifiedName,
pub alias: Option<TableAlias>,
pub assignments: Vec<Assignment>,
pub from: Vec<TableRef>,
pub match_clause: Option<GraphPattern>,
pub where_clause: Option<Expr>,
pub returning: Vec<SelectItem>,
}
impl UpdateStatement {
#[must_use]
pub fn new(table: impl Into<QualifiedName>, assignments: Vec<Assignment>) -> Self {
Self {
table: table.into(),
alias: None,
assignments,
from: vec![],
match_clause: None,
where_clause: None,
returning: vec![],
}
}
#[must_use]
pub fn where_clause(mut self, condition: Expr) -> Self {
self.where_clause = Some(condition);
self
}
#[must_use]
pub fn returning(mut self, items: Vec<SelectItem>) -> Self {
self.returning = items;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Assignment {
pub column: Identifier,
pub value: Expr,
}
impl Assignment {
#[must_use]
pub fn new(column: impl Into<Identifier>, value: Expr) -> Self {
Self { column: column.into(), value }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeleteStatement {
pub table: QualifiedName,
pub alias: Option<TableAlias>,
pub using: Vec<TableRef>,
pub match_clause: Option<GraphPattern>,
pub where_clause: Option<Expr>,
pub returning: Vec<SelectItem>,
}
impl DeleteStatement {
#[must_use]
pub fn new(table: impl Into<QualifiedName>) -> Self {
Self {
table: table.into(),
alias: None,
using: vec![],
match_clause: None,
where_clause: None,
returning: vec![],
}
}
#[must_use]
pub fn where_clause(mut self, condition: Expr) -> Self {
self.where_clause = Some(condition);
self
}
#[must_use]
pub fn returning(mut self, items: Vec<SelectItem>) -> Self {
self.returning = items;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateTableStatement {
pub if_not_exists: bool,
pub name: QualifiedName,
pub columns: Vec<ColumnDef>,
pub constraints: Vec<TableConstraint>,
}
impl CreateTableStatement {
#[must_use]
pub fn new(name: impl Into<QualifiedName>, columns: Vec<ColumnDef>) -> Self {
Self { if_not_exists: false, name: name.into(), columns, constraints: vec![] }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColumnDef {
pub name: Identifier,
pub data_type: DataType,
pub constraints: Vec<ColumnConstraint>,
}
impl ColumnDef {
#[must_use]
pub fn new(name: impl Into<Identifier>, data_type: DataType) -> Self {
Self { name: name.into(), data_type, constraints: vec![] }
}
#[must_use]
pub fn not_null(mut self) -> Self {
self.constraints.push(ColumnConstraint::NotNull);
self
}
#[must_use]
pub fn primary_key(mut self) -> Self {
self.constraints.push(ColumnConstraint::PrimaryKey);
self
}
#[must_use]
pub fn unique(mut self) -> Self {
self.constraints.push(ColumnConstraint::Unique);
self
}
#[must_use]
pub fn default(mut self, value: Expr) -> Self {
self.constraints.push(ColumnConstraint::Default(value));
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
Boolean,
SmallInt,
Integer,
BigInt,
Real,
DoublePrecision,
Numeric {
precision: Option<u32>,
scale: Option<u32>,
},
Varchar(Option<u32>),
Text,
Bytea,
Timestamp,
Date,
Time,
Interval,
Json,
Jsonb,
Uuid,
Vector(Option<u32>),
Array(Box<DataType>),
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ColumnConstraint {
NotNull,
Null,
Unique,
PrimaryKey,
References {
table: QualifiedName,
column: Option<Identifier>,
},
Check(Expr),
Default(Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub enum TableConstraint {
PrimaryKey {
name: Option<Identifier>,
columns: Vec<Identifier>,
},
Unique {
name: Option<Identifier>,
columns: Vec<Identifier>,
},
ForeignKey {
name: Option<Identifier>,
columns: Vec<Identifier>,
references_table: QualifiedName,
references_columns: Vec<Identifier>,
},
Check {
name: Option<Identifier>,
expr: Expr,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateIndexStatement {
pub unique: bool,
pub if_not_exists: bool,
pub name: Identifier,
pub table: QualifiedName,
pub columns: Vec<IndexColumn>,
pub using: Option<String>,
pub with: Vec<(String, String)>,
pub where_clause: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IndexColumn {
pub expr: Expr,
pub asc: Option<bool>,
pub nulls_first: Option<bool>,
pub opclass: Option<String>,
}
impl IndexColumn {
#[must_use]
pub const fn new(expr: Expr) -> Self {
Self { expr, asc: None, nulls_first: None, opclass: None }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DropTableStatement {
pub if_exists: bool,
pub names: Vec<QualifiedName>,
pub cascade: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DropIndexStatement {
pub if_exists: bool,
pub names: Vec<QualifiedName>,
pub cascade: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateCollectionStatement {
pub if_not_exists: bool,
pub name: Identifier,
pub vectors: Vec<VectorDef>,
pub payload_fields: Vec<PayloadFieldDef>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PayloadFieldDef {
pub name: Identifier,
pub data_type: DataType,
pub indexed: bool,
}
impl CreateCollectionStatement {
#[must_use]
pub fn new(name: impl Into<Identifier>, vectors: Vec<VectorDef>) -> Self {
Self { if_not_exists: false, name: name.into(), vectors, payload_fields: vec![] }
}
#[must_use]
pub fn with_payload(
name: impl Into<Identifier>,
vectors: Vec<VectorDef>,
payload_fields: Vec<PayloadFieldDef>,
) -> Self {
Self { if_not_exists: false, name: name.into(), vectors, payload_fields }
}
#[must_use]
pub const fn if_not_exists(mut self) -> Self {
self.if_not_exists = true;
self
}
#[must_use]
pub fn with_field(mut self, field: PayloadFieldDef) -> Self {
self.payload_fields.push(field);
self
}
}
impl PayloadFieldDef {
#[must_use]
pub fn new(name: impl Into<Identifier>, data_type: DataType) -> Self {
Self { name: name.into(), data_type, indexed: false }
}
#[must_use]
pub const fn indexed(mut self) -> Self {
self.indexed = true;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VectorDef {
pub name: Identifier,
pub vector_type: VectorTypeDef,
pub using: Option<String>,
pub with_options: Vec<(String, String)>,
}
impl VectorDef {
#[must_use]
pub fn new(name: impl Into<Identifier>, vector_type: VectorTypeDef) -> Self {
Self { name: name.into(), vector_type, using: None, with_options: vec![] }
}
#[must_use]
pub fn using(mut self, method: impl Into<String>) -> Self {
self.using = Some(method.into());
self
}
#[must_use]
pub fn with_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.with_options.push((key.into(), value.into()));
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VectorTypeDef {
Vector {
dimension: u32,
},
SparseVector {
max_dimension: Option<u32>,
},
MultiVector {
token_dim: u32,
},
BinaryVector {
bits: u32,
},
}
impl VectorTypeDef {
#[must_use]
pub const fn dense(dimension: u32) -> Self {
Self::Vector { dimension }
}
#[must_use]
pub const fn sparse(max_dimension: Option<u32>) -> Self {
Self::SparseVector { max_dimension }
}
#[must_use]
pub const fn multi(token_dim: u32) -> Self {
Self::MultiVector { token_dim }
}
#[must_use]
pub const fn binary(bits: u32) -> Self {
Self::BinaryVector { bits }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DropCollectionStatement {
pub if_exists: bool,
pub names: Vec<Identifier>,
pub cascade: bool,
}
impl DropCollectionStatement {
#[must_use]
pub fn new(names: Vec<Identifier>) -> Self {
Self { if_exists: false, names, cascade: false }
}
#[must_use]
pub const fn if_exists(mut self) -> Self {
self.if_exists = true;
self
}
#[must_use]
pub const fn cascade(mut self) -> Self {
self.cascade = true;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn select_builder() {
let stmt = SelectStatement::new(vec![SelectItem::Wildcard])
.from(TableRef::table(QualifiedName::simple("users")))
.where_clause(Expr::column(QualifiedName::simple("id")).eq(Expr::integer(1)));
assert!(stmt.where_clause.is_some());
assert_eq!(stmt.from.len(), 1);
}
#[test]
fn insert_builder() {
let stmt = InsertStatement::values(
QualifiedName::simple("users"),
vec![vec![Expr::string("Alice"), Expr::integer(30)]],
)
.columns(vec![Identifier::new("name"), Identifier::new("age")]);
assert_eq!(stmt.columns.len(), 2);
}
#[test]
fn column_def_builder() {
let col = ColumnDef::new("id", DataType::BigInt).primary_key().not_null();
assert_eq!(col.constraints.len(), 2);
}
#[test]
fn assignment() {
let assign = Assignment::new("status", Expr::string("active"));
assert_eq!(assign.column.name, "status");
}
}