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#[derive(Clone, Debug, PartialEq)]
12pub struct Entry {
13    pub call: EntryCall,
15    pub session: EntrySession,
17    pub stats: EntryStats,
19    pub sql_attributes: EntrySqlAttributes,
21}
22
23impl Entry {
24    pub fn log_time(&self) -> DateTime {
26        self.call.log_time
27    }
28
29    pub fn user_name(&self) -> Cow<str> {
31        String::from_utf8_lossy(&self.session.user_name)
32    }
33
34    pub fn user_name_bytes(&self) -> Bytes {
36        self.session.user_name.clone()
37    }
38
39    pub fn sys_user_name(&self) -> Cow<str> {
41        String::from_utf8_lossy(&self.session.sys_user_name)
42    }
43
44    pub fn sys_user_name_bytes(&self) -> Bytes {
46        self.session.sys_user_name.clone()
47    }
48
49    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    pub fn host_name_bytes(&self) -> Option<Bytes> {
60        self.session.host_name_bytes()
61    }
62
63    pub fn ip_address(&self) -> Option<Cow<'_, str>> {
65        self.session.ip_address()
66    }
67
68    pub fn ip_address_bytes(&self) -> Option<Bytes> {
70        self.session.ip_address_bytes()
71    }
72
73    pub fn thread_id(&self) -> u32 {
75        self.session.thread_id()
76    }
77
78    pub fn stats(&self) -> &EntryStats {
80        &self.stats
81    }
82
83    pub fn query_time(&self) -> f64 {
85        self.stats.query_time()
86    }
87
88    pub fn lock_time(&self) -> f64 {
90        self.stats.lock_time()
91    }
92
93    pub fn rows_sent(&self) -> u32 {
95        self.stats.rows_sent()
96    }
97
98    pub fn rows_examined(&self) -> u32 {
100        self.stats.rows_examined()
101    }
102}
103
104#[derive(Clone, Debug, PartialEq)]
105pub struct EntrySqlStatement {
107    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#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
194pub struct EntrySqlStatementObject {
195    pub schema_name: Option<Bytes>,
197    pub object_name: Bytes,
199}
200
201impl EntrySqlStatementObject {
202    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    pub fn schema_name_bytes(&self) -> Option<Bytes> {
213        self.schema_name.clone()
214    }
215
216    pub fn object_name(&self) -> Cow<str> {
218        String::from_utf8_lossy(self.object_name.as_ref())
219    }
220
221    pub fn object_name_bytes(&self) -> Bytes {
223        self.object_name.clone()
224    }
225
226    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    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#[derive(Clone, Debug, PartialEq)]
253pub enum EntryStatement {
254    AdminCommand(EntryAdminCommand),
256    SqlStatement(EntrySqlStatement),
258    InvalidStatement(String),
260}
261
262impl EntryStatement {
263    pub fn objects(&self) -> Option<Vec<EntrySqlStatementObject>> {
265        match self {
266            Self::SqlStatement(s) => Some(s.objects().clone()),
267            _ => None,
268        }
269    }
270
271    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    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#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
293pub enum EntrySqlType {
294    Query,
296    Insert,
298    Update,
300    Delete,
302    CreateTable,
304    CreateIndex,
306    CreateView,
308    AlterTable,
310    AlterIndex,
312    Drop,
314    DropFunction,
316    SetVariable,
318    SetNames,
320    SetNamesDefault,
322    ShowVariable,
324    ShowVariables,
326    ShowCreate,
328    ShowColumns,
330    ShowTables,
332    ShowCollation,
334    Use,
336    StartTransaction,
338    SetTransaction,
340    Commit,
342    Rollback,
344    CreateSchema,
346    CreateDatabase,
348    Grant,
350    Revoke,
352    Kill,
354    ExplainTable,
356    Explain,
358    Savepoint,
360    LockTables,
362    UnlockTables,
364    Flush,
366    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#[derive(Clone, Debug, PartialEq)]
418pub struct EntrySession {
419    pub user_name: Bytes,
421    pub sys_user_name: Bytes,
423    pub host_name: Option<Bytes>,
425    pub ip_address: Option<Bytes>,
427    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    pub fn user_name(&self) -> Cow<str> {
446        String::from_utf8_lossy(&self.user_name)
447    }
448
449    pub fn user_name_bytes(&self) -> Bytes {
451        self.user_name.clone()
452    }
453
454    pub fn sys_user_name(&self) -> Cow<str> {
456        String::from_utf8_lossy(&self.sys_user_name)
457    }
458
459    pub fn sys_user_name_bytes(&self) -> Bytes {
461        self.sys_user_name.clone()
462    }
463
464    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    pub fn host_name_bytes(&self) -> Option<Bytes> {
475        self.host_name.clone()
476    }
477
478    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    pub fn ip_address_bytes(&self) -> Option<Bytes> {
489        self.ip_address.clone()
490    }
491
492    pub fn thread_id(&self) -> u32 {
494        self.thread_id
495    }
496}
497
498#[derive(Clone, Debug, PartialEq)]
500pub struct EntrySqlAttributes {
501    pub sql: Bytes,
503    pub statement: EntryStatement,
505}
506
507impl EntrySqlAttributes {
508    pub fn sql_bytes(&self) -> Bytes {
510        self.sql.clone()
511    }
512
513    pub fn sql(&self) -> Cow<'_, str> {
515        String::from_utf8_lossy(self.sql.as_ref())
516    }
517
518    pub fn sql_type(&self) -> Option<EntrySqlType> {
520        self.statement.sql_type()
521    }
522
523    pub fn objects(&self) -> Option<Vec<EntrySqlStatementObject>> {
525        self.statement.objects()
526    }
527
528    pub fn statement(&self) -> &EntryStatement {
530        &self.statement
531    }
532}
533
534#[derive(Clone, Copy, Debug, PartialEq)]
536pub struct EntryCall {
537    pub log_time: DateTime,
539    pub set_timestamp: u32,
541}
542
543impl EntryCall {
544    pub fn new(log_time: DateTime, set_timestamp: u32) -> Self {
546        Self {
547            log_time,
548            set_timestamp,
549        }
550    }
551
552    pub fn log_time(&self) -> DateTime {
554        self.log_time
555    }
556
557    pub fn set_timestamp(&self) -> u32 {
559        self.set_timestamp
560    }
561}
562
563#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
565pub struct EntryStats {
566    pub query_time: f64,
568    pub lock_time: f64,
570    pub rows_sent: u32,
572    pub rows_examined: u32,
574}
575
576impl EntryStats {
577    pub fn query_time(&self) -> f64 {
579        self.query_time
580    }
581
582    pub fn lock_time(&self) -> f64 {
584        self.lock_time
585    }
586
587    pub fn rows_sent(&self) -> u32 {
589        self.rows_sent
590    }
591
592    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#[derive(Clone, Debug, PartialEq)]
611pub struct EntryContext {
612    pub request_id: Option<Bytes>,
614    pub caller: Option<Bytes>,
616    pub function: Option<Bytes>,
618    pub line: Option<u32>,
620}
621
622impl EntryContext {
623    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    pub fn request_id_bytes(&self) -> Option<Bytes> {
634        self.request_id.clone()
635    }
636
637    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    pub fn caller_bytes(&self) -> Option<Bytes> {
648        self.caller.clone()
649    }
650
651    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    pub fn function_bytes(&self) -> Option<Bytes> {
662        self.function.clone()
663    }
664
665    pub fn line(&self) -> Option<u32> {
667        self.line
668    }
669}