pub mod create;
pub mod ddl;
pub mod diff;
pub mod expected;
pub mod introspect;
pub mod migrate;
pub use create::{CreateTable, SchemaBuilder};
pub use ddl::{
DdlGenerator, MysqlDdlGenerator, PostgresDdlGenerator, SqliteDdlGenerator,
generator_for_dialect,
};
pub use expected::{
ModelSchema, ModelTuple, expected_schema, normalize_sql_type, table_schema_from_fields,
table_schema_from_model,
};
pub use introspect::{
CheckConstraintInfo, ColumnInfo, DatabaseSchema, Dialect, ForeignKeyInfo, IndexInfo,
Introspector, ParsedSqlType, TableInfo, UniqueConstraintInfo,
};
pub use migrate::{
DEFAULT_LOCK_TIMEOUT, Migration, MigrationFormat, MigrationRunner, MigrationStatus,
MigrationWriter, split_statements,
};
use asupersync::{Cx, Outcome};
use sqlmodel_core::{Connection, Model};
pub fn create_table<M: Model>() -> CreateTable<M> {
CreateTable::new()
}
pub async fn create_all<C: Connection>(
cx: &Cx,
conn: &C,
schemas: &[&str],
) -> Outcome<(), sqlmodel_core::Error> {
for sql in schemas {
match conn.execute(cx, sql, &[]).await {
Outcome::Ok(_) => continue,
Outcome::Err(e) => return Outcome::Err(e),
Outcome::Cancelled(r) => return Outcome::Cancelled(r),
Outcome::Panicked(p) => return Outcome::Panicked(p),
}
}
Outcome::Ok(())
}
pub async fn drop_table<C: Connection>(
cx: &Cx,
conn: &C,
table_name: &str,
if_exists: bool,
) -> Outcome<(), sqlmodel_core::Error> {
let sql = drop_table_sql(conn.dialect(), table_name, if_exists);
conn.execute(cx, &sql, &[]).await.map(|_| ())
}
pub fn drop_table_sql(
dialect: sqlmodel_core::Dialect,
table_name: &str,
if_exists: bool,
) -> String {
let table = dialect.quote_identifier(table_name);
if if_exists {
format!("DROP TABLE IF EXISTS {table}")
} else {
format!("DROP TABLE {table}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drop_table_sql_simple() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "users", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"users\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "heroes", false);
assert_eq!(sql, "DROP TABLE \"heroes\"");
}
#[test]
fn test_drop_table_sql_with_keyword_name() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "order", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"order\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "select", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"select\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "user", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"user\"");
}
#[test]
fn test_drop_table_sql_with_embedded_quotes() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "my\"table", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"my\"\"table\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "test\"\"name", false);
assert_eq!(sql, "DROP TABLE \"test\"\"\"\"name\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "\"", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"\"\"\"");
}
#[test]
fn test_drop_table_sql_with_spaces() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "my table", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"my table\"");
}
#[test]
fn test_drop_table_sql_with_unicode() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "用户", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"用户\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "tâble_émoji_🦀", false);
assert_eq!(sql, "DROP TABLE \"tâble_émoji_🦀\"");
}
#[test]
fn test_drop_table_sql_edge_cases() {
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "x", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"x\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "123table", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"123table\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "table-with-dashes", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"table-with-dashes\"");
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, "table.with.dots", true);
assert_eq!(sql, "DROP TABLE IF EXISTS \"table.with.dots\"");
}
#[test]
fn test_drop_table_sql_sql_injection_attempt_neutralized() {
let malicious = "users\"; DROP TABLE secrets; --";
let sql = drop_table_sql(sqlmodel_core::Dialect::Sqlite, malicious, true);
assert_eq!(
sql,
"DROP TABLE IF EXISTS \"users\"\"; DROP TABLE secrets; --\""
);
assert!(sql.starts_with("DROP TABLE IF EXISTS \""));
assert!(sql.ends_with('"'));
let quote_count = sql.matches('"').count();
assert_eq!(quote_count, 4);
}
}