mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 09:57:47 +08:00
refactor(data): remove MySQL and SQLite support
Use PostgreSQL as the only database backend across runtime, schema tooling, installation, Compose, and CI. Update regression tests and reject removed drivers explicitly.
This commit is contained in:
@@ -1,22 +1,16 @@
|
||||
use crate::{Column, DefaultValue, DriverColumnOverride, LogicalType, ReferentialAction};
|
||||
|
||||
pub mod mysql;
|
||||
pub mod postgres;
|
||||
pub mod sqlite;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Dialect {
|
||||
Postgres,
|
||||
Mysql,
|
||||
Sqlite,
|
||||
}
|
||||
|
||||
impl Dialect {
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Postgres => "postgres",
|
||||
Self::Mysql => "mysql",
|
||||
Self::Sqlite => "sqlite",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,25 +23,11 @@ fn default_sql(default: &DefaultValue) -> String {
|
||||
match default {
|
||||
DefaultValue::String(value) => quote_string(value),
|
||||
DefaultValue::Integer(value) => value.to_string(),
|
||||
DefaultValue::Bool(value) => {
|
||||
if *value {
|
||||
"1".to_string()
|
||||
} else {
|
||||
"0".to_string()
|
||||
}
|
||||
}
|
||||
DefaultValue::Bool(value) => value.to_string(),
|
||||
DefaultValue::Raw { raw } => raw.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_sql_bool_keywords(default: &DefaultValue) -> String {
|
||||
match default {
|
||||
DefaultValue::Bool(true) => "true".to_string(),
|
||||
DefaultValue::Bool(false) => "false".to_string(),
|
||||
_ => default_sql(default),
|
||||
}
|
||||
}
|
||||
|
||||
fn column_default<'a>(
|
||||
column: &'a Column,
|
||||
override_: Option<&'a DriverColumnOverride>,
|
||||
@@ -94,50 +74,6 @@ fn postgres_type(column: &Column) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn mysql_type(column: &Column) -> String {
|
||||
let override_ = column.driver.mysql.as_ref();
|
||||
if let Some(sql_type) = override_type(override_) {
|
||||
return sql_type.to_string();
|
||||
}
|
||||
match column.logical_type {
|
||||
LogicalType::TextId | LogicalType::Text => match column.length {
|
||||
Some(length) => format!("VARCHAR({length})"),
|
||||
None => "TEXT".to_string(),
|
||||
},
|
||||
LogicalType::LongText => "LONGTEXT".to_string(),
|
||||
LogicalType::Bool => "TINYINT(1)".to_string(),
|
||||
LogicalType::Int32 => "INT".to_string(),
|
||||
LogicalType::Int64 | LogicalType::UnixSeconds | LogicalType::UnixMillis => {
|
||||
"BIGINT".to_string()
|
||||
}
|
||||
LogicalType::Float64 => "DOUBLE".to_string(),
|
||||
LogicalType::DecimalMoney => "DECIMAL(20,8)".to_string(),
|
||||
LogicalType::Timestamp => "BIGINT".to_string(),
|
||||
LogicalType::Json => "JSON".to_string(),
|
||||
LogicalType::Bytes => "LONGBLOB".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlite_type(column: &Column) -> String {
|
||||
let override_ = column.driver.sqlite.as_ref();
|
||||
if let Some(sql_type) = override_type(override_) {
|
||||
return sql_type.to_string();
|
||||
}
|
||||
match column.logical_type {
|
||||
LogicalType::TextId | LogicalType::Text | LogicalType::LongText | LogicalType::Json => {
|
||||
"TEXT".to_string()
|
||||
}
|
||||
LogicalType::Bool
|
||||
| LogicalType::Int32
|
||||
| LogicalType::Int64
|
||||
| LogicalType::UnixSeconds
|
||||
| LogicalType::UnixMillis
|
||||
| LogicalType::Timestamp => "INTEGER".to_string(),
|
||||
LogicalType::Float64 | LogicalType::DecimalMoney => "REAL".to_string(),
|
||||
LogicalType::Bytes => "BLOB".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn referential_action_sql(action: &ReferentialAction) -> &'static str {
|
||||
match action {
|
||||
ReferentialAction::Cascade => "CASCADE",
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
use crate::dialect::{
|
||||
column_default, column_nullable, default_sql, mysql_type, referential_action_sql,
|
||||
};
|
||||
use crate::LogicalSchema;
|
||||
|
||||
pub fn emit_schema(schema: &LogicalSchema) -> String {
|
||||
emit_named_schema(schema, &schema.ordered_table_names())
|
||||
}
|
||||
|
||||
pub fn emit_named_schema(schema: &LogicalSchema, table_names: &[String]) -> String {
|
||||
let mut out = String::new();
|
||||
for table_name in table_names {
|
||||
let table = schema
|
||||
.tables
|
||||
.get(table_name)
|
||||
.expect("named schema table should exist");
|
||||
let mut definitions = Vec::new();
|
||||
for column in &table.columns {
|
||||
let mut definition = format!(" `{}` {}", column.name, mysql_type(column));
|
||||
if !column_nullable(column, column.driver.mysql.as_ref()) {
|
||||
definition.push_str(" NOT NULL");
|
||||
}
|
||||
if column.auto_increment {
|
||||
definition.push_str(" AUTO_INCREMENT");
|
||||
}
|
||||
if let Some(default) = column_default(column, column.driver.mysql.as_ref()) {
|
||||
definition.push_str(" DEFAULT ");
|
||||
definition.push_str(&default_sql(default));
|
||||
}
|
||||
definitions.push(definition);
|
||||
}
|
||||
if !table.primary_key.is_empty() {
|
||||
definitions.push(format!(
|
||||
" PRIMARY KEY ({})",
|
||||
table
|
||||
.primary_key
|
||||
.iter()
|
||||
.map(|column| format!("`{column}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
for unique in &table.uniques {
|
||||
definitions.push(format!(
|
||||
" UNIQUE KEY {} ({})",
|
||||
unique.name,
|
||||
unique
|
||||
.columns
|
||||
.iter()
|
||||
.map(|column| format!("`{column}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
for index in &table.indexes {
|
||||
let unique = if index.unique { "UNIQUE " } else { "" };
|
||||
definitions.push(format!(
|
||||
" {unique}KEY {} ({})",
|
||||
index.name,
|
||||
index
|
||||
.columns
|
||||
.iter()
|
||||
.map(|column| format!("`{column}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
));
|
||||
}
|
||||
for foreign_key in &table.foreign_keys {
|
||||
let mut definition = format!(
|
||||
" CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({})",
|
||||
foreign_key.name,
|
||||
foreign_key
|
||||
.columns
|
||||
.iter()
|
||||
.map(|column| format!("`{column}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
foreign_key.references_table,
|
||||
foreign_key
|
||||
.references_columns
|
||||
.iter()
|
||||
.map(|column| format!("`{column}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
if let Some(action) = &foreign_key.on_delete {
|
||||
definition.push_str(" ON DELETE ");
|
||||
definition.push_str(referential_action_sql(action));
|
||||
}
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
out.push_str(&format!(
|
||||
"CREATE TABLE IF NOT EXISTS {} (\n",
|
||||
quote_identifier_if_needed(table_name)
|
||||
));
|
||||
out.push_str(&definitions.join(",\n"));
|
||||
out.push_str("\n);\n\n");
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn quote_identifier_if_needed(identifier: &str) -> String {
|
||||
if needs_quoting(identifier) {
|
||||
quote_identifier(identifier)
|
||||
} else {
|
||||
identifier.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_quoting(identifier: &str) -> bool {
|
||||
matches!(identifier, "date" | "usage")
|
||||
}
|
||||
|
||||
fn quote_identifier(identifier: &str) -> String {
|
||||
format!("`{}`", identifier.replace('`', "``"))
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::dialect::{
|
||||
column_default, column_nullable, default_sql_bool_keywords, postgres_type,
|
||||
referential_action_sql,
|
||||
column_default, column_nullable, default_sql, postgres_type, referential_action_sql,
|
||||
};
|
||||
use crate::LogicalSchema;
|
||||
|
||||
@@ -30,7 +29,7 @@ pub fn emit_named_schema(schema: &LogicalSchema, table_names: &[String]) -> Stri
|
||||
out.push_str(&postgres_type(column));
|
||||
if let Some(default) = column_default(column, column.driver.postgres.as_ref()) {
|
||||
out.push_str(" DEFAULT ");
|
||||
out.push_str(&default_sql_bool_keywords(default));
|
||||
out.push_str(&default_sql(default));
|
||||
}
|
||||
if !column_nullable(column, column.driver.postgres.as_ref()) {
|
||||
out.push_str(" NOT NULL");
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
use crate::dialect::{
|
||||
column_default, column_nullable, default_sql, referential_action_sql, sqlite_type,
|
||||
};
|
||||
use crate::LogicalSchema;
|
||||
|
||||
pub fn emit_schema(schema: &LogicalSchema) -> String {
|
||||
emit_named_schema(schema, &schema.ordered_table_names())
|
||||
}
|
||||
|
||||
pub fn emit_named_schema(schema: &LogicalSchema, table_names: &[String]) -> String {
|
||||
let mut out = String::new();
|
||||
for table_name in table_names {
|
||||
let table = schema
|
||||
.tables
|
||||
.get(table_name)
|
||||
.expect("named schema table should exist");
|
||||
let mut definitions = Vec::new();
|
||||
for column in &table.columns {
|
||||
let mut definition = format!(" {} {}", column.name, sqlite_type(column));
|
||||
if table.primary_key.len() == 1 && table.primary_key[0] == column.name {
|
||||
definition.push_str(" PRIMARY KEY");
|
||||
if column.auto_increment {
|
||||
definition.push_str(" AUTOINCREMENT");
|
||||
}
|
||||
}
|
||||
if !column.auto_increment && !column_nullable(column, column.driver.sqlite.as_ref()) {
|
||||
definition.push_str(" NOT NULL");
|
||||
}
|
||||
if let Some(default) = column_default(column, column.driver.sqlite.as_ref()) {
|
||||
definition.push_str(" DEFAULT ");
|
||||
definition.push_str(&default_sql(default));
|
||||
}
|
||||
definitions.push(definition);
|
||||
}
|
||||
if table.primary_key.len() > 1 {
|
||||
definitions.push(format!(
|
||||
" PRIMARY KEY ({})",
|
||||
table.primary_key.join(", ")
|
||||
));
|
||||
}
|
||||
for unique in &table.uniques {
|
||||
definitions.push(format!(" UNIQUE ({})", unique.columns.join(", ")));
|
||||
}
|
||||
for foreign_key in &table.foreign_keys {
|
||||
let mut definition = format!(
|
||||
" CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({})",
|
||||
foreign_key.name,
|
||||
foreign_key.columns.join(", "),
|
||||
foreign_key.references_table,
|
||||
foreign_key.references_columns.join(", ")
|
||||
);
|
||||
if let Some(action) = &foreign_key.on_delete {
|
||||
definition.push_str(" ON DELETE ");
|
||||
definition.push_str(referential_action_sql(action));
|
||||
}
|
||||
definitions.push(definition);
|
||||
}
|
||||
|
||||
let quoted_table_name = quote_identifier_if_needed(table_name);
|
||||
out.push_str(&format!(
|
||||
"CREATE TABLE IF NOT EXISTS {quoted_table_name} (\n"
|
||||
));
|
||||
out.push_str(&definitions.join(",\n"));
|
||||
out.push_str("\n);\n");
|
||||
for index in &table.indexes {
|
||||
let unique = if index.unique { "UNIQUE " } else { "" };
|
||||
out.push_str(&format!(
|
||||
"CREATE {unique}INDEX IF NOT EXISTS {} ON {quoted_table_name} ({});\n",
|
||||
index.name,
|
||||
index.columns.join(", ")
|
||||
));
|
||||
}
|
||||
out.push('\n');
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn quote_identifier_if_needed(identifier: &str) -> String {
|
||||
if needs_quoting(identifier) {
|
||||
quote_identifier(identifier)
|
||||
} else {
|
||||
identifier.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_quoting(identifier: &str) -> bool {
|
||||
matches!(identifier, "date" | "usage")
|
||||
}
|
||||
|
||||
fn quote_identifier(identifier: &str) -> String {
|
||||
format!("\"{}\"", identifier.replace('"', "\"\""))
|
||||
}
|
||||
Reference in New Issue
Block a user