1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2025-06-20 00:15:17 +00:00

Update mysql

This commit is contained in:
2022-03-09 19:21:57 +01:00
parent ae546b2d3e
commit 4aa7657e28
4 changed files with 432 additions and 171 deletions

View File

@ -1,7 +1,7 @@
use core::fmt;
use std::error;
use std::error::Error;
use std::fmt::Formatter;
use std::fmt::{Display, Formatter};
/// Simple rust error
///
@ -21,7 +21,7 @@ impl ExecError {
ExecError(msg.to_string())
}
pub fn boxed_new(msg: &str) -> Box<ExecError> {
pub fn boxed_new<D: Display>(msg: D) -> Box<ExecError> {
Box::new(ExecError(msg.to_string()))
}

View File

@ -1,17 +1,17 @@
use std::collections;
use std::collections::HashMap;
use std::error::Error;
use std::ops::Add;
use std::sync::{Arc, Mutex};
use chrono::{TimeZone, Utc};
use mysql::*;
use mysql::{Binary, Pool, ResultSet, Value};
use mysql::prelude::Queryable;
use mysql::prelude::*;
use crate::data::admin::AdminID;
use crate::data::config::{conf, DatabaseConfig};
use crate::data::conversation::ConvID;
use crate::data::error::{ExecError, Res, ResultBoxError};
use crate::data::error::{ExecError, Res};
use crate::data::group_id::GroupID;
use crate::data::user::UserID;
@ -19,19 +19,19 @@ use crate::data::user::UserID;
///
/// @author Pierre Hubert
pub type ProcessRowResult<E> = Result<E, Box<dyn Error>>;
pub type ProcessRowResult<E> = Res<E>;
// Pool shared across threads
static mut POOL: Option<Arc<Mutex<mysql::Pool>>> = None;
/// Connect to the database
pub fn connect(conf: &DatabaseConfig) -> Result<(), Box<dyn Error>> {
pub fn connect(conf: &DatabaseConfig) -> Res {
let url = format!(
"mysql://{}:{}@{}:3306/{}",
conf.username, conf.password, conf.host, conf.name
);
let pool = Pool::new(url)?;
let pool = Pool::new(Opts::from_url(&url)?)?;
let pool = Some(Arc::new(Mutex::new(pool)));
unsafe {
@ -42,7 +42,7 @@ pub fn connect(conf: &DatabaseConfig) -> Result<(), Box<dyn Error>> {
}
/// Get a connection to the database
pub fn get_connection() -> Result<mysql::PooledConn, Box<dyn Error>> {
pub fn get_connection() -> Res<mysql::PooledConn> {
let pool: Pool;
unsafe {
@ -258,18 +258,18 @@ impl QueryInfo {
/// Execute query
pub fn exec<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(self, process_function: F)
-> Result<Vec<E>, Box<dyn Error>> {
-> Res<Vec<E>> {
query(self, process_function)
}
/// Query just a row
pub fn query_row<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(self, process_function: F)
-> Result<E, Box<dyn Error>> {
-> Res<E> {
query_row(self, process_function)
}
/// Execute count query
pub fn exec_count(self) -> ResultBoxError<usize> {
pub fn exec_count(self) -> Res<usize> {
count(self)
}
@ -281,7 +281,7 @@ impl QueryInfo {
/// Struct used to read the result of a request
pub struct RowResult<'a> {
row: &'a mysql::Row
row: &'a mysql::Row,
}
impl<'a> RowResult<'a> {
@ -292,7 +292,7 @@ impl<'a> RowResult<'a> {
}
/// Find a column in result set
fn find_col(&self, name: &str) -> Result<usize, ExecError> {
fn find_col(&self, name: &str) -> Res<usize> {
let name_bytes = name.as_bytes();
let mut index = 0;
for c in self.row.columns_ref() {
@ -303,11 +303,11 @@ impl<'a> RowResult<'a> {
index += 1;
}
Err(ExecError(format!("Column {} not found in database result set!", name)))
Err(ExecError::boxed_new(format!("Column {} not found in database result set!", name)))
}
/// Find an integer included in the request
pub fn get_int64(&self, name: &str) -> Result<i64, Box<dyn Error>> {
pub fn get_int64(&self, name: &str) -> Res<i64> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -318,7 +318,7 @@ impl<'a> RowResult<'a> {
}
/// Find an integer included in the request
pub fn get_u64(&self, name: &str) -> Result<u64, Box<dyn Error>> {
pub fn get_u64(&self, name: &str) -> Res<u64> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -329,14 +329,14 @@ impl<'a> RowResult<'a> {
}
/// Get an optional unsigned number => Set to None if value is null / empty
pub fn get_optional_u64(&self, name: &str) -> ResultBoxError<Option<u64>> {
pub fn get_optional_u64(&self, name: &str) -> Res<Option<u64>> {
match self.is_null(name)? {
true => Ok(None),
false => Ok(Some(self.get_u64(name)?))
}
}
pub fn get_optional_u32(&self, name: &str) -> ResultBoxError<Option<u32>> {
pub fn get_optional_u32(&self, name: &str) -> Res<Option<u32>> {
match self.is_null(name)? {
true => Ok(None),
false => Ok(Some(self.get_u32(name)?))
@ -344,14 +344,14 @@ impl<'a> RowResult<'a> {
}
/// Get an optional unsigned number => Set to None if value is null / empty / 0
pub fn get_optional_positive_u64(&self, name: &str) -> ResultBoxError<Option<u64>> {
pub fn get_optional_positive_u64(&self, name: &str) -> Res<Option<u64>> {
Ok(match self.get_optional_u64(name)? {
None | Some(0) => None,
Some(val) => Some(val)
})
}
pub fn get_u32(&self, name: &str) -> ResultBoxError<u32> {
pub fn get_u32(&self, name: &str) -> Res<u32> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -361,7 +361,7 @@ impl<'a> RowResult<'a> {
}
}
pub fn get_u16(&self, name: &str) -> ResultBoxError<u16> {
pub fn get_u16(&self, name: &str) -> Res<u16> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -372,7 +372,7 @@ impl<'a> RowResult<'a> {
}
/// Find an integer included in the request
pub fn get_usize(&self, name: &str) -> Result<usize, Box<dyn Error>> {
pub fn get_usize(&self, name: &str) -> Res<usize> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -383,22 +383,22 @@ impl<'a> RowResult<'a> {
}
/// Get the ID of a user included in the request
pub fn get_user_id(&self, name: &str) -> ResultBoxError<UserID> {
pub fn get_user_id(&self, name: &str) -> Res<UserID> {
Ok(UserID::new(self.get_u64(name)?))
}
/// Get the ID of an admin included in the request
pub fn get_admin_id(&self, name: &str) -> ResultBoxError<AdminID> {
pub fn get_admin_id(&self, name: &str) -> Res<AdminID> {
Ok(AdminID::new(self.get_u64(name)?))
}
/// Get the ID of a group included in the response
pub fn get_group_id(&self, name: &str) -> ResultBoxError<GroupID> {
pub fn get_group_id(&self, name: &str) -> Res<GroupID> {
Ok(GroupID::new(self.get_u64(name)?))
}
/// Get the optional ID of a group included in the response
pub fn get_optional_group_id(&self, name: &str) -> ResultBoxError<Option<GroupID>> {
pub fn get_optional_group_id(&self, name: &str) -> Res<Option<GroupID>> {
Ok(match self.get_optional_u64(name)? {
None | Some(0) => None,
Some(id) => Some(GroupID::new(id))
@ -406,12 +406,12 @@ impl<'a> RowResult<'a> {
}
/// Get the ID of a conversation included in the response
pub fn get_conv_id(&self, name: &str) -> ResultBoxError<ConvID> {
pub fn get_conv_id(&self, name: &str) -> Res<ConvID> {
Ok(ConvID::new(self.get_u64(name)?))
}
/// Find a string included in the request
pub fn get_str(&self, name: &str) -> Result<String, Box<dyn Error>> {
pub fn get_str(&self, name: &str) -> Res<String> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -422,7 +422,7 @@ impl<'a> RowResult<'a> {
}
/// Check out whether a given value is null or not
pub fn is_null(&self, name: &str) -> ResultBoxError<bool> {
pub fn is_null(&self, name: &str) -> Res<bool> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
@ -433,7 +433,7 @@ impl<'a> RowResult<'a> {
}
/// Check out whether a given value is null or empty or not
pub fn is_null_or_empty(&self, name: &str) -> ResultBoxError<bool> {
pub fn is_null_or_empty(&self, name: &str) -> Res<bool> {
if self.is_null(name)? {
return Ok(true);
}
@ -442,7 +442,7 @@ impl<'a> RowResult<'a> {
}
/// Get an optional string => Set to None if string is null / empty
pub fn get_optional_str(&self, name: &str) -> ResultBoxError<Option<String>> {
pub fn get_optional_str(&self, name: &str) -> Res<Option<String>> {
match self.is_null(name)? {
true => Ok(None),
false => Ok(Some(self.get_str(name)?).map_or(None, |d| {
@ -455,12 +455,12 @@ impl<'a> RowResult<'a> {
}
/// Get legacy boolean value : 1 = true / 0 = false
pub fn get_legacy_bool(&self, name: &str) -> ResultBoxError<bool> {
pub fn get_legacy_bool(&self, name: &str) -> Res<bool> {
Ok(self.get_int64(name)? == 1)
}
/// Get a MYSQL date as a timestamp
pub fn get_date_as_time(&self, name: &str) -> ResultBoxError<u64> {
pub fn get_date_as_time(&self, name: &str) -> Res<u64> {
let value = self.row.get_opt(self.find_col(name)?);
let value: Value = value.ok_or(ExecError(format!("Could not find date field {} !", name)))??;
@ -479,7 +479,7 @@ impl<'a> RowResult<'a> {
/// Query a single row of the database
pub fn query_row<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(mut info: QueryInfo,
process_function: F) -> Result<E, Box<dyn Error>> {
process_function: F) -> Res<E> {
let table = info.table.clone();
info.limit = 1;
@ -494,7 +494,7 @@ pub fn query_row<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(mut info: QueryInf
/// Make a simple query
pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, process_function: F)
-> Result<Vec<E>, Box<dyn Error>> {
-> Res<Vec<E>> {
let mut params = vec![];
let select_elements = match info.fields.len() {
@ -576,11 +576,11 @@ pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, proce
let mut res = con.exec_iter(stmt, params)?;
// This system is made to support only one dataset
let result_set = res.next_set();
let result_set = res.iter();
if let None = result_set {
return Err(Box::new(ExecError::new("No result set in a query!")));
}
let result_set: ResultSet<Binary> = result_set.unwrap()?;
let result_set: ResultSet<Binary> = result_set.unwrap();
// Parse each result of the dataset
@ -598,7 +598,7 @@ pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, proce
}
/// Count the number of results a query would have produced
pub fn count(mut q: QueryInfo) -> ResultBoxError<usize> {
pub fn count(mut q: QueryInfo) -> Res<usize> {
q.fields.clear();
q.fields.push("COUNT(*) as count".to_string());
@ -725,25 +725,25 @@ impl InsertQuery {
}
/// Process insert
pub fn insert(self) -> ResultBoxError<Option<u64>> {
pub fn insert(self) -> Res<Option<u64>> {
insert(self)
}
/// Process insert, drop the result of the operation
pub fn insert_drop_result(self) -> ResultBoxError<()> {
pub fn insert_drop_result(self) -> Res<()> {
insert(self)?;
Ok(())
}
/// Process insert, excepting an ID in the response
pub fn insert_expect_result(self) -> ResultBoxError<u64> {
pub fn insert_expect_result(self) -> Res<u64> {
let res = insert(self)?;
res.ok_or(ExecError::boxed_new("Expected an ID in insert result!"))
}
}
/// Insert a new entry into the database
pub fn insert(query: InsertQuery) -> ResultBoxError<Option<u64>> {
pub fn insert(query: InsertQuery) -> Res<Option<u64>> {
// Collect keys
let keys = query.values
@ -862,13 +862,13 @@ impl DeleteQuery {
}
/// Execute the delete query
pub fn exec(self) -> ResultBoxError<()> {
pub fn exec(self) -> Res<()> {
delete(self)
}
}
/// Delete an entry from the database
pub fn delete(query: DeleteQuery) -> ResultBoxError<()> {
pub fn delete(query: DeleteQuery) -> Res<()> {
if query.conditions.is_empty() && query.custom_where.is_none() {
return Err(ExecError::boxed_new("DELETE without WHERE condition blocked for security reasons!"));
}
@ -993,9 +993,9 @@ impl UpdateInfo {
}
/// Set an u64 number
///
/// None => 0
/// Some => The value
///
/// None => 0
/// Some => The value
pub fn set_opt_u64_or_zero(mut self, name: &str, val: Option<u64>) -> UpdateInfo {
self.set.insert(name.to_string(), Value::from(val.unwrap_or(0)));
self
@ -1025,13 +1025,13 @@ impl UpdateInfo {
}
/// Execute the update
pub fn exec(self) -> ResultBoxError<()> {
pub fn exec(self) -> Res<()> {
update(self)
}
}
/// Execute an update query
pub fn update(u: UpdateInfo) -> ResultBoxError<()> {
pub fn update(u: UpdateInfo) -> Res<()> {
if u.cond.is_empty() {
Err(ExecError::boxed_new("Update without conditions blocked for security!"))?;
}