use crate::database::enums::database_drivers::DatabaseDrivers;
pub fn format_hash_value(engine: DatabaseDrivers, hex_value: &str, is_binary: bool) -> String {
if is_binary {
match engine {
DatabaseDrivers::sqlite3 => format!("X'{hex_value}'"),
DatabaseDrivers::mysql => format!("UNHEX('{hex_value}')"),
DatabaseDrivers::pgsql => format!("decode('{hex_value}', 'hex')"),
}
} else {
format!("'{hex_value}'")
}
}
pub fn format_hex_select(engine: DatabaseDrivers, column: &str, is_binary: bool) -> String {
if is_binary {
match engine {
DatabaseDrivers::sqlite3 => {
format!("hex(`{column}`) AS `{column}`")
}
DatabaseDrivers::mysql => {
format!("HEX(`{column}`) AS `{column}`")
}
DatabaseDrivers::pgsql => {
format!("encode({column}, 'hex') AS {column}")
}
}
} else {
quote_identifier(engine, column)
}
}
pub fn quote_identifier(engine: DatabaseDrivers, identifier: &str) -> String {
match engine {
DatabaseDrivers::sqlite3 | DatabaseDrivers::mysql => format!("`{identifier}`"),
DatabaseDrivers::pgsql => identifier.to_string(),
}
}
pub fn insert_ignore_prefix(engine: DatabaseDrivers) -> &'static str {
match engine {
DatabaseDrivers::sqlite3 => "INSERT OR IGNORE INTO",
DatabaseDrivers::mysql => "INSERT IGNORE INTO",
DatabaseDrivers::pgsql => "INSERT INTO",
}
}
pub fn insert_ignore_suffix(engine: DatabaseDrivers, conflict_column: &str) -> String {
match engine {
DatabaseDrivers::sqlite3 | DatabaseDrivers::mysql => String::new(),
DatabaseDrivers::pgsql => format!(" ON CONFLICT ({conflict_column}) DO NOTHING"),
}
}
pub fn update_ignore_prefix(engine: DatabaseDrivers) -> &'static str {
match engine {
DatabaseDrivers::sqlite3 => "UPDATE OR IGNORE",
DatabaseDrivers::mysql => "UPDATE IGNORE",
DatabaseDrivers::pgsql => "UPDATE",
}
}
pub fn upsert_conflict_clause(engine: DatabaseDrivers, conflict_column: &str, update_columns: &[&str]) -> String {
match engine {
DatabaseDrivers::sqlite3 | DatabaseDrivers::pgsql => {
let updates: Vec<String> = update_columns
.iter()
.map(|col| {
let quoted = quote_identifier(engine, col);
format!("{quoted}=excluded.{quoted}")
})
.collect();
format!(
"ON CONFLICT ({}) DO UPDATE SET {}",
quote_identifier(engine, conflict_column),
updates.join(", ")
)
}
DatabaseDrivers::mysql => {
let updates: Vec<String> = update_columns
.iter()
.map(|col| {
let quoted = quote_identifier(engine, col);
format!("{quoted}=VALUES({quoted})")
})
.collect();
format!("ON DUPLICATE KEY UPDATE {}", updates.join(", "))
}
}
}
pub fn limit_offset(engine: DatabaseDrivers, start: u64, length: u64) -> String {
match engine {
DatabaseDrivers::sqlite3 | DatabaseDrivers::mysql => format!("LIMIT {start}, {length}"),
DatabaseDrivers::pgsql => format!("LIMIT {length} OFFSET {start}"),
}
}
pub fn build_delete_hash_query(
engine: DatabaseDrivers,
table_name: &str,
column_name: &str,
hash_value: &str,
is_binary: bool,
) -> String {
let quoted_table = quote_identifier(engine, table_name);
let quoted_column = quote_identifier(engine, column_name);
let value = format_hash_value(engine, hash_value, is_binary);
format!(
"DELETE FROM {quoted_table} WHERE {quoted_column}={value}"
)
}
pub fn build_insert_ignore_hash_query(
engine: DatabaseDrivers,
table_name: &str,
column_name: &str,
hash_value: &str,
is_binary: bool,
) -> String {
let quoted_table = quote_identifier(engine, table_name);
let quoted_column = quote_identifier(engine, column_name);
let value = format_hash_value(engine, hash_value, is_binary);
let prefix = insert_ignore_prefix(engine);
let suffix = insert_ignore_suffix(engine, column_name);
format!(
"{prefix} {quoted_table} ({quoted_column}) VALUES ({value}){suffix}"
)
}
pub fn build_select_hash_query(
engine: DatabaseDrivers,
table_name: &str,
hash_column: &str,
additional_columns: &[&str],
is_binary: bool,
start: u64,
length: u64,
) -> String {
let quoted_table = quote_identifier(engine, table_name);
let hash_select = format_hex_select(engine, hash_column, is_binary);
let columns = if additional_columns.is_empty() {
hash_select
} else {
let additional: Vec<String> = additional_columns
.iter()
.map(|col| quote_identifier(engine, col))
.collect();
format!("{}, {}", hash_select, additional.join(", "))
};
let limit = limit_offset(engine, start, length);
format!(
"SELECT {columns} FROM {quoted_table} {limit}"
)
}
pub fn build_upsert_torrent_query(
engine: DatabaseDrivers,
table_name: &str,
column_infohash: &str,
value_columns: &[(&str, &str)],
update_columns: &[&str],
hash_value: &str,
is_binary: bool,
) -> String {
let quoted_table = quote_identifier(engine, table_name);
let quoted_infohash = quote_identifier(engine, column_infohash);
let hash_val = format_hash_value(engine, hash_value, is_binary);
let mut col_names = vec![quoted_infohash.clone()];
let mut col_values = vec![hash_val];
for (col, val) in value_columns {
col_names.push(quote_identifier(engine, col));
col_values.push(val.to_string());
}
let conflict = upsert_conflict_clause(engine, column_infohash, update_columns);
format!(
"INSERT INTO {} ({}) VALUES ({}) {}",
quoted_table,
col_names.join(", "),
col_values.join(", "),
conflict
)
}
pub fn build_update_ignore_torrent_query(
engine: DatabaseDrivers,
table_name: &str,
column_infohash: &str,
set_columns: &[(&str, &str)],
hash_value: &str,
is_binary: bool,
) -> String {
let quoted_table = quote_identifier(engine, table_name);
let quoted_infohash = quote_identifier(engine, column_infohash);
let hash_val = format_hash_value(engine, hash_value, is_binary);
let prefix = update_ignore_prefix(engine);
let sets: Vec<String> = set_columns
.iter()
.map(|(col, val)| format!("{}={}", quote_identifier(engine, col), val))
.collect();
format!(
"{} {} SET {} WHERE {}={}",
prefix,
quoted_table,
sets.join(", "),
quoted_infohash,
hash_val
)
}
pub fn engine_name(engine: DatabaseDrivers) -> &'static str {
match engine {
DatabaseDrivers::sqlite3 => "SQLite",
DatabaseDrivers::mysql => "MySQL",
DatabaseDrivers::pgsql => "PgSQL",
}
}