rs_postgres_stat2otel/
query.rs1use crate::{evt::Event, multi::RawMulti, single::RawSingle};
4
5#[derive(serde::Deserialize)]
7pub struct CustomQuery {
8 multi: Option<Vec<RawMulti>>,
9 single: Option<Vec<RawSingle>>,
10}
11
12impl CustomQuery {
13 pub fn take_single(&mut self) -> Option<Vec<RawSingle>> {
15 self.single.take()
16 }
17
18 pub fn take_multi(&mut self) -> Option<Vec<RawMulti>> {
20 self.multi.take()
21 }
22}
23
24#[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#[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}