use std::fmt;
use crate::span::Span;
#[derive(Clone, Debug, PartialEq)]
pub struct Statement {
pub kind: StatementKind,
pub span: Span,
}
impl Statement {
#[must_use]
pub const fn new(kind: StatementKind, span: Span) -> Self {
Self { kind, span }
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum StatementKind {
Select(SelectStmt),
Insert(InsertStmt),
Update(UpdateStmt),
Delete(DeleteStmt),
CreateTable(CreateTableStmt),
DropTable(DropTableStmt),
CreateIndex(CreateIndexStmt),
DropIndex(DropIndexStmt),
ShowTables,
ShowEmbeddings {
limit: Option<Expr>,
},
ShowVectorIndex,
CountEmbeddings,
Describe(DescribeStmt),
Node(NodeStmt),
Edge(EdgeStmt),
Neighbors(NeighborsStmt),
Path(PathStmt),
Embed(EmbedStmt),
Similar(SimilarStmt),
Spatial(SpatialStmt),
Find(FindStmt),
Entity(EntityStmt),
Vault(VaultStmt),
Cache(CacheStmt),
Blob(BlobStmt),
Blobs(BlobsStmt),
Checkpoint(CheckpointStmt),
Rollback(RollbackStmt),
Checkpoints(CheckpointsStmt),
Chain(ChainStmt),
Cluster(ClusterStmt),
GraphAlgorithm(GraphAlgorithmStmt),
GraphConstraint(GraphConstraintStmt),
GraphIndex(GraphIndexStmt),
GraphAggregate(GraphAggregateStmt),
GraphPattern(GraphPatternStmt),
GraphBatch(GraphBatchStmt),
CypherMatch(crate::cypher::CypherMatchStmt),
CypherCreate(crate::cypher::CypherCreateStmt),
CypherDelete(crate::cypher::CypherDeleteStmt),
CypherMerge(crate::cypher::CypherMergeStmt),
Empty,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SelectStmt {
pub distinct: bool,
pub columns: Vec<SelectItem>,
pub from: Option<FromClause>,
pub where_clause: Option<Box<Expr>>,
pub group_by: Vec<Expr>,
pub having: Option<Box<Expr>>,
pub order_by: Vec<OrderByItem>,
pub limit: Option<Box<Expr>>,
pub offset: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SelectItem {
pub expr: Expr,
pub alias: Option<Ident>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FromClause {
pub table: TableRef,
pub joins: Vec<Join>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TableRef {
pub kind: TableRefKind,
pub alias: Option<Ident>,
pub span: Span,
}
#[derive(Clone, Debug, PartialEq)]
pub enum TableRefKind {
Table(Ident),
Subquery(Box<SelectStmt>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Join {
pub kind: JoinKind,
pub table: TableRef,
pub condition: Option<JoinCondition>,
pub span: Span,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum JoinKind {
Inner,
Left,
Right,
Full,
Cross,
Natural,
}
#[derive(Clone, Debug, PartialEq)]
pub enum JoinCondition {
On(Box<Expr>),
Using(Vec<Ident>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OrderByItem {
pub expr: Expr,
pub direction: SortDirection,
pub nulls: Option<NullsOrder>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum SortDirection {
#[default]
Asc,
Desc,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NullsOrder {
First,
Last,
}
#[derive(Clone, Debug, PartialEq)]
pub struct InsertStmt {
pub table: Ident,
pub columns: Option<Vec<Ident>>,
pub source: InsertSource,
}
#[derive(Clone, Debug, PartialEq)]
pub enum InsertSource {
Values(Vec<Vec<Expr>>),
Query(Box<SelectStmt>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct UpdateStmt {
pub table: Ident,
pub assignments: Vec<Assignment>,
pub where_clause: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Assignment {
pub column: Ident,
pub value: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct DeleteStmt {
pub table: Ident,
pub where_clause: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CreateTableStmt {
pub if_not_exists: bool,
pub table: Ident,
pub columns: Vec<ColumnDef>,
pub constraints: Vec<TableConstraint>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ColumnDef {
pub name: Ident,
pub data_type: DataType,
pub constraints: Vec<ColumnConstraint>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DataType {
Int,
Integer,
Bigint,
Smallint,
Float,
Double,
Real,
Decimal(Option<u32>, Option<u32>),
Numeric(Option<u32>, Option<u32>),
Varchar(Option<u32>),
Char(Option<u32>),
Text,
Boolean,
Date,
Time,
Timestamp,
Blob,
Custom(String),
}
#[derive(Clone, Debug, PartialEq)]
pub enum ColumnConstraint {
NotNull,
Null,
Unique,
PrimaryKey,
Default(Expr),
Check(Expr),
References(ForeignKeyRef),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ForeignKeyRef {
pub table: Ident,
pub column: Option<Ident>,
pub on_delete: Option<ReferentialAction>,
pub on_update: Option<ReferentialAction>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReferentialAction {
Cascade,
Restrict,
SetNull,
SetDefault,
NoAction,
}
#[derive(Clone, Debug, PartialEq)]
pub enum TableConstraint {
PrimaryKey(Vec<Ident>),
Unique(Vec<Ident>),
ForeignKey {
columns: Vec<Ident>,
reference: ForeignKeyRef,
},
Check(Expr),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DropTableStmt {
pub if_exists: bool,
pub table: Ident,
pub cascade: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CreateIndexStmt {
pub unique: bool,
pub if_not_exists: bool,
pub name: Ident,
pub table: Ident,
pub columns: Vec<Ident>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DropIndexStmt {
pub if_exists: bool,
pub name: Option<Ident>,
pub table: Option<Ident>,
pub column: Option<Ident>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DescribeStmt {
pub target: DescribeTarget,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DescribeTarget {
Table(Ident),
Node(Ident),
Edge(Ident),
}
#[derive(Clone, Debug, PartialEq)]
pub struct NodeStmt {
pub operation: NodeOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum NodeOp {
Create {
label: Ident,
properties: Vec<Property>,
},
Get {
id: Expr,
},
Delete {
id: Expr,
},
List {
label: Option<Ident>,
limit: Option<Box<Expr>>,
offset: Option<Box<Expr>>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct EdgeStmt {
pub operation: EdgeOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum EdgeOp {
Create {
from_id: Expr,
to_id: Expr,
edge_type: Ident,
properties: Vec<Property>,
},
Get {
id: Expr,
},
Delete {
id: Expr,
},
List {
edge_type: Option<Ident>,
limit: Option<Box<Expr>>,
offset: Option<Box<Expr>>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct Property {
pub key: Ident,
pub value: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct NeighborsStmt {
pub node_id: Expr,
pub direction: Direction,
pub edge_type: Option<Ident>,
pub by_similarity: Option<Vec<Expr>>,
pub limit: Option<Expr>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Direction {
#[default]
Outgoing,
Incoming,
Both,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PathStmt {
pub algorithm: PathAlgorithm,
pub from_id: Expr,
pub to_id: Expr,
pub max_depth: Option<Expr>,
pub min_depth: Option<Expr>,
pub weight_property: Option<Ident>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum PathAlgorithm {
#[default]
Shortest,
All,
Weighted,
AllWeighted,
Variable,
}
#[derive(Clone, Debug, PartialEq)]
pub struct EmbedStmt {
pub operation: EmbedOp,
pub collection: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum EmbedOp {
Store {
key: Expr,
vector: Vec<Expr>,
},
Get {
key: Expr,
},
Delete {
key: Expr,
},
BuildIndex,
Batch {
items: Vec<(Expr, Vec<Expr>)>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct SimilarStmt {
pub query: SimilarQuery,
pub limit: Option<Expr>,
pub metric: Option<DistanceMetric>,
pub connected_to: Option<Expr>,
pub collection: Option<String>,
pub where_clause: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SimilarQuery {
Key(Expr),
Vector(Vec<Expr>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum DistanceMetric {
#[default]
Cosine,
Euclidean,
DotProduct,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SpatialStmt {
pub op: SpatialOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SpatialOp {
Insert {
key: Expr,
x: Expr,
y: Expr,
width: Expr,
height: Expr,
},
WithinRadius {
x: Expr,
y: Expr,
radius: Expr,
limit: Option<Expr>,
},
Delete {
key: Expr,
x: Expr,
y: Expr,
width: Expr,
height: Expr,
},
Nearest {
x: Expr,
y: Expr,
limit: Option<Expr>,
},
Count,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FindStmt {
pub pattern: FindPattern,
pub where_clause: Option<Box<Expr>>,
pub similar_to: Option<Expr>,
pub connected_to: Option<Expr>,
pub return_items: Vec<SelectItem>,
pub limit: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FindPattern {
Nodes {
label: Option<Ident>,
},
Edges {
edge_type: Option<Ident>,
},
Rows {
table: Ident,
},
Path {
from: Option<Ident>,
edge: Option<Ident>,
to: Option<Ident>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct EntityStmt {
pub operation: EntityOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum EntityOp {
Create {
key: Expr,
properties: Vec<Property>,
embedding: Option<Vec<Expr>>,
},
Get {
key: Expr,
},
Update {
key: Expr,
properties: Vec<Property>,
embedding: Option<Vec<Expr>>,
},
Delete {
key: Expr,
},
Connect {
from_key: Expr,
to_key: Expr,
edge_type: Ident,
},
Batch {
entities: Vec<BatchEntityDef>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct BatchEntityDef {
pub key: Expr,
pub properties: Vec<Property>,
pub embedding: Option<Vec<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct VaultStmt {
pub operation: VaultOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum VaultOp {
Set {
key: Expr,
value: Expr,
},
Get {
key: Expr,
},
Delete {
key: Expr,
},
List {
pattern: Option<Expr>,
},
Rotate {
key: Expr,
new_value: Expr,
},
Grant {
entity: Expr,
key: Expr,
},
Revoke {
entity: Expr,
key: Expr,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct CacheStmt {
pub operation: CacheOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CacheOp {
Init,
Stats,
Clear,
Evict {
count: Option<Expr>,
},
Get {
key: Expr,
},
Put {
key: Expr,
value: Expr,
},
SemanticGet {
query: Expr,
threshold: Option<Expr>,
},
SemanticPut {
query: Expr,
response: Expr,
embedding: Vec<Expr>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct ClusterStmt {
pub operation: ClusterOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ClusterOp {
Connect {
addresses: Expr,
},
Disconnect,
Status,
Nodes,
Leader,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BlobStmt {
pub operation: BlobOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum BlobOp {
Init,
Put {
filename: Expr,
data: Option<Expr>,
from_path: Option<Expr>,
options: BlobOptions,
},
Get {
artifact_id: Expr,
to_path: Option<Expr>,
},
Delete {
artifact_id: Expr,
},
Info {
artifact_id: Expr,
},
Link {
artifact_id: Expr,
entity: Expr,
},
Unlink {
artifact_id: Expr,
entity: Expr,
},
Links {
artifact_id: Expr,
},
Tag {
artifact_id: Expr,
tag: Expr,
},
Untag {
artifact_id: Expr,
tag: Expr,
},
Verify {
artifact_id: Expr,
},
Gc {
full: bool,
},
Repair,
Stats,
MetaSet {
artifact_id: Expr,
key: Expr,
value: Expr,
},
MetaGet {
artifact_id: Expr,
key: Expr,
},
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct BlobOptions {
pub content_type: Option<Expr>,
pub created_by: Option<Expr>,
pub link: Vec<Expr>,
pub tag: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BlobsStmt {
pub operation: BlobsOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum BlobsOp {
List {
pattern: Option<Expr>,
},
For {
entity: Expr,
},
ByTag {
tag: Expr,
},
ByType {
content_type: Expr,
},
Similar {
artifact_id: Expr,
limit: Option<Expr>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct CheckpointStmt {
pub name: Option<Expr>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RollbackStmt {
pub target: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CheckpointsStmt {
pub limit: Option<Expr>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ChainStmt {
pub operation: ChainOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ChainOp {
Begin,
Commit,
Rollback {
height: Expr,
},
History {
key: Expr,
},
Similar {
embedding: Vec<Expr>,
limit: Option<Expr>,
},
Drift {
from_height: Expr,
to_height: Expr,
},
ShowCodebookGlobal,
ShowCodebookLocal {
domain: Expr,
},
AnalyzeTransitions,
Height,
Tip,
Block {
height: Expr,
},
Verify,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphAlgorithmStmt {
pub operation: GraphAlgorithmOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphAlgorithmOp {
PageRank {
damping: Option<Expr>,
tolerance: Option<Expr>,
max_iterations: Option<Expr>,
direction: Option<Direction>,
edge_type: Option<Ident>,
},
BetweennessCentrality {
sampling_ratio: Option<Expr>,
direction: Option<Direction>,
edge_type: Option<Ident>,
},
ClosenessCentrality {
direction: Option<Direction>,
edge_type: Option<Ident>,
},
EigenvectorCentrality {
max_iterations: Option<Expr>,
tolerance: Option<Expr>,
direction: Option<Direction>,
edge_type: Option<Ident>,
},
LouvainCommunities {
resolution: Option<Expr>,
max_passes: Option<Expr>,
direction: Option<Direction>,
edge_type: Option<Ident>,
},
LabelPropagation {
max_iterations: Option<Expr>,
direction: Option<Direction>,
edge_type: Option<Ident>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GraphConstraintStmt {
pub operation: GraphConstraintOp,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GraphConstraintOp {
Create {
name: Ident,
target: ConstraintTarget,
property: Ident,
constraint_type: ConstraintType,
},
Drop {
name: Ident,
},
List,
Get {
name: Ident,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ConstraintTarget {
Node {
label: Option<Ident>,
},
Edge {
edge_type: Option<Ident>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ConstraintType {
Unique,
Exists,
Type(String),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GraphIndexStmt {
pub operation: GraphIndexOp,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GraphIndexOp {
CreateNodeProperty {
property: Ident,
},
CreateEdgeProperty {
property: Ident,
},
CreateLabel,
CreateEdgeType,
DropNode {
property: Ident,
},
DropEdge {
property: Ident,
},
ShowNodeIndexes,
ShowEdgeIndexes,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphAggregateStmt {
pub operation: GraphAggregateOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphAggregateOp {
CountNodes {
label: Option<Ident>,
},
CountEdges {
edge_type: Option<Ident>,
},
AggregateNodeProperty {
function: AggregateFunction,
property: Ident,
label: Option<Ident>,
filter: Option<Box<Expr>>,
},
AggregateEdgeProperty {
function: AggregateFunction,
property: Ident,
edge_type: Option<Ident>,
filter: Option<Box<Expr>>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AggregateFunction {
Sum,
Avg,
Min,
Max,
Count,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphPatternStmt {
pub operation: GraphPatternOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphPatternOp {
Match {
pattern: PatternSpec,
limit: Option<Expr>,
},
Count {
pattern: PatternSpec,
},
Exists {
pattern: PatternSpec,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct PatternSpec {
pub nodes: Vec<NodePatternSpec>,
pub edges: Vec<EdgePatternSpec>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct NodePatternSpec {
pub alias: Option<Ident>,
pub label: Option<Ident>,
pub properties: Vec<Property>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct EdgePatternSpec {
pub alias: Option<Ident>,
pub edge_type: Option<Ident>,
pub direction: Direction,
pub from_node: usize,
pub to_node: usize,
pub properties: Vec<Property>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphBatchStmt {
pub operation: GraphBatchOp,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphBatchOp {
CreateNodes {
nodes: Vec<BatchNodeDef>,
},
CreateEdges {
edges: Vec<BatchEdgeDef>,
},
DeleteNodes {
ids: Vec<Expr>,
},
DeleteEdges {
ids: Vec<Expr>,
},
UpdateNodes {
updates: Vec<BatchNodeUpdate>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct BatchNodeDef {
pub labels: Vec<Ident>,
pub properties: Vec<Property>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BatchEdgeDef {
pub from_id: Expr,
pub to_id: Expr,
pub edge_type: Ident,
pub properties: Vec<Property>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BatchNodeUpdate {
pub id: Expr,
pub properties: Vec<Property>,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct PaginationOpts {
pub skip: Option<Expr>,
pub limit: Option<Expr>,
pub count_total: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Expr {
pub kind: ExprKind,
pub span: Span,
}
impl Expr {
#[must_use]
pub const fn new(kind: ExprKind, span: Span) -> Self {
Self { kind, span }
}
#[must_use]
pub fn boxed(kind: ExprKind, span: Span) -> Box<Self> {
Box::new(Self::new(kind, span))
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ExprKind {
Literal(Literal),
Ident(Ident),
Qualified(Box<Expr>, Ident),
Binary(Box<Expr>, BinaryOp, Box<Expr>),
Unary(UnaryOp, Box<Expr>),
Call(FunctionCall),
Case(CaseExpr),
Subquery(Box<SelectStmt>),
Exists(Box<SelectStmt>),
In {
expr: Box<Expr>,
list: InList,
negated: bool,
},
Between {
expr: Box<Expr>,
low: Box<Expr>,
high: Box<Expr>,
negated: bool,
},
Like {
expr: Box<Expr>,
pattern: Box<Expr>,
negated: bool,
},
IsNull {
expr: Box<Expr>,
negated: bool,
},
Array(Vec<Expr>),
Tuple(Vec<Expr>),
Cast(Box<Expr>, DataType),
Wildcard,
QualifiedWildcard(Ident),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
Null,
Boolean(bool),
Integer(i64),
Float(f64),
String(String),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ident {
pub name: String,
pub span: Span,
}
impl Ident {
pub fn new(name: impl Into<String>, span: Span) -> Self {
Self {
name: name.into(),
span,
}
}
pub fn unspanned(name: impl Into<String>) -> Self {
Self {
name: name.into(),
span: Span::dummy(),
}
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Concat,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
}
impl BinaryOp {
#[must_use]
pub const fn precedence(self) -> u8 {
match self {
Self::Or => 1,
Self::And => 2,
Self::Eq | Self::Ne | Self::Lt | Self::Le | Self::Gt | Self::Ge => 3,
Self::BitOr => 4,
Self::BitXor => 5,
Self::BitAnd => 6,
Self::Shl | Self::Shr => 7,
Self::Add | Self::Sub | Self::Concat => 8,
Self::Mul | Self::Div | Self::Mod => 9,
}
}
#[must_use]
pub const fn is_left_assoc(self) -> bool {
true
}
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Add => "+",
Self::Sub => "-",
Self::Mul => "*",
Self::Div => "/",
Self::Mod => "%",
Self::Eq => "=",
Self::Ne => "!=",
Self::Lt => "<",
Self::Le => "<=",
Self::Gt => ">",
Self::Ge => ">=",
Self::And => "AND",
Self::Or => "OR",
Self::Concat => "||",
Self::BitAnd => "&",
Self::BitOr => "|",
Self::BitXor => "^",
Self::Shl => "<<",
Self::Shr => ">>",
};
write!(f, "{s}")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
Not,
Neg,
BitNot,
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Not => "NOT",
Self::Neg => "-",
Self::BitNot => "~",
};
write!(f, "{s}")
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionCall {
pub name: Ident,
pub args: Vec<Expr>,
pub distinct: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CaseExpr {
pub operand: Option<Box<Expr>>,
pub when_clauses: Vec<WhenClause>,
pub else_clause: Option<Box<Expr>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct WhenClause {
pub condition: Expr,
pub result: Expr,
}
#[derive(Clone, Debug, PartialEq)]
pub enum InList {
Values(Vec<Expr>),
Subquery(Box<SelectStmt>),
}
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int => write!(f, "INT"),
Self::Integer => write!(f, "INTEGER"),
Self::Bigint => write!(f, "BIGINT"),
Self::Smallint => write!(f, "SMALLINT"),
Self::Float => write!(f, "FLOAT"),
Self::Double => write!(f, "DOUBLE"),
Self::Real => write!(f, "REAL"),
Self::Decimal(p, s) => match (p, s) {
(Some(p), Some(s)) => write!(f, "DECIMAL({p}, {s})"),
(Some(p), None) => write!(f, "DECIMAL({p})"),
_ => write!(f, "DECIMAL"),
},
Self::Numeric(p, s) => match (p, s) {
(Some(p), Some(s)) => write!(f, "NUMERIC({p}, {s})"),
(Some(p), None) => write!(f, "NUMERIC({p})"),
_ => write!(f, "NUMERIC"),
},
Self::Varchar(n) => match n {
Some(n) => write!(f, "VARCHAR({n})"),
None => write!(f, "VARCHAR"),
},
Self::Char(n) => match n {
Some(n) => write!(f, "CHAR({n})"),
None => write!(f, "CHAR"),
},
Self::Text => write!(f, "TEXT"),
Self::Boolean => write!(f, "BOOLEAN"),
Self::Date => write!(f, "DATE"),
Self::Time => write!(f, "TIME"),
Self::Timestamp => write!(f, "TIMESTAMP"),
Self::Blob => write!(f, "BLOB"),
Self::Custom(name) => write!(f, "{name}"),
}
}
}
impl fmt::Display for JoinKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Inner => "INNER JOIN",
Self::Left => "LEFT JOIN",
Self::Right => "RIGHT JOIN",
Self::Full => "FULL JOIN",
Self::Cross => "CROSS JOIN",
Self::Natural => "NATURAL JOIN",
};
write!(f, "{s}")
}
}
impl fmt::Display for SortDirection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Asc => write!(f, "ASC"),
Self::Desc => write!(f, "DESC"),
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Outgoing => write!(f, "OUTGOING"),
Self::Incoming => write!(f, "INCOMING"),
Self::Both => write!(f, "BOTH"),
}
}
}
impl fmt::Display for DistanceMetric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Cosine => write!(f, "COSINE"),
Self::Euclidean => write!(f, "EUCLIDEAN"),
Self::DotProduct => write!(f, "DOT_PRODUCT"),
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => write!(f, "NULL"),
Self::Boolean(b) => write!(f, "{}", if *b { "TRUE" } else { "FALSE" }),
Self::Integer(n) => write!(f, "{n}"),
Self::Float(n) => write!(f, "{n}"),
Self::String(s) => {
let escaped = s.replace('\'', "''");
write!(f, "'{escaped}'")
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ident() {
let ident = Ident::new("users", Span::from_offsets(0, 5));
assert_eq!(ident.name, "users");
assert_eq!(format!("{}", ident), "users");
let unspanned = Ident::unspanned("column");
assert!(unspanned.span.is_dummy());
}
#[test]
fn test_literal_display() {
assert_eq!(format!("{}", Literal::Null), "NULL");
assert_eq!(format!("{}", Literal::Boolean(true)), "TRUE");
assert_eq!(format!("{}", Literal::Boolean(false)), "FALSE");
assert_eq!(format!("{}", Literal::Integer(42)), "42");
assert_eq!(format!("{}", Literal::Float(3.15)), "3.15");
assert_eq!(
format!("{}", Literal::String("hello".to_string())),
"'hello'"
);
assert_eq!(
format!("{}", Literal::String("it's".to_string())),
"'it''s'"
);
}
#[test]
fn test_binary_op_precedence() {
assert!(BinaryOp::Mul.precedence() > BinaryOp::Add.precedence());
assert!(BinaryOp::Add.precedence() > BinaryOp::Eq.precedence());
assert!(BinaryOp::Eq.precedence() > BinaryOp::And.precedence());
assert!(BinaryOp::And.precedence() > BinaryOp::Or.precedence());
}
#[test]
fn test_binary_op_display() {
assert_eq!(format!("{}", BinaryOp::Add), "+");
assert_eq!(format!("{}", BinaryOp::Sub), "-");
assert_eq!(format!("{}", BinaryOp::Mul), "*");
assert_eq!(format!("{}", BinaryOp::Eq), "=");
assert_eq!(format!("{}", BinaryOp::And), "AND");
assert_eq!(format!("{}", BinaryOp::Or), "OR");
}
#[test]
fn test_unary_op_display() {
assert_eq!(format!("{}", UnaryOp::Not), "NOT");
assert_eq!(format!("{}", UnaryOp::Neg), "-");
assert_eq!(format!("{}", UnaryOp::BitNot), "~");
}
#[test]
fn test_data_type_display() {
assert_eq!(format!("{}", DataType::Int), "INT");
assert_eq!(format!("{}", DataType::Varchar(Some(255))), "VARCHAR(255)");
assert_eq!(format!("{}", DataType::Varchar(None)), "VARCHAR");
assert_eq!(
format!("{}", DataType::Decimal(Some(10), Some(2))),
"DECIMAL(10, 2)"
);
assert_eq!(
format!("{}", DataType::Decimal(Some(10), None)),
"DECIMAL(10)"
);
assert_eq!(format!("{}", DataType::Custom("UUID".to_string())), "UUID");
}
#[test]
fn test_join_kind_display() {
assert_eq!(format!("{}", JoinKind::Inner), "INNER JOIN");
assert_eq!(format!("{}", JoinKind::Left), "LEFT JOIN");
assert_eq!(format!("{}", JoinKind::Right), "RIGHT JOIN");
assert_eq!(format!("{}", JoinKind::Full), "FULL JOIN");
assert_eq!(format!("{}", JoinKind::Cross), "CROSS JOIN");
}
#[test]
fn test_direction_display() {
assert_eq!(format!("{}", Direction::Outgoing), "OUTGOING");
assert_eq!(format!("{}", Direction::Incoming), "INCOMING");
assert_eq!(format!("{}", Direction::Both), "BOTH");
}
#[test]
fn test_distance_metric_display() {
assert_eq!(format!("{}", DistanceMetric::Cosine), "COSINE");
assert_eq!(format!("{}", DistanceMetric::Euclidean), "EUCLIDEAN");
assert_eq!(format!("{}", DistanceMetric::DotProduct), "DOT_PRODUCT");
}
#[test]
fn test_sort_direction_default() {
assert_eq!(SortDirection::default(), SortDirection::Asc);
}
#[test]
fn test_direction_default() {
assert_eq!(Direction::default(), Direction::Outgoing);
}
#[test]
fn test_distance_metric_default() {
assert_eq!(DistanceMetric::default(), DistanceMetric::Cosine);
}
#[test]
fn test_path_algorithm_default() {
assert_eq!(PathAlgorithm::default(), PathAlgorithm::Shortest);
}
#[test]
fn test_expr_boxed() {
let expr = Expr::boxed(
ExprKind::Literal(Literal::Integer(42)),
Span::from_offsets(0, 2),
);
assert!(matches!(expr.kind, ExprKind::Literal(Literal::Integer(42))));
}
#[test]
fn test_statement_new() {
let stmt = Statement::new(StatementKind::Empty, Span::from_offsets(0, 1));
assert!(matches!(stmt.kind, StatementKind::Empty));
}
#[test]
fn test_binary_op_left_assoc() {
assert!(BinaryOp::Add.is_left_assoc());
assert!(BinaryOp::Mul.is_left_assoc());
assert!(BinaryOp::And.is_left_assoc());
}
#[test]
fn test_binary_op_display_comprehensive() {
assert_eq!(format!("{}", BinaryOp::Div), "/");
assert_eq!(format!("{}", BinaryOp::Mod), "%");
assert_eq!(format!("{}", BinaryOp::Ne), "!=");
assert_eq!(format!("{}", BinaryOp::Lt), "<");
assert_eq!(format!("{}", BinaryOp::Le), "<=");
assert_eq!(format!("{}", BinaryOp::Gt), ">");
assert_eq!(format!("{}", BinaryOp::Ge), ">=");
assert_eq!(format!("{}", BinaryOp::Concat), "||");
assert_eq!(format!("{}", BinaryOp::BitAnd), "&");
assert_eq!(format!("{}", BinaryOp::BitOr), "|");
assert_eq!(format!("{}", BinaryOp::BitXor), "^");
assert_eq!(format!("{}", BinaryOp::Shl), "<<");
assert_eq!(format!("{}", BinaryOp::Shr), ">>");
}
#[test]
fn test_data_type_display_comprehensive() {
assert_eq!(format!("{}", DataType::Integer), "INTEGER");
assert_eq!(format!("{}", DataType::Bigint), "BIGINT");
assert_eq!(format!("{}", DataType::Smallint), "SMALLINT");
assert_eq!(format!("{}", DataType::Float), "FLOAT");
assert_eq!(format!("{}", DataType::Double), "DOUBLE");
assert_eq!(format!("{}", DataType::Real), "REAL");
assert_eq!(format!("{}", DataType::Text), "TEXT");
assert_eq!(format!("{}", DataType::Boolean), "BOOLEAN");
assert_eq!(format!("{}", DataType::Date), "DATE");
assert_eq!(format!("{}", DataType::Time), "TIME");
assert_eq!(format!("{}", DataType::Timestamp), "TIMESTAMP");
assert_eq!(format!("{}", DataType::Blob), "BLOB");
assert_eq!(format!("{}", DataType::Char(Some(10))), "CHAR(10)");
assert_eq!(format!("{}", DataType::Char(None)), "CHAR");
assert_eq!(format!("{}", DataType::Decimal(None, None)), "DECIMAL");
assert_eq!(
format!("{}", DataType::Numeric(Some(5), Some(2))),
"NUMERIC(5, 2)"
);
assert_eq!(
format!("{}", DataType::Numeric(Some(5), None)),
"NUMERIC(5)"
);
assert_eq!(format!("{}", DataType::Numeric(None, None)), "NUMERIC");
}
#[test]
fn test_binary_op_precedence_comprehensive() {
assert!(BinaryOp::Or.precedence() < BinaryOp::And.precedence());
assert!(BinaryOp::And.precedence() < BinaryOp::Eq.precedence());
assert_eq!(BinaryOp::Eq.precedence(), BinaryOp::Lt.precedence());
assert!(BinaryOp::Lt.precedence() < BinaryOp::Add.precedence());
assert!(BinaryOp::Add.precedence() < BinaryOp::Mul.precedence());
assert!(BinaryOp::Concat.precedence() > 0);
}
#[test]
fn test_nulls_order_variants() {
let first = NullsOrder::First;
let last = NullsOrder::Last;
assert_ne!(first, last);
}
#[test]
fn test_column_constraint_variants() {
let pk = ColumnConstraint::PrimaryKey;
let nn = ColumnConstraint::NotNull;
let u = ColumnConstraint::Unique;
assert_ne!(pk, nn);
assert_ne!(nn, u);
}
#[test]
fn test_join_kind_display_all() {
assert!(format!("{}", JoinKind::Cross).contains("CROSS"));
assert!(format!("{}", JoinKind::Natural).contains("NATURAL"));
assert!(format!("{}", JoinKind::Full).contains("FULL"));
}
#[test]
fn test_literal_display_all() {
assert_eq!(format!("{}", Literal::Float(3.15)), "3.15");
assert_eq!(format!("{}", Literal::Boolean(false)), "FALSE");
}
#[test]
fn test_sort_direction_display() {
assert_eq!(format!("{}", SortDirection::Asc), "ASC");
assert_eq!(format!("{}", SortDirection::Desc), "DESC");
}
#[test]
fn test_binary_op_precedence_bitwise() {
assert_eq!(BinaryOp::BitOr.precedence(), 4);
assert_eq!(BinaryOp::BitXor.precedence(), 5);
assert_eq!(BinaryOp::BitAnd.precedence(), 6);
assert_eq!(BinaryOp::Shl.precedence(), 7);
assert_eq!(BinaryOp::Shr.precedence(), 7);
assert!(BinaryOp::Eq.precedence() < BinaryOp::BitOr.precedence());
assert!(BinaryOp::BitAnd.precedence() < BinaryOp::Shl.precedence());
assert!(BinaryOp::Shr.precedence() < BinaryOp::Add.precedence());
}
}