use chrono::Local;
use json::Null;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::fs;
use std::panic::{self, AssertUnwindSafe};
#[cfg(any(feature = "db-mysql", feature = "db-sqlite", feature = "db-mssql"))]
use std::process::Command;
use std::sync::RwLock;
use std::vec;
use crate::config::{Config, Connection};
#[cfg(feature = "db-mssql")]
use crate::types::mssql::Mssql;
#[cfg(feature = "db-mysql")]
use crate::types::mysql::Mysql;
#[cfg(feature = "db-pgsql")]
use crate::types::pgsql::Pgsql;
#[cfg(feature = "db-sqlite")]
use crate::types::sqlite::Sqlite;
use crate::types::Mode;
use crate::types::{DbMode, TableOptions};
use json::object;
use json::JsonValue;
use log::{error, info};
pub mod config;
pub mod error;
pub mod fields;
pub mod pools;
pub mod types;
pub use error::{DbError, DbResult};
lazy_static! {
pub static ref DB: RwLock<HashMap<String, Db>> = RwLock::new(HashMap::new());
static ref CONNECTION: RwLock<HashMap<String, Connection>> = RwLock::new(HashMap::new());
static ref DEFAULT: RwLock<HashMap<usize, String>> = RwLock::new(HashMap::new());
static ref TABLE_FIELDS: RwLock<HashMap<String, JsonValue>> = RwLock::new(HashMap::new());
}
#[derive(Clone)]
pub enum Db {
#[cfg(feature = "db-mysql")]
Mysql(Mysql),
#[cfg(feature = "db-sqlite")]
Sqlite(Sqlite),
#[cfg(feature = "db-mssql")]
Mssql(Mssql),
#[cfg(feature = "db-pgsql")]
Pgsql(Pgsql),
None,
}
impl Db {
pub fn load(path: &str) -> Result<Db, String> {
let conf = fs::read_to_string(path);
let t = match conf {
Ok(str) => match json::parse(str.as_str()) {
Ok(config) => Db::new(Config::from(config))?,
Err(_) => Db::new(Config::create(path.into(), false))?,
},
Err(_) => Db::new(Config::create(path.into(), false))?,
};
Ok(t)
}
pub fn new(config: Config) -> Result<Db, String> {
let connection = match config.connections.get(&*config.default) {
None => return Err(format!("无此配置: {}", config.default)),
Some(e) => e,
};
for (name, conn) in config.connections.clone() {
let mut conn_guard = match CONNECTION.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
if conn_guard.get(&name).is_none() {
conn_guard.insert(name, conn);
}
}
{
let mut default_guard = match DEFAULT.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
default_guard.insert(0, config.default.clone());
}
let db = Db::setlist(connection.clone(), config.default.clone());
match db {
Ok(db) => {
let mut db_guard = match DB.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
db_guard.insert(config.default.clone(), db);
match db_guard.get(&*config.default.clone()) {
Some(r) => Ok(r.clone()),
None => Err("数据库初始化失败".to_string()),
}
}
Err(e) => Err(e),
}
}
fn setlist(mut connection: Connection, default: String) -> Result<Db, String> {
match connection.mode.str().as_str() {
#[cfg(feature = "db-mysql")]
"mysql" => match Mysql::connect(connection.clone(), default.clone()) {
Ok(e) => Ok(Db::Mysql(e)),
Err(e) => Err(e),
},
#[cfg(feature = "db-sqlite")]
"sqlite" => match Sqlite::connect(connection.clone(), default.clone()) {
Ok(e) => Ok(Db::Sqlite(e)),
Err(e) => Err(e),
},
#[cfg(feature = "db-mssql")]
"mssql" => match Mssql::connect(connection.clone(), default.clone()) {
Ok(e) => Ok(Db::Mssql(e)),
Err(e) => Err(e),
},
#[cfg(feature = "db-pgsql")]
"pgsql" => match Pgsql::connect(connection.clone(), default.clone()) {
Ok(e) => Ok(Db::Pgsql(e)),
Err(e) => Err(e),
},
_ => Err("未匹配数据库".to_string()),
}
}
pub fn get_config_default(&mut self) -> String {
let default_guard = match DEFAULT.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
match default_guard.get(&0) {
Some(d) => d.clone(),
None => String::new(),
}
}
pub fn get_config_list(&mut self) -> Vec<String> {
let conn_guard = match CONNECTION.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
conn_guard
.iter()
.map(|x| x.0.clone())
.collect::<Vec<String>>()
}
pub fn connection(&mut self, name: &str) -> Db {
let default_guard = match DEFAULT.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
let default = match default_guard.get(&0) {
Some(d) => d.clone(),
None => String::new(),
};
drop(default_guard);
let name = if name.is_empty() { &default } else { name };
let db_guard = match DB.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
if let Some(db) = db_guard.get(name) {
return db.clone();
}
drop(db_guard);
let conn_guard = match CONNECTION.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
let connection = match conn_guard.get(name) {
Some(c) => c.clone(),
None => {
drop(conn_guard);
return self.clone();
}
};
drop(conn_guard);
let db = match Db::setlist(connection, name.to_string()) {
Ok(e) => e,
Err(_) => {
let db_guard = match DB.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
match db_guard.get(name) {
Some(db) => return db.clone(),
None => return self.clone(),
}
}
};
let mut db_guard = match DB.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
db_guard.insert(name.to_string(), db.clone());
db
}
pub fn connection_add(&mut self, config_name: &str, connection: Connection) -> Db {
let name = if config_name.is_empty() {
let default_guard = match DEFAULT.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
match default_guard.get(&0) {
Some(d) => d.clone(),
None => String::new(),
}
} else {
config_name.to_string()
};
let db_guard = match DB.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
if let Some(db) = db_guard.get(&name) {
return db.clone();
}
drop(db_guard);
{
let mut conn_guard = match CONNECTION.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
conn_guard.insert(name.clone(), connection.clone());
}
let db = match Db::setlist(connection, name.clone()) {
Ok(e) => e,
Err(_) => {
let db_guard = match DB.read() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
match db_guard.get(&name) {
Some(db) => return db.clone(),
None => return self.clone(),
}
}
};
let mut db_guard = match DB.write() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
db_guard.insert(name, db.clone());
db
}
pub fn table_create(&mut self, options: TableOptions) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.table_create(options),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.table_create(options),
#[cfg(feature = "db-mssql")]
Db::Mssql(_) => JsonValue::from(false),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.table_create(options),
_ => JsonValue::from(false),
}
}
pub fn table_update(&mut self, options: TableOptions) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.table_update(options),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.table_update(options),
#[cfg(feature = "db-mssql")]
Db::Mssql(_) => JsonValue::from(false),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.table_update(options),
_ => JsonValue::from(false),
}
}
pub fn table_info(&mut self, table: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.table_info(table),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.table_info(table),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.table_info(table),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.table_info(table),
_ => object! {},
}
}
pub fn table_is_exist(&mut self, name: &str) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.table_is_exist(name),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.table_is_exist(name),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.table_is_exist(name),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.table_is_exist(name),
_ => false,
}
}
pub fn table(&mut self, name: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.table(name);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.table(name);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.table(name);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.table(name);
}
_ => {}
};
self
}
pub fn change_table(&mut self, name: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.change_table(name);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.change_table(name);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.change_table(name);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.change_table(name);
}
_ => {}
};
self
}
pub fn autoinc(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.autoinc();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.autoinc();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.autoinc();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.autoinc();
}
_ => {}
};
self
}
pub fn timestamps(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.timestamps();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.timestamps();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.timestamps();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.timestamps();
}
_ => {}
};
self
}
pub fn fetch_sql(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.fetch_sql();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.fetch_sql();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.fetch_sql();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.fetch_sql();
}
_ => {}
};
self
}
pub fn order(&mut self, field: &str, by: bool) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.order(field, by);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.order(field, by);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.order(field, by);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.order(field, by);
}
_ => {}
}
self
}
pub fn group(&mut self, field: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.group(field);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.group(field);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.group(field);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.group(field);
}
_ => {}
}
self
}
pub fn having(&mut self, expr: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.having(expr);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.having(expr);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.having(expr);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.having(expr);
}
_ => {}
};
self
}
pub fn distinct(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.distinct();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.distinct();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.distinct();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.distinct();
}
_ => {}
}
self
}
pub fn json(&mut self, field: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.json(field);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.json(field);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.json(field);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.json(field);
}
_ => {}
}
self
}
pub fn location(&mut self, field: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.location(field);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.location(field);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.location(field);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.location(field);
}
_ => {}
}
self
}
pub fn column(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.column(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.column(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.column(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.column(field),
_ => object! {},
}
}
pub fn where_and(&mut self, field: &str, compare: &str, value: JsonValue) -> &mut Self {
if field.contains("|") {
let fields: Vec<&str> = field.split("|").collect();
for field in fields.iter() {
self.where_or(field.to_string().as_str(), compare, value.clone());
}
return self;
}
let compare = compare.to_lowercase();
let compare = compare.replace(" ", "");
let compare = compare.as_str();
let value = match &value {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
JsonValue::Array(arr) => JsonValue::Array(
arr.iter()
.map(|v| match v {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
_ => v.clone(),
})
.collect(),
),
JsonValue::Object(obj) => {
let mut new_obj = object::Object::new();
for (k, v) in obj.iter() {
let vv = match v {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
_ => v.clone(),
};
new_obj.insert(k, vv);
}
JsonValue::Object(new_obj)
}
_ => value.clone(),
};
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_and(field, compare, value);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_and(field, compare, value);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_and(field, compare, value);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_and(field, compare, value);
}
_ => {}
};
self
}
pub fn where_or(&mut self, field: &str, compare: &str, value: JsonValue) -> &mut Self {
if field.contains("|") {
let fields: Vec<&str> = field.split("|").collect();
for field in fields.iter() {
self.where_or(field.to_string().as_str(), compare, value.clone());
}
return self;
}
let compare = compare.to_lowercase();
let compare = compare.replace(" ", "");
let compare = compare.as_str();
let value = match &value {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
JsonValue::Array(arr) => JsonValue::Array(
arr.iter()
.map(|v| match v {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
_ => v.clone(),
})
.collect(),
),
JsonValue::Object(obj) => {
let mut new_obj = object::Object::new();
for (k, v) in obj.iter() {
let vv = match v {
JsonValue::Short(s) => JsonValue::String(s.as_str().replace("'", "''")),
JsonValue::String(s) => JsonValue::String(s.replace("'", "''")),
_ => v.clone(),
};
new_obj.insert(k, vv);
}
JsonValue::Object(new_obj)
}
_ => value.clone(),
};
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_or(field, compare, value);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_or(field, compare, value);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_or(field, compare, value);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_or(field, compare, value);
}
_ => {}
};
self
}
pub fn where_column(&mut self, field_a: &str, compare: &str, field_b: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_column(field_a, compare, field_b);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_column(field_a, compare, field_b);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_column(field_a, compare, field_b);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_column(field_a, compare, field_b);
}
_ => {}
};
self
}
pub fn where_raw(&mut self, expr: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_raw(expr);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_raw(expr);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_raw(expr);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_raw(expr);
}
_ => {}
};
self
}
pub fn where_in_sub(&mut self, field: &str, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_in_sub(field, sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_in_sub(field, sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_in_sub(field, sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_in_sub(field, sub_sql);
}
_ => {}
};
self
}
pub fn where_not_in_sub(&mut self, field: &str, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_not_in_sub(field, sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_not_in_sub(field, sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_not_in_sub(field, sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_not_in_sub(field, sub_sql);
}
_ => {}
};
self
}
pub fn where_exists(&mut self, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_exists(sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_exists(sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_exists(sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_exists(sub_sql);
}
_ => {}
};
self
}
pub fn where_not_exists(&mut self, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.where_not_exists(sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.where_not_exists(sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.where_not_exists(sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.where_not_exists(sub_sql);
}
_ => {}
};
self
}
pub fn update_column(&mut self, field_a: &str, compare: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.update_column(field_a, compare);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.update_column(field_a, compare);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.update_column(field_a, compare);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.update_column(field_a, compare);
}
_ => {}
};
self
}
pub fn count(&mut self) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.count(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.count(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.count(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.count(),
_ => JsonValue::from(0),
}
}
pub fn max(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.max(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.max(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.max(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.max(field),
_ => object! {},
}
}
pub fn min(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.min(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.min(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.min(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.min(field),
_ => object! {},
}
}
pub fn sum(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.sum(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.sum(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.sum(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.sum(field),
_ => object! {},
}
}
pub fn avg(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.avg(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.avg(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.avg(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.avg(field),
_ => object! {},
}
}
pub fn select(&mut self) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.select(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.select(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.select(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.select(),
_ => object! {},
}
}
pub fn find(&mut self) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.find(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.find(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.find(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.find(),
_ => object! {},
}
}
pub fn value(&mut self, field: &str) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.value(field),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.value(field),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.value(field),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.value(field),
_ => Null,
}
}
pub fn insert(&mut self, mut data: JsonValue) -> JsonValue {
let ts = match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.params.timestamps,
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.params.timestamps,
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.params.timestamps,
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.params.timestamps,
_ => false,
};
if ts {
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
if data["created_at"].is_empty() {
data["created_at"] = now.clone().into();
}
if data["updated_at"].is_empty() {
data["updated_at"] = now.into();
}
}
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.insert(data),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.insert(data),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.insert(data),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.insert(data),
_ => JsonValue::from(""),
}
}
pub fn insert_all(&mut self, data: JsonValue) -> JsonValue {
let data = if data.is_object() {
JsonValue::from(vec![data])
} else {
data
};
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.insert_all(data.clone()),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.insert_all(data.clone()),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.insert_all(data.clone()),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.insert_all(data.clone()),
_ => object! {},
}
}
pub fn upsert(&mut self, data: JsonValue, conflict_fields: Vec<&str>) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.upsert(data.clone(), conflict_fields),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.upsert(data.clone(), conflict_fields),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.upsert(data.clone(), conflict_fields),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.upsert(data.clone(), conflict_fields),
_ => JsonValue::from(""),
}
}
pub fn page(&mut self, mut page: i32, limit: i32) -> &mut Self {
if page <= 0 {
page = 1;
}
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.page(page, limit);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.page(page, limit);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.page(page, limit);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.page(page, limit);
}
_ => {}
};
self
}
pub fn limit(&mut self, count: i32) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.limit(count);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.limit(count);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.limit(count);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.limit(count);
}
_ => {}
};
self
}
pub fn update(&mut self, mut data: JsonValue) -> JsonValue {
let ts = match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.params.timestamps,
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.params.timestamps,
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.params.timestamps,
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.params.timestamps,
_ => false,
};
if ts {
let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
if data["updated_at"].is_empty() {
data["updated_at"] = now.into();
}
}
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.update(data),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.update(data),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.update(data),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.update(data),
_ => JsonValue::from(0),
}
}
pub fn update_all(&mut self, data: JsonValue) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.update_all(data.clone()),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.update_all(data.clone()),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.update_all(data.clone()),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.update_all(data.clone()),
_ => JsonValue::from(0),
}
}
pub fn get_connection(&mut self) -> Connection {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.connection.clone(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.connection.clone(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.connection.clone(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.connection.clone(),
_ => Connection::new(""),
}
}
pub fn database_tables(&mut self) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.database_tables(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.database_tables(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.database_tables(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.database_tables(),
_ => JsonValue::Array(vec![]),
}
}
pub fn database_create(&mut self, name: &str) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.database_create(name),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.database_create(name),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.database_create(name),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.database_create(name),
_ => false,
}
}
pub fn truncate(&mut self, table: &str) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.truncate(table),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.truncate(table),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.truncate(table),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.truncate(table),
_ => false,
}
}
pub fn backups(&mut self, filename: &str) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
match Command::new("mysqldump")
.arg("-u")
.arg(db.connection.username.clone())
.arg("-p")
.arg(db.connection.userpass.clone())
.arg(db.connection.database.clone())
.arg(">")
.arg(filename)
.output()
{
Ok(output) => {
if output.status.success() {
info!("数据库备份完成!");
return true;
}
true
}
Err(e) => {
error!("mysqldump 命令执行失败:{e}");
false
}
}
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(_db) => false,
#[cfg(feature = "db-mssql")]
Db::Mssql(_db) => false,
#[cfg(feature = "db-pgsql")]
Db::Pgsql(_db) => false,
_ => false,
}
}
pub fn delete(&mut self) -> JsonValue {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.delete(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.delete(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.delete(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.delete(),
_ => JsonValue::from(0),
}
}
pub fn field(&mut self, name: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.field(name);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.field(name);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.field(name);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.field(name);
}
_ => {}
};
self
}
pub fn field_raw(&mut self, expr: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.field_raw(expr);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.field_raw(expr);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.field_raw(expr);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.field_raw(expr);
}
_ => {}
};
self
}
pub fn hidden(&mut self, name: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.hidden(name);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.hidden(name);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.hidden(name);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.hidden(name);
}
_ => {}
};
self
}
pub fn transaction(&mut self) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.transaction(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.transaction(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.transaction(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.transaction(),
_ => false,
}
}
pub fn commit(&mut self) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.commit(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.commit(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.commit(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.commit(),
_ => false,
}
}
pub fn rollback(&mut self) -> bool {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.rollback(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.rollback(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.rollback(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.rollback(),
_ => false,
}
}
pub fn transaction_fn<F, R>(&mut self, f: F) -> Result<R, String>
where
F: FnOnce(&mut Self) -> Result<R, String>,
{
if !self.transaction() {
return Err("事务开启失败".to_string());
}
let result = panic::catch_unwind(AssertUnwindSafe(|| f(self)));
match result {
Ok(Ok(v)) => {
if self.commit() {
Ok(v)
} else {
self.rollback();
Err("事务提交失败".to_string())
}
}
Ok(Err(e)) => {
self.rollback();
Err(e)
}
Err(payload) => {
self.rollback();
panic::resume_unwind(payload);
}
}
}
pub fn sql(&mut self, sql: &str) -> Result<JsonValue, String> {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.sql(sql),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.sql(sql),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.sql(sql),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.sql(sql),
_ => Err("".to_string()),
}
}
pub fn sql_execute(&mut self, sql: &str) -> Result<JsonValue, String> {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.sql_execute(sql),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.sql_execute(sql),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.sql_execute(sql),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.sql_execute(sql),
_ => Err("".to_string()),
}
}
pub fn inc(&mut self, field: &str, num: f64) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.inc(field, num);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.inc(field, num);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.inc(field, num);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.inc(field, num);
}
_ => {}
}
self
}
pub fn dec(&mut self, field: &str, num: f64) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.dec(field, num);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.dec(field, num);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.dec(field, num);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.dec(field, num);
}
_ => {}
}
self
}
pub fn buildsql(&mut self) -> String {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => db.buildsql(),
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => db.buildsql(),
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => db.buildsql(),
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => db.buildsql(),
_ => "".to_string(),
}
}
pub fn join_fields(&mut self, fields: Vec<&str>) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.join_fields(fields);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.join_fields(fields);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.join_fields(fields);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.join_fields(fields);
}
_ => {}
}
self
}
pub fn join(
&mut self,
main_table: &str,
main_fields: &str,
right_table: &str,
right_fields: &str,
) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.join(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.join(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.join(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.join(main_table, main_fields, right_table, right_fields);
}
_ => {}
}
self
}
pub fn join_inner(&mut self, table: &str, main_fields: &str, second_fields: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.join_inner(table, main_fields, second_fields);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.join_inner(table, main_fields, second_fields);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.join_inner(table, main_fields, second_fields);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.join_inner(table, main_fields, second_fields);
}
_ => {}
}
self
}
pub fn join_right(
&mut self,
main_table: &str,
main_fields: &str,
right_table: &str,
right_fields: &str,
) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.join_right(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.join_right(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.join_right(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.join_right(main_table, main_fields, right_table, right_fields);
}
_ => {}
}
self
}
pub fn join_full(
&mut self,
main_table: &str,
main_fields: &str,
right_table: &str,
right_fields: &str,
) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.join_full(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.join_full(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.join_full(main_table, main_fields, right_table, right_fields);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.join_full(main_table, main_fields, right_table, right_fields);
}
_ => {}
}
self
}
pub fn union(&mut self, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.union(sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.union(sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.union(sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.union(sub_sql);
}
_ => {}
};
self
}
pub fn union_all(&mut self, sub_sql: &str) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.union_all(sub_sql);
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.union_all(sub_sql);
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.union_all(sub_sql);
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.union_all(sub_sql);
}
_ => {}
};
self
}
pub fn lock_for_update(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.lock_for_update();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.lock_for_update();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.lock_for_update();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.lock_for_update();
}
_ => {}
};
self
}
pub fn lock_for_share(&mut self) -> &mut Self {
match self {
#[cfg(feature = "db-mysql")]
Db::Mysql(db) => {
db.lock_for_share();
}
#[cfg(feature = "db-sqlite")]
Db::Sqlite(db) => {
db.lock_for_share();
}
#[cfg(feature = "db-mssql")]
Db::Mssql(db) => {
db.lock_for_share();
}
#[cfg(feature = "db-pgsql")]
Db::Pgsql(db) => {
db.lock_for_share();
}
_ => {}
};
self
}
}
#[derive(Clone, Debug)]
pub struct Table {
pub version: String,
pub table: String,
pub title: String,
pub primary_key: String,
pub unique: Vec<String>,
pub index: Vec<Vec<String>>,
pub fields: JsonValue,
pub partition: bool,
pub partition_columns: JsonValue,
}
impl Table {
pub fn to_str(self) -> String {
let data = object! {
version:self.version,
table:self.table,
title:self.title,
primary_key:self.primary_key,
unique:self.unique,
index:self.index,
fields:self.fields,
partition:self.partition,
partition_columns:self.partition_columns,
};
data.to_string()
}
pub fn parse(mut data: JsonValue) -> Table {
let mut unique = vec![];
for item in 0..data["unique"].len() {
let str = data["unique"][item].clone().to_string();
unique.push(str);
}
let mut index = vec![];
for item in data["index"].members_mut() {
let mut row = vec![];
for col in item.members_mut() {
row.push(col.to_string());
}
if !row.is_empty() {
index.push(row);
}
}
Self {
version: data["version"].to_string(),
table: data["table"].to_string(),
title: data["title"].to_string(),
primary_key: data["primary_key"].to_string(),
unique,
index,
fields: data["fields"].clone(),
partition: data["partition"].as_bool().unwrap_or(false),
partition_columns: data["partition_columns"].clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::Table;
use json::{array, object};
fn sample_table() -> Table {
Table {
version: "1.0.0".to_string(),
table: "users".to_string(),
title: "Users".to_string(),
primary_key: "id".to_string(),
unique: vec!["email".to_string(), "phone".to_string()],
index: vec![
vec!["name".to_string()],
vec!["created_at".to_string(), "status".to_string()],
],
fields: object! {
id: "bigint",
name: "string",
active: true,
},
partition: true,
partition_columns: array!["created_at", "region"],
}
}
fn assert_tables_equal(actual: &Table, expected: &Table) {
assert_eq!(actual.version, expected.version);
assert_eq!(actual.table, expected.table);
assert_eq!(actual.title, expected.title);
assert_eq!(actual.primary_key, expected.primary_key);
assert_eq!(actual.unique, expected.unique);
assert_eq!(actual.index, expected.index);
assert_eq!(actual.fields, expected.fields);
assert_eq!(actual.partition, expected.partition);
assert_eq!(actual.partition_columns, expected.partition_columns);
}
#[test]
fn table_to_str_contains_expected_keys_and_values() {
let json_str = sample_table().to_str();
assert!(json_str.contains("\"version\":\"1.0.0\""));
assert!(json_str.contains("\"table\":\"users\""));
assert!(json_str.contains("\"primary_key\":\"id\""));
assert!(json_str.contains("\"partition\":true"));
assert!(json_str.contains("\"unique\":[\"email\",\"phone\"]"));
assert!(json_str.contains("\"partition_columns\":[\"created_at\",\"region\"]"));
}
#[test]
fn table_parse_reads_all_fields() {
let data = object! {
version: "2.1.3",
table: "orders",
title: "Orders",
primary_key: "order_id",
unique: array!["order_no", "out_trade_no"],
index: array![
array!["user_id"],
array!["created_at", "status"],
],
fields: object! {
order_id: "bigint",
amount: "decimal",
},
partition: true,
partition_columns: array!["created_at"],
};
let table = Table::parse(data);
assert_eq!(table.version, "2.1.3");
assert_eq!(table.table, "orders");
assert_eq!(table.title, "Orders");
assert_eq!(table.primary_key, "order_id");
assert_eq!(table.unique, vec!["order_no", "out_trade_no"]);
assert_eq!(
table.index,
vec![vec!["user_id"], vec!["created_at", "status"]]
);
assert_eq!(
table.fields,
object! { order_id: "bigint", amount: "decimal" }
);
assert!(table.partition);
assert_eq!(table.partition_columns, array!["created_at"]);
}
#[test]
fn table_roundtrip_to_str_and_parse() {
let original = sample_table();
let json_str = original.clone().to_str();
let parsed_json = json::parse(&json_str).expect("valid table json");
let parsed_table = Table::parse(parsed_json);
assert_tables_equal(&parsed_table, &original);
}
#[test]
fn table_parse_with_missing_fields_uses_defaults() {
let data = object! {};
let table = Table::parse(data);
assert_eq!(table.version, "null");
assert_eq!(table.table, "null");
assert_eq!(table.title, "null");
assert_eq!(table.primary_key, "null");
assert!(table.unique.is_empty());
assert!(table.index.is_empty());
assert_eq!(table.fields, json::JsonValue::Null);
assert!(!table.partition);
assert_eq!(table.partition_columns, json::JsonValue::Null);
}
#[test]
fn table_parse_nested_index_arrays() {
let data = object! {
index: array![
array!["col_a", "col_b"],
array!["col_c"],
]
};
let table = Table::parse(data);
assert_eq!(table.index, vec![vec!["col_a", "col_b"], vec!["col_c"]]);
}
#[test]
fn table_parse_unique_array() {
let data = object! {
unique: array!["email", "username"]
};
let table = Table::parse(data);
assert_eq!(table.unique, vec!["email", "username"]);
}
#[test]
fn table_parse_partition_and_partition_columns() {
let data = object! {
partition: true,
partition_columns: array!["created_at", "tenant_id"]
};
let table = Table::parse(data);
assert!(table.partition);
assert_eq!(table.partition_columns, array!["created_at", "tenant_id"]);
}
}