use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SqlDialect {
#[default]
Standard,
PostgreSQL,
MySQL,
SQLite,
}
impl SqlDialect {
pub fn detect(sql: &str) -> Self {
let upper = sql.to_uppercase();
if upper.contains("INSERT IGNORE") || upper.contains("ON DUPLICATE KEY") {
return SqlDialect::MySQL;
}
if upper.contains("INSERT OR IGNORE")
|| upper.contains("INSERT OR REPLACE")
|| upper.contains("INSERT OR ABORT")
{
return SqlDialect::SQLite;
}
if upper.contains("ON CONFLICT") {
return SqlDialect::PostgreSQL;
}
SqlDialect::Standard
}
}
impl fmt::Display for SqlDialect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SqlDialect::Standard => write!(f, "Standard SQL"),
SqlDialect::PostgreSQL => write!(f, "PostgreSQL"),
SqlDialect::MySQL => write!(f, "MySQL"),
SqlDialect::SQLite => write!(f, "SQLite"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureSupport {
Full,
Partial,
Planned,
NotSupported,
}
impl fmt::Display for FeatureSupport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FeatureSupport::Full => write!(f, "✅ Full"),
FeatureSupport::Partial => write!(f, "🔄 Partial"),
FeatureSupport::Planned => write!(f, "📋 Planned"),
FeatureSupport::NotSupported => write!(f, "❌ Not Supported"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SqlFeature {
Select,
Insert,
Update,
Delete,
CreateTable,
DropTable,
AlterTable,
CreateIndex,
DropIndex,
CreateTableIfNotExists,
DropTableIfExists,
CreateIndexIfNotExists,
DropIndexIfExists,
OnConflictDoNothing,
OnConflictDoUpdate,
InsertIgnore,
InsertOrIgnore,
InsertOrReplace,
OnDuplicateKeyUpdate,
Begin,
Commit,
Rollback,
Savepoint,
Where,
OrderBy,
Limit,
Offset,
GroupBy,
Having,
Distinct,
InnerJoin,
LeftJoin,
RightJoin,
CrossJoin,
SubqueryInFrom,
SubqueryInWhere,
SubqueryInSelect,
Union,
Intersect,
Except,
ParameterizedQueries,
CaseWhen,
Cast,
NullHandling,
InList,
Between,
Like,
VectorSearch,
EmbeddingType,
ContextWindow,
}
pub fn get_feature_support(feature: SqlFeature) -> FeatureSupport {
use SqlFeature::*;
match feature {
Select | Insert | Update | Delete => FeatureSupport::Full,
CreateTable | DropTable | CreateIndex | DropIndex => FeatureSupport::Full,
CreateTableIfNotExists | DropTableIfExists => FeatureSupport::Full,
CreateIndexIfNotExists | DropIndexIfExists => FeatureSupport::Full,
Begin | Commit | Rollback => FeatureSupport::Full,
Where | OrderBy | Limit | Offset | Distinct => FeatureSupport::Full,
ParameterizedQueries | NullHandling | InList | Like => FeatureSupport::Full,
OnConflictDoNothing | InsertIgnore | InsertOrIgnore => FeatureSupport::Full,
VectorSearch | EmbeddingType => FeatureSupport::Full,
AlterTable => FeatureSupport::Partial, GroupBy | Having => FeatureSupport::Partial, InnerJoin => FeatureSupport::Partial, OnConflictDoUpdate | InsertOrReplace | OnDuplicateKeyUpdate => FeatureSupport::Partial,
CaseWhen | Cast | Between => FeatureSupport::Partial,
Union => FeatureSupport::Partial,
SubqueryInFrom => FeatureSupport::Partial,
Savepoint => FeatureSupport::Partial,
ContextWindow => FeatureSupport::Partial,
LeftJoin | RightJoin | CrossJoin => FeatureSupport::Planned,
SubqueryInWhere | SubqueryInSelect => FeatureSupport::Planned,
Intersect | Except => FeatureSupport::Planned,
}
}
pub struct CompatibilityMatrix;
impl CompatibilityMatrix {
pub fn is_supported(feature: SqlFeature) -> bool {
matches!(
get_feature_support(feature),
FeatureSupport::Full | FeatureSupport::Partial
)
}
pub fn fully_supported() -> Vec<SqlFeature> {
use SqlFeature::*;
vec![
Select,
Insert,
Update,
Delete,
CreateTable,
DropTable,
CreateIndex,
DropIndex,
CreateTableIfNotExists,
DropTableIfExists,
CreateIndexIfNotExists,
DropIndexIfExists,
Begin,
Commit,
Rollback,
Where,
OrderBy,
Limit,
Offset,
Distinct,
ParameterizedQueries,
NullHandling,
InList,
Like,
OnConflictDoNothing,
InsertIgnore,
InsertOrIgnore,
VectorSearch,
EmbeddingType,
]
}
pub fn print_matrix() -> String {
let mut output = String::new();
output.push_str("# SochDB SQL Compatibility Matrix\n\n");
output.push_str("## Core DML\n\n");
output.push_str("| Feature | Support |\n");
output.push_str("|---------|--------|\n");
for feature in &[
SqlFeature::Select,
SqlFeature::Insert,
SqlFeature::Update,
SqlFeature::Delete,
] {
output.push_str(&format!(
"| {:?} | {} |\n",
feature,
get_feature_support(*feature)
));
}
output.push_str("\n## DDL\n\n");
output.push_str("| Feature | Support |\n");
output.push_str("|---------|--------|\n");
for feature in &[
SqlFeature::CreateTable,
SqlFeature::DropTable,
SqlFeature::AlterTable,
SqlFeature::CreateIndex,
SqlFeature::DropIndex,
SqlFeature::CreateTableIfNotExists,
SqlFeature::DropTableIfExists,
] {
output.push_str(&format!(
"| {:?} | {} |\n",
feature,
get_feature_support(*feature)
));
}
output.push_str("\n## Conflict/Upsert\n\n");
output.push_str("| Feature | Support |\n");
output.push_str("|---------|--------|\n");
for feature in &[
SqlFeature::OnConflictDoNothing,
SqlFeature::OnConflictDoUpdate,
SqlFeature::InsertIgnore,
SqlFeature::InsertOrIgnore,
SqlFeature::InsertOrReplace,
SqlFeature::OnDuplicateKeyUpdate,
] {
output.push_str(&format!(
"| {:?} | {} |\n",
feature,
get_feature_support(*feature)
));
}
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dialect_detection() {
assert_eq!(
SqlDialect::detect("SELECT * FROM users"),
SqlDialect::Standard
);
assert_eq!(
SqlDialect::detect("INSERT IGNORE INTO users VALUES (1)"),
SqlDialect::MySQL
);
assert_eq!(
SqlDialect::detect("INSERT OR IGNORE INTO users VALUES (1)"),
SqlDialect::SQLite
);
assert_eq!(
SqlDialect::detect("INSERT INTO users VALUES (1) ON CONFLICT DO NOTHING"),
SqlDialect::PostgreSQL
);
}
#[test]
fn test_feature_support() {
assert_eq!(
get_feature_support(SqlFeature::Select),
FeatureSupport::Full
);
assert_eq!(
get_feature_support(SqlFeature::AlterTable),
FeatureSupport::Partial
);
assert_eq!(
get_feature_support(SqlFeature::LeftJoin),
FeatureSupport::Planned
);
}
#[test]
fn test_compatibility_matrix() {
assert!(CompatibilityMatrix::is_supported(SqlFeature::Select));
assert!(CompatibilityMatrix::is_supported(SqlFeature::AlterTable));
assert!(!CompatibilityMatrix::is_supported(SqlFeature::LeftJoin));
}
}