1use std::{error, fmt};
2
3#[derive(Debug)]
4pub enum Error {
5 MySQL(mysql::Error),
6 RowError(mysql::FromRowError),
7 RowExpected,
8}
9
10impl fmt::Display for Error {
11 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12 match *self {
13 Error::MySQL(ref err) => err.fmt(f),
14 Error::RowError(ref err) => err.fmt(f),
15 Error::RowExpected => write!(f, "row expected"),
16 }
17 }
18}
19
20impl error::Error for Error {}
21
22impl From<mysql::Error> for Error {
23 fn from(err: mysql::Error) -> Self {
24 Error::MySQL(err)
25 }
26}
27
28impl From<mysql::FromRowError> for Error {
29 fn from(err: mysql::FromRowError) -> Self {
30 Error::RowError(err)
31 }
32}
33
34pub struct Queries<'a> {
35 pool: &'a mysql::Pool,
36}
37
38pub fn new(pool: &mysql::Pool) -> Queries {
39 return Queries { pool: pool };
40}
41
42impl<'a> Queries<'a> {
43 pub fn update_db_size(&self) -> Result<(), Error> {
44 self.pool.prep_exec(
46 r#"CREATE TABLE IF NOT EXISTS quotas (
47 name VARCHAR(64) NOT NULL,
48 bytes BIGINT UNSIGNED NOT NULL,
49 mbytes FLOAT UNSIGNED NOT NULL,
50 quota BIGINT UNSIGNED,
51 enabled TINYINT(1) DEFAULT 0,
52 cdate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
53 edate timestamp NULL DEFAULT NULL,
54 PRIMARY KEY(name))"#,
55 (),
56 )?;
57
58 let mut tr = self.pool.start_transaction(true, None, None)?;
61 tr.prep_exec(
62 r#"INSERT INTO quotas (name, bytes, mbytes)
63 SELECT t.name, t.bytes, t.mbytes
64 FROM
65 (SELECT
66 table_schema 'name',
67 SUM(data_length + index_length) 'bytes',
68 ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) 'mbytes'
69 FROM information_schema.tables
70 GROUP BY table_schema) as t
71 ON DUPLICATE KEY UPDATE bytes=t.bytes, mbytes=t.mbytes"#,
72 (),
73 )?;
74 tr.commit()?;
75 Ok(())
76 }
77
78 pub fn enforce_quota(&self) -> Result<(), Error> {
79 let rows = self.pool.prep_exec(
80 "SELECT name FROM quotas WHERE bytes > quota AND enabled=1",
81 (),
82 )?;
83 for row in rows {
84 let row = row.map_err(Error::MySQL)?;
85 let dbname = mysql::from_row_opt::<String>(row)?;
86 let users = self
88 .pool
89 .prep_exec("SELECT user, host FROM mysql.db WHERE Db=?", (&dbname,))?;
90 for user in users {
91 let user = user.map_err(Error::MySQL)?;
92 let (user, host) = mysql::from_row_opt::<(String, String)>(user)?;
93 let mut tr = self.pool.start_transaction(true, None, None)?;
94 tr.prep_exec(
95 format!(
96 "REVOKE INSERT, UPDATE, CREATE, ALTER ON `{}`.* FROM '{}'@'{}'",
97 &dbname, user, host
98 ),
99 (),
100 )?;
101 tr.prep_exec(
102 "UPDATE quotas SET enabled=2, edate=NOW() WHERE name=?",
103 (&dbname,),
104 )?;
105 tr.commit()?;
106 }
107 }
108 Ok(())
109 }
110
111 pub fn revoke_quota(&self) -> Result<(), Error> {
112 let rows = self.pool.prep_exec(
113 "SELECT name FROM quotas WHERE bytes < quota AND enabled > 1",
114 (),
115 )?;
116 for row in rows {
117 let row = row.map_err(Error::MySQL)?;
118 let dbname = mysql::from_row_opt::<String>(row)?;
119 let users = self
121 .pool
122 .prep_exec("SELECT user, host FROM mysql.db WHERE Db=?", (&dbname,))?;
123 for user in users {
124 let user = user.map_err(Error::MySQL)?;
125 let (user, host) = mysql::from_row_opt::<(String, String)>(user)?;
126 let mut tr = self.pool.start_transaction(true, None, None)?;
127 tr.prep_exec(
128 format!(
129 "GRANT ALL PRIVILEGES ON `{}`.* TO '{}'@'{}'",
130 &dbname, user, host
131 ),
132 (),
133 )?;
134 tr.prep_exec(
135 "UPDATE quotas SET enabled=1, edate=NOW() WHERE name=?",
136 (&dbname,),
137 )?;
138 tr.commit()?;
139 }
140 }
141 Ok(())
142 }
143}