rs_postgres_stat2otel/
query.rs

1//! Metrics config collection.
2
3use crate::{evt::Event, multi::RawMulti, single::RawSingle};
4
5/// Metrics config collection(Single row query collection/Multi rows query collection).
6#[derive(serde::Deserialize)]
7pub struct CustomQuery {
8    multi: Option<Vec<RawMulti>>,
9    single: Option<Vec<RawSingle>>,
10}
11
12impl CustomQuery {
13    /// Takes `RawSingle` collection.
14    pub fn take_single(&mut self) -> Option<Vec<RawSingle>> {
15        self.single.take()
16    }
17
18    /// Takes `RawMulti` collection.
19    pub fn take_multi(&mut self) -> Option<Vec<RawMulti>> {
20        self.multi.take()
21    }
22}
23
24/// Parses custom query config bytes to create `CustomQuery` using toml parser.
25#[cfg(feature = "config_toml")]
26pub fn from_toml_slice(b: &[u8]) -> Result<CustomQuery, Event> {
27    toml::de::from_slice(b)
28        .map_err(|e| Event::InvalidConfig(format!("Unable to parse toml bytes: {}", e)))
29}
30
31/// Parses custom query config bytes to create `CustomQuery` using default parser.
32#[cfg(feature = "config_toml")]
33pub fn from_slice_default(b: &[u8]) -> Result<CustomQuery, Event> {
34    from_toml_slice(b)
35}
36
37#[cfg(test)]
38mod test_query {
39
40    #[cfg(feature = "config_toml")]
41    mod test_toml {
42        use crate::{
43            gauge::{Gauge, GaugeType},
44            label::Label,
45            multi::{Multi, RawMulti},
46            query::{from_toml_slice, CustomQuery},
47            single::{RawSingle, Single},
48        };
49
50        #[test]
51        fn test_empty() {
52            let c: CustomQuery = from_toml_slice(&[]).unwrap();
53            assert!(c.multi.is_none());
54            assert!(c.single.is_none());
55        }
56
57        #[test]
58        fn test_single() {
59            let single: &str = r#"
60                [[single]]
61                
62                query = '''
63                    SELECT
64                        wal_records::BIGINT,
65                        wal_fpi::BIGINT,
66                        wal_bytes::BIGINT,
67                        wal_buffers_full::BIGINT,
68                        wal_write::BIGINT,
69                        wal_sync::BIGINT,
70                        wal_write_time::FLOAT8,
71                        wal_sync_time::FLOAT8,
72                        EXTRACT(EPOCH FROM stats_reset)::FLOAT8 AS stats_reset
73                    FROM pg_stat_wal
74                    LIMIT 1
75                '''
76                
77                [[single.gauge]]
78                name = "wal_records"
79                gauge_type = "i64"
80                description = "Total number of WAL records generated."
81                unit = ""
82                
83                [[single.gauge]]
84                name = "wal_fpi"
85                gauge_type = "i64"
86                description = "Total number of WAL full page images generated."
87                unit = ""
88            "#;
89
90            let mut c: CustomQuery = from_toml_slice(single.as_bytes()).unwrap();
91            assert!(c.single.is_some());
92            assert!(c.multi.is_none());
93
94            let o: Option<Vec<RawSingle>> = c.take_single();
95            assert!(o.is_some());
96            let vs: Vec<RawSingle> = o.unwrap();
97            assert_eq!(vs.len(), 1);
98            let rs: RawSingle = vs.into_iter().next().unwrap();
99
100            let mut s: Single = Single::try_from(rs).unwrap();
101
102            let ovl: Option<Vec<Label>> = s.take_label();
103            assert!(ovl.is_none());
104
105            assert!(0 < s.as_query().len());
106
107            let g: &[Gauge] = s.as_gauge();
108            assert_eq!(g.len(), 2);
109            let wal_records: &Gauge = &g[0];
110            let wal_fpi: &Gauge = &g[1];
111
112            assert_eq!(wal_records.as_name(), "wal_records");
113            assert_eq!(wal_records.as_type(), GaugeType::Integer);
114            assert!(0 < wal_records.as_desc().len());
115            assert_eq!(wal_records.as_unit(), "");
116
117            assert_eq!(wal_fpi.as_name(), "wal_fpi");
118            assert_eq!(wal_fpi.as_type(), GaugeType::Integer);
119            assert!(0 < wal_fpi.as_desc().len());
120            assert_eq!(wal_fpi.as_unit(), "");
121        }
122
123        #[test]
124        fn test_multi() {
125            let multi: &str = r#"
126                [[multi]]
127                
128                query = '''
129                    SELECT
130                        datname::TEXT,
131                        application_name::TEXT,
132                        EXTRACT(SECOND FROM (CLOCK_TIMESTAMP() - query_start))::FLOAT8 AS elapsed,
133                        wait_event_type::TEXT,
134                        wait_event::TEXT,
135                        state::TEXT,
136                        backend_type::TEXT
137                    FROM pg_stat_activity
138                '''
139                
140                [[multi.label]]
141                name = "datname"
142                description = "Name of this database."
143                
144                [[multi.label]]
145                name = "application_name"
146                description = "Name of the application that is connected to this backend."
147                
148                [[multi.label]]
149                name = "wait_event_type"
150                description = "The type of event for which the backend is waiting."
151                
152                [[multi.gauge]]
153                name = "elapsed"
154                gauge_type = "f64"
155                description = "Time elapsed for this query."
156                unit = "seconds"
157            "#;
158            let mut c: CustomQuery = from_toml_slice(multi.as_bytes()).unwrap();
159            assert!(c.take_single().is_none());
160
161            let ovrm: Option<Vec<RawMulti>> = c.take_multi();
162            assert!(ovrm.is_some());
163
164            let vrm: Vec<RawMulti> = ovrm.unwrap();
165            assert_eq!(vrm.len(), 1);
166
167            let rm: RawMulti = vrm.into_iter().next().unwrap();
168
169            let m: Multi = Multi::try_from(rm).unwrap();
170
171            assert!(0 < m.as_query().len());
172            let sl: &[Label] = m.as_label();
173
174            let datname: &Label = &sl[0];
175            let application_name: &Label = &sl[1];
176            let wait_event_type: &Label = &sl[2];
177
178            assert_eq!(datname.as_name(), "datname");
179            assert_eq!(application_name.as_name(), "application_name");
180            assert_eq!(wait_event_type.as_name(), "wait_event_type");
181
182            assert!(0 < datname.as_desc().len());
183            assert!(0 < application_name.as_desc().len());
184            assert!(0 < wait_event_type.as_desc().len());
185
186            let sg: &[Gauge] = m.as_gauge();
187            assert_eq!(sg.len(), 1);
188            let g: &Gauge = &sg[0];
189
190            assert_eq!(g.as_name(), "elapsed");
191            assert_eq!(g.as_type(), GaugeType::Float);
192            assert_eq!(g.as_unit(), "seconds");
193        }
194    }
195}