mysql_slowlog_parser/
types.rs

1use crate::{EntryAdminCommand, SessionLine, SqlStatementContext, StatsLine};
2use bytes::{BufMut, Bytes, BytesMut};
3use sqlparser::ast::{visit_relations, Statement};
4use std::borrow::Cow;
5use std::collections::BTreeSet;
6use std::fmt::{Display, Formatter};
7use std::ops::ControlFlow;
8use winnow_datetime::DateTime;
9
10/// a struct representing a single log entry
11#[derive(Clone, Debug, PartialEq)]
12pub struct Entry {
13    /// holds information about the call made to mysqld
14    pub call: EntryCall,
15    /// holds information about the connection that made the call
16    pub session: EntrySession,
17    /// stats about how long it took for the query to run
18    pub stats: EntryStats,
19    /// information obtained while parsing the SQL query
20    pub sql_attributes: EntrySqlAttributes,
21}
22
23impl Entry {
24    /// returns the time the entry was recorded
25    pub fn log_time(&self) -> DateTime {
26        self.call.log_time
27    }
28
29    /// returns the mysql user name that requested the command
30    pub fn user_name(&self) -> Cow<str> {
31        String::from_utf8_lossy(&self.session.user_name)
32    }
33
34    /// returns the mysql user name that requested the command
35    pub fn user_name_bytes(&self) -> Bytes {
36        self.session.user_name.clone()
37    }
38
39    /// returns the system user name that requested the command
40    pub fn sys_user_name(&self) -> Cow<str> {
41        String::from_utf8_lossy(&self.session.sys_user_name)
42    }
43
44    /// returns the system user name that requested the command
45    pub fn sys_user_name_bytes(&self) -> Bytes {
46        self.session.sys_user_name.clone()
47    }
48
49    /// returns the host name which requested the command
50    pub fn host_name(&self) -> Option<Cow<str>> {
51        if let Some(v) = &self.session.host_name {
52            Some(String::from_utf8_lossy(v.as_ref()))
53        } else {
54            None
55        }
56    }
57
58    /// returns the host name which requested the command
59    pub fn host_name_bytes(&self) -> Option<Bytes> {
60        self.session.host_name_bytes()
61    }
62
63    /// returns the ip address which requested the command
64    pub fn ip_address(&self) -> Option<Cow<'_, str>> {
65        self.session.ip_address()
66    }
67
68    /// returns the ip address which requested the command
69    pub fn ip_address_bytes(&self) -> Option<Bytes> {
70        self.session.ip_address_bytes()
71    }
72
73    /// returns the the thread id of the session which requested the command
74    pub fn thread_id(&self) -> u32 {
75        self.session.thread_id()
76    }
77
78    /// returns a ref to the entry's EntryStats struct
79    pub fn stats(&self) -> &EntryStats {
80        &self.stats
81    }
82
83    /// returns how long the query took to run
84    pub fn query_time(&self) -> f64 {
85        self.stats.query_time()
86    }
87
88    /// returns how long it took to lock
89    pub fn lock_time(&self) -> f64 {
90        self.stats.lock_time()
91    }
92
93    /// returns number of rows returned when query was executed
94    pub fn rows_sent(&self) -> u32 {
95        self.stats.rows_sent()
96    }
97
98    /// returns how many rows where examined to execute the query
99    pub fn rows_examined(&self) -> u32 {
100        self.stats.rows_examined()
101    }
102}
103
104#[derive(Clone, Debug, PartialEq)]
105///
106pub struct EntrySqlStatement {
107    /// Holds the Statement
108    pub statement: Statement,
109    pub context: Option<SqlStatementContext>,
110}
111
112impl EntrySqlStatement {
113    pub fn sql_context(&self) -> Option<SqlStatementContext> {
114        self.context.clone()
115    }
116
117    pub fn objects(&self) -> Vec<EntrySqlStatementObject> {
118        let mut visited = BTreeSet::new();
119
120        visit_relations(&self.statement, |relation| {
121            let ident = &relation.0;
122
123            let _ = visited.insert(if ident.len() == 2 {
124                EntrySqlStatementObject {
125                    schema_name: Some(ident[0].value.to_owned().into()),
126                    object_name: ident[1].value.to_owned().into(),
127                }
128            } else {
129                EntrySqlStatementObject {
130                    schema_name: None,
131                    object_name: ident.last().unwrap().value.to_owned().into(),
132                }
133            });
134
135            ControlFlow::<()>::Continue(())
136        });
137        visited.into_iter().collect()
138    }
139
140    pub fn sql_type(&self) -> EntrySqlType {
141        match self.statement {
142            Statement::Query(_) => EntrySqlType::Query,
143            Statement::Insert { .. } => EntrySqlType::Insert,
144            Statement::Update { .. } => EntrySqlType::Update,
145            Statement::Delete { .. } => EntrySqlType::Delete,
146            Statement::CreateTable { .. } => EntrySqlType::CreateTable,
147            Statement::CreateIndex { .. } => EntrySqlType::CreateIndex,
148            Statement::CreateView { .. } => EntrySqlType::CreateView,
149            Statement::AlterTable { .. } => EntrySqlType::AlterTable,
150            Statement::AlterIndex { .. } => EntrySqlType::AlterIndex,
151            Statement::Drop { .. } => EntrySqlType::Drop,
152            Statement::DropFunction { .. } => EntrySqlType::DropFunction,
153            Statement::SetVariable { .. } => EntrySqlType::SetVariable,
154            Statement::SetNames { .. } => EntrySqlType::SetNames,
155            Statement::SetNamesDefault { .. } => EntrySqlType::SetNamesDefault,
156            Statement::ShowVariable { .. } => EntrySqlType::ShowVariable,
157            Statement::ShowVariables { .. } => EntrySqlType::ShowVariables,
158            Statement::ShowCreate { .. } => EntrySqlType::ShowCreate,
159            Statement::ShowColumns { .. } => EntrySqlType::ShowColumns,
160            Statement::ShowTables { .. } => EntrySqlType::ShowTables,
161            Statement::ShowCollation { .. } => EntrySqlType::ShowCollation,
162            Statement::Use { .. } => EntrySqlType::Use,
163            Statement::StartTransaction { .. } => EntrySqlType::StartTransaction,
164            Statement::SetTransaction { .. } => EntrySqlType::SetTransaction,
165            Statement::Commit { .. } => EntrySqlType::Commit,
166            Statement::Rollback { .. } => EntrySqlType::Rollback,
167            Statement::CreateSchema { .. } => EntrySqlType::CreateSchema,
168            Statement::CreateDatabase { .. } => EntrySqlType::CreateDatabase,
169            Statement::Grant { .. } => EntrySqlType::Grant,
170            Statement::Revoke { .. } => EntrySqlType::Revoke,
171            Statement::Kill { .. } => EntrySqlType::Kill,
172            Statement::ExplainTable { .. } => EntrySqlType::ExplainTable,
173            Statement::Explain { .. } => EntrySqlType::Explain,
174            Statement::Savepoint { .. } => EntrySqlType::Savepoint,
175            Statement::LockTables { .. } => EntrySqlType::LockTables,
176            Statement::UnlockTables { .. } => EntrySqlType::LockTables,
177            Statement::Flush { .. } => EntrySqlType::Flush,
178            _ => EntrySqlType::Unknown,
179        }
180    }
181}
182
183impl From<Statement> for EntrySqlStatement {
184    fn from(statement: Statement) -> Self {
185        EntrySqlStatement {
186            statement,
187            context: None,
188        }
189    }
190}
191
192/// Database objects called from within a query
193#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
194pub struct EntrySqlStatementObject {
195    /// optional schema name
196    pub schema_name: Option<Bytes>,
197    /// object name (i.e. table name)
198    pub object_name: Bytes,
199}
200
201impl EntrySqlStatementObject {
202    /// returns the optional schema name of object
203    pub fn schema_name(&self) -> Option<Cow<str>> {
204        if let Some(v) = &self.schema_name {
205            Some(String::from_utf8_lossy(v.as_ref()))
206        } else {
207            None
208        }
209    }
210
211    /// returns the optional schema name of object as bytes
212    pub fn schema_name_bytes(&self) -> Option<Bytes> {
213        self.schema_name.clone()
214    }
215
216    /// returns the object name of object
217    pub fn object_name(&self) -> Cow<str> {
218        String::from_utf8_lossy(self.object_name.as_ref())
219    }
220
221    /// returns the object name of object as bytes
222    pub fn object_name_bytes(&self) -> Bytes {
223        self.object_name.clone()
224    }
225
226    /// full object name \[schema.\]object in Bytes
227    pub fn full_object_name_bytes(&self) -> Bytes {
228        let mut s = if let Some(n) = self.schema_name.clone() {
229            let mut s = BytesMut::from(n.as_ref());
230            s.put_slice(b".");
231            s
232        } else {
233            BytesMut::new()
234        };
235
236        s.put_slice(self.object_name.as_ref());
237        s.freeze()
238    }
239
240    /// full object name \[schema.\]object as a CoW
241    pub fn full_object_name(&self) -> Cow<'_, str> {
242        String::from_utf8_lossy(self.full_object_name_bytes().as_ref())
243            .to_string()
244            .into()
245    }
246}
247
248/// Types of possible statements parsed from the log:
249/// * SqlStatement: parseable statement with a proper SQL AST
250/// * AdminCommand: commands passed from the mysql cli/admin tools
251/// * InvalidStatement: statement which isn't currently parseable as plain-text
252#[derive(Clone, Debug, PartialEq)]
253pub enum EntryStatement {
254    /// AdminCommand: commands passed from the mysql cli/admin tools
255    AdminCommand(EntryAdminCommand),
256    /// SqlStatement: parseable statement with a proper SQL AST
257    SqlStatement(EntrySqlStatement),
258    /// InvalidStatement: statement which isn't currently parseable by `sql-parser` crate
259    InvalidStatement(String),
260}
261
262impl EntryStatement {
263    /// returns the `EntrySqlStatement` objects associated with this statement, if known
264    pub fn objects(&self) -> Option<Vec<EntrySqlStatementObject>> {
265        match self {
266            Self::SqlStatement(s) => Some(s.objects().clone()),
267            _ => None,
268        }
269    }
270
271    /// returns the `EntrySqlType` associated with this statement if known
272    pub fn sql_type(&self) -> Option<EntrySqlType> {
273        match self {
274            Self::SqlStatement(s) => Some(s.sql_type().clone()),
275            _ => None,
276        }
277    }
278
279    /// returns the `SqlStatementContext` associated with this statement
280    pub fn sql_context(&self) -> Option<SqlStatementContext> {
281        match self {
282            Self::SqlStatement(s) => s.sql_context().clone(),
283            _ => None,
284        }
285    }
286}
287
288/// The SQL statement type of the EntrySqlStatement.
289///
290/// NOTE: this is a MySQL specific sub-set of the entries in `sql_parser::ast::Statement`. This is
291/// a simpler enum to match against and displays as the start of the SQL command.
292#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
293pub enum EntrySqlType {
294    /// SELECT
295    Query,
296    /// INSERT
297    Insert,
298    /// UPDATE
299    Update,
300    /// DELETE
301    Delete,
302    /// CREATE TABLE
303    CreateTable,
304    /// CREATE INDEX
305    CreateIndex,
306    /// CREATE VIEW
307    CreateView,
308    /// ALTER TABLE
309    AlterTable,
310    /// ALTER INDEX
311    AlterIndex,
312    /// DROP TABLE
313    Drop,
314    /// DROP FUNCTION
315    DropFunction,
316    /// SET VARIABLE
317    SetVariable,
318    /// SET NAMES
319    SetNames,
320    /// SET NAMES DEFAULT
321    SetNamesDefault,
322    /// SHOW VARIABLE
323    ShowVariable,
324    /// SHOW VARIABLES
325    ShowVariables,
326    /// SHOW CREATE TABLE
327    ShowCreate,
328    /// SHOW COLUMNS
329    ShowColumns,
330    /// SHOW TABLES
331    ShowTables,
332    /// SHOW COLLATION
333    ShowCollation,
334    /// USE
335    Use,
336    /// BEGIN TRANSACTION
337    StartTransaction,
338    /// SET TRANSACTION
339    SetTransaction,
340    /// COMMIT TRANSACTION
341    Commit,
342    /// ROLLBACK TRANSACTION
343    Rollback,
344    /// CREATE SCHEMA
345    CreateSchema,
346    /// CREATE DATABASE
347    CreateDatabase,
348    /// GRANT
349    Grant,
350    /// REVOKE
351    Revoke,
352    /// KILL
353    Kill,
354    /// EXPLAIN TABLE
355    ExplainTable,
356    /// EXPLAIN
357    Explain,
358    /// SAVEPOINT
359    Savepoint,
360    /// LOCK TABLES
361    LockTables,
362    /// UNLOCK TABLES
363    UnlockTables,
364    /// FLUSH
365    Flush,
366    /// Unable to identy if the type of statement
367    Unknown,
368}
369
370impl Display for EntrySqlType {
371    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372        let out = match self {
373            Self::Query => "SELECT",
374            Self::Insert => "INSERT",
375            Self::Update => "UPDATE",
376            Self::Delete => "DELETE",
377            Self::CreateTable => "CREATE TABLE",
378            Self::CreateIndex => "CREATE INDEX",
379            Self::CreateView => "CREATE VIEW",
380            Self::AlterTable => "ALTER TABLE",
381            Self::AlterIndex => "ALTER INDEX",
382            Self::Drop => "DROP TABLE",
383            Self::DropFunction => "DROP FUNCTION",
384            Self::SetVariable => "SET VARIABLE",
385            Self::SetNames => "SET NAMES",
386            Self::SetNamesDefault => "SET NAMES DEFAULT",
387            Self::ShowVariable => "SHOW VARIABLE",
388            Self::ShowVariables => "SHOW VARIABLES",
389            Self::ShowCreate => "SHOW CREATE TABLE",
390            Self::ShowColumns => "SHOW COLUMNS",
391            Self::ShowTables => "SHOW TABLES",
392            Self::ShowCollation => "SHOW COLLATION",
393            Self::Use => "USE",
394            Self::StartTransaction => "BEGIN TRANSACTION",
395            Self::SetTransaction => "SET TRANSACTION",
396            Self::Commit => "COMMIT TRANSACTION",
397            Self::Rollback => "ROLLBACK TRANSACTION",
398            Self::CreateSchema => "CREATE SCHEMA",
399            Self::CreateDatabase => "CREATE DATABASE",
400            Self::Grant => "GRANT",
401            Self::Revoke => "REVOKE",
402            Self::Kill => "KILL",
403            Self::ExplainTable => "EXPLAIN TABLE",
404            Self::Explain => "EXPLAIN",
405            Self::Savepoint => "SAVEPOINT",
406            Self::LockTables => "LOCK TABLES",
407            Self::UnlockTables => "UNLOCK TABLES",
408            Self::Flush => "FLUSH",
409            Self::Unknown => "NULL",
410        };
411
412        write!(f, "{}", out)
413    }
414}
415
416/// struct containing information about the connection where the query originated
417#[derive(Clone, Debug, PartialEq)]
418pub struct EntrySession {
419    /// user name of the connected user who ran the query
420    pub user_name: Bytes,
421    /// system user name of the connected user who ran the query
422    pub sys_user_name: Bytes,
423    /// hostname of the connected user who ran the query
424    pub host_name: Option<Bytes>,
425    /// ip address of the connected user who ran the query
426    pub ip_address: Option<Bytes>,
427    /// the thread id that the session was conntected on
428    pub thread_id: u32,
429}
430
431impl From<SessionLine> for EntrySession {
432    fn from(line: SessionLine) -> Self {
433        Self {
434            user_name: line.user,
435            sys_user_name: line.sys_user,
436            host_name: line.host,
437            ip_address: line.ip_address,
438            thread_id: line.thread_id,
439        }
440    }
441}
442
443impl EntrySession {
444    /// returns the mysql user name that requested the command
445    pub fn user_name(&self) -> Cow<str> {
446        String::from_utf8_lossy(&self.user_name)
447    }
448
449    /// returns the mysql user name that requested the command
450    pub fn user_name_bytes(&self) -> Bytes {
451        self.user_name.clone()
452    }
453
454    /// returns the system user name that requested the command
455    pub fn sys_user_name(&self) -> Cow<str> {
456        String::from_utf8_lossy(&self.sys_user_name)
457    }
458
459    /// returns the system user name that requested the command
460    pub fn sys_user_name_bytes(&self) -> Bytes {
461        self.sys_user_name.clone()
462    }
463
464    /// returns the host name which requested the command
465    pub fn host_name(&self) -> Option<Cow<str>> {
466        if let Some(v) = &self.host_name {
467            Some(String::from_utf8_lossy(v.as_ref()))
468        } else {
469            None
470        }
471    }
472
473    /// returns the host name which requested the command
474    pub fn host_name_bytes(&self) -> Option<Bytes> {
475        self.host_name.clone()
476    }
477
478    /// returns the ip address which requested the command
479    pub fn ip_address(&self) -> Option<Cow<'_, str>> {
480        if let Some(v) = &self.ip_address {
481            Some(String::from_utf8_lossy(v.as_ref()))
482        } else {
483            None
484        }
485    }
486
487    /// returns the ip address which requested the command
488    pub fn ip_address_bytes(&self) -> Option<Bytes> {
489        self.ip_address.clone()
490    }
491
492    /// returns the the thread id of the which requested the command
493    pub fn thread_id(&self) -> u32 {
494        self.thread_id
495    }
496}
497
498/// struct with information about the Entry's SQL query
499#[derive(Clone, Debug, PartialEq)]
500pub struct EntrySqlAttributes {
501    /// the sql for this entry, possibly with values replaced by parameters
502    pub sql: Bytes,
503    /// the `EntryStatement for this entry
504    pub statement: EntryStatement,
505}
506
507impl EntrySqlAttributes {
508    /// returns the sql statement as bytes
509    pub fn sql_bytes(&self) -> Bytes {
510        self.sql.clone()
511    }
512
513    /// returns the ip address which requested the command
514    pub fn sql(&self) -> Cow<'_, str> {
515        String::from_utf8_lossy(self.sql.as_ref())
516    }
517
518    /// returns the ip address which requested the command
519    pub fn sql_type(&self) -> Option<EntrySqlType> {
520        self.statement.sql_type()
521    }
522
523    /// returns entry sql statment objects
524    pub fn objects(&self) -> Option<Vec<EntrySqlStatementObject>> {
525        self.statement.objects()
526    }
527
528    /// returns the entry's `EntryStatement`
529    pub fn statement(&self) -> &EntryStatement {
530        &self.statement
531    }
532}
533
534/// struct containing details of how long the query took
535#[derive(Clone, Copy, Debug, PartialEq)]
536pub struct EntryCall {
537    /// time recorded for the log entry
538    pub log_time: DateTime,
539    /// effective time of NOW() during the query run
540    pub set_timestamp: u32,
541}
542
543impl EntryCall {
544    /// create a new instance of EntryCall
545    pub fn new(log_time: DateTime, set_timestamp: u32) -> Self {
546        Self {
547            log_time,
548            set_timestamp,
549        }
550    }
551
552    /// returns the entry time as an `DateTime`
553    pub fn log_time(&self) -> DateTime {
554        self.log_time
555    }
556
557    /// returns the time stamp set at the beginning of each entry
558    pub fn set_timestamp(&self) -> u32 {
559        self.set_timestamp
560    }
561}
562
563/// struct with stats on how long a query took and number of rows examined
564#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
565pub struct EntryStats {
566    /// how long the query took
567    pub query_time: f64,
568    /// how long the query held locks
569    pub lock_time: f64,
570    /// how many rows were returned to the client
571    pub rows_sent: u32,
572    /// how many rows were scanned to find result
573    pub rows_examined: u32,
574}
575
576impl EntryStats {
577    /// returns how long the query took to run
578    pub fn query_time(&self) -> f64 {
579        self.query_time
580    }
581
582    /// returns how long it took to lock
583    pub fn lock_time(&self) -> f64 {
584        self.lock_time
585    }
586
587    /// returns number of rows returned when query was executed
588    pub fn rows_sent(&self) -> u32 {
589        self.rows_sent
590    }
591
592    /// returns how many rows where examined to execute the query
593    pub fn rows_examined(&self) -> u32 {
594        self.rows_examined
595    }
596}
597
598impl From<StatsLine> for EntryStats {
599    fn from(line: StatsLine) -> Self {
600        Self {
601            query_time: line.query_time,
602            lock_time: line.lock_time,
603            rows_sent: line.rows_sent,
604            rows_examined: line.rows_examined,
605        }
606    }
607}
608
609/// Values parsed from a query comment, these values are currently overly-specific
610#[derive(Clone, Debug, PartialEq)]
611pub struct EntryContext {
612    /// optional request id, such as an SSRID
613    pub request_id: Option<Bytes>,
614    /// optional caller
615    pub caller: Option<Bytes>,
616    /// optional function/method
617    pub function: Option<Bytes>,
618    /// optional line number
619    pub line: Option<u32>,
620}
621
622impl EntryContext {
623    /// returns the request_id from query comment
624    pub fn request_id(&self) -> Option<Cow<str>> {
625        if let Some(v) = &self.request_id {
626            Some(String::from_utf8_lossy(v.as_ref()))
627        } else {
628            None
629        }
630    }
631
632    /// returns the request_id from query comment
633    pub fn request_id_bytes(&self) -> Option<Bytes> {
634        self.request_id.clone()
635    }
636
637    /// returns the caller from query comment
638    pub fn caller(&self) -> Option<Cow<str>> {
639        if let Some(v) = &self.caller {
640            Some(String::from_utf8_lossy(v.as_ref()))
641        } else {
642            None
643        }
644    }
645
646    /// returns the caller from query comment
647    pub fn caller_bytes(&self) -> Option<Bytes> {
648        self.caller.clone()
649    }
650
651    /// returns the function from query comment
652    pub fn function(&self) -> Option<Cow<str>> {
653        if let Some(v) = &self.function {
654            Some(String::from_utf8_lossy(v.as_ref()))
655        } else {
656            None
657        }
658    }
659
660    /// returns the function from query comment
661    pub fn function_bytes(&self) -> Option<Bytes> {
662        self.function.clone()
663    }
664
665    /// returns the line from query comment
666    pub fn line(&self) -> Option<u32> {
667        self.line
668    }
669}