Skip to main content

clickhouse_native_client/column/
ipv4.rs

1use super::{
2    Column,
3    ColumnRef,
4};
5use crate::{
6    types::Type,
7    Error,
8    Result,
9};
10use bytes::BytesMut;
11use std::sync::Arc;
12
13/// Column for IPv4 addresses (stored as UInt32)
14///
15/// **C++ Implementation Pattern:**
16/// Uses delegation to `ColumnUInt32` for storage, matching the C++
17/// clickhouse-cpp reference implementation's `std::shared_ptr<ColumnUInt32>
18/// data_` pattern.
19pub struct ColumnIpv4 {
20    type_: Type,
21    data: Arc<super::ColumnUInt32>, /* Delegates to ColumnUInt32, matches
22                                     * C++ pattern */
23}
24
25impl ColumnIpv4 {
26    /// Create a new empty IPv4 column.
27    pub fn new(type_: Type) -> Self {
28        Self { type_, data: Arc::new(super::ColumnUInt32::new()) }
29    }
30
31    /// Set the column data from a vector of raw `u32` IPv4 addresses.
32    pub fn with_data(mut self, data: Vec<u32>) -> Self {
33        self.data =
34            Arc::new(super::ColumnUInt32::from_vec(Type::uint32(), data));
35        self
36    }
37
38    /// Append an IPv4 address parsed from a dotted-decimal string like
39    /// `"192.168.1.1"`.
40    ///
41    /// # Errors
42    ///
43    /// Returns an error if the string does not contain exactly four
44    /// dot-separated octets or an octet is not a valid `u8`.
45    pub fn append_from_string(&mut self, s: &str) -> Result<()> {
46        let parts: Vec<&str> = s.split('.').collect();
47        if parts.len() != 4 {
48            return Err(Error::Protocol(format!(
49                "Invalid IPv4 format: {}",
50                s
51            )));
52        }
53
54        let mut ip: u32 = 0;
55        for (i, part) in parts.iter().enumerate() {
56            let octet = part.parse::<u8>().map_err(|e| {
57                Error::Protocol(format!("Invalid IPv4 octet: {}", e))
58            })?;
59            // Network byte order: most significant byte first
60            ip |= (octet as u32) << (24 - i * 8);
61        }
62
63        self.append(ip);
64        Ok(())
65    }
66
67    /// Append IPv4 from u32 value
68    pub fn append(&mut self, value: u32) {
69        Arc::get_mut(&mut self.data)
70            .expect("Cannot append to shared column")
71            .append(value);
72    }
73
74    /// Get IPv4 at index as u32
75    pub fn at(&self, index: usize) -> u32 {
76        self.data.at(index)
77    }
78
79    /// Format IPv4 at index as dotted decimal string
80    pub fn as_string(&self, index: usize) -> String {
81        let ip = self.data.at(index);
82        format!(
83            "{}.{}.{}.{}",
84            (ip >> 24) & 0xFF,
85            (ip >> 16) & 0xFF,
86            (ip >> 8) & 0xFF,
87            ip & 0xFF
88        )
89    }
90
91    /// Returns the number of values in this column.
92    pub fn len(&self) -> usize {
93        self.data.len()
94    }
95
96    /// Returns `true` if the column contains no values.
97    pub fn is_empty(&self) -> bool {
98        self.data.is_empty()
99    }
100
101    /// Get reference to underlying data column (for advanced use).
102    pub fn data(&self) -> &super::ColumnUInt32 {
103        &self.data
104    }
105}
106
107impl Column for ColumnIpv4 {
108    fn column_type(&self) -> &Type {
109        &self.type_
110    }
111
112    fn size(&self) -> usize {
113        self.data.size()
114    }
115
116    fn clear(&mut self) {
117        Arc::get_mut(&mut self.data)
118            .expect("Cannot clear shared column")
119            .clear();
120    }
121
122    fn reserve(&mut self, new_cap: usize) {
123        Arc::get_mut(&mut self.data)
124            .expect("Cannot reserve on shared column")
125            .reserve(new_cap);
126    }
127
128    fn append_column(&mut self, other: ColumnRef) -> Result<()> {
129        let other =
130            other.as_any().downcast_ref::<ColumnIpv4>().ok_or_else(|| {
131                Error::TypeMismatch {
132                    expected: self.type_.name(),
133                    actual: other.column_type().name(),
134                }
135            })?;
136
137        // Delegate to underlying ColumnUInt32
138        Arc::get_mut(&mut self.data)
139            .expect("Cannot append to shared column")
140            .append_column(other.data.clone() as ColumnRef)?;
141        Ok(())
142    }
143
144    fn load_from_buffer(
145        &mut self,
146        buffer: &mut &[u8],
147        rows: usize,
148    ) -> Result<()> {
149        // Delegate to ColumnUInt32 which has bulk copy optimization
150        Arc::get_mut(&mut self.data)
151            .expect("Cannot load into shared column")
152            .load_from_buffer(buffer, rows)
153    }
154
155    fn save_to_buffer(&self, buffer: &mut BytesMut) -> Result<()> {
156        // Delegate to ColumnUInt32 which has bulk copy optimization
157        self.data.save_to_buffer(buffer)
158    }
159
160    fn clone_empty(&self) -> ColumnRef {
161        Arc::new(ColumnIpv4::new(self.type_.clone()))
162    }
163
164    fn slice(&self, begin: usize, len: usize) -> Result<ColumnRef> {
165        // Delegate to underlying column and wrap result
166        let sliced_data = self.data.slice(begin, len)?;
167
168        Ok(Arc::new(ColumnIpv4 {
169            type_: self.type_.clone(),
170            data: sliced_data
171                .as_any()
172                .downcast_ref::<super::ColumnUInt32>()
173                .map(|col| {
174                    // Create new Arc from the sliced data
175                    Arc::new(super::ColumnUInt32::from_vec(
176                        Type::uint32(),
177                        col.data().to_vec(),
178                    ))
179                })
180                .expect("Slice should return ColumnUInt32"),
181        }))
182    }
183
184    fn as_any(&self) -> &dyn std::any::Any {
185        self
186    }
187
188    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
189        self
190    }
191}
192
193#[cfg(test)]
194#[cfg_attr(coverage_nightly, coverage(off))]
195mod tests {
196    use super::*;
197
198    #[test]
199    fn test_ipv4_from_string() {
200        let mut col = ColumnIpv4::new(Type::ipv4());
201        col.append_from_string("192.168.1.1").unwrap();
202        col.append_from_string("10.0.0.1").unwrap();
203        col.append_from_string("0.0.0.0").unwrap();
204
205        assert_eq!(col.len(), 3);
206        assert_eq!(col.as_string(0), "192.168.1.1");
207        assert_eq!(col.as_string(1), "10.0.0.1");
208        assert_eq!(col.as_string(2), "0.0.0.0");
209    }
210
211    #[test]
212    fn test_ipv4_from_u32() {
213        let mut col = ColumnIpv4::new(Type::ipv4());
214        col.append(0xC0A80101); // 192.168.1.1
215        col.append(0x0A000001); // 10.0.0.1
216        col.append(0); // 0.0.0.0
217
218        assert_eq!(col.len(), 3);
219        assert_eq!(col.at(0), 0xC0A80101);
220        assert_eq!(col.at(1), 0x0A000001);
221        assert_eq!(col.at(2), 0);
222    }
223
224    #[test]
225    fn test_ipv4_edge_cases() {
226        let mut col = ColumnIpv4::new(Type::ipv4());
227        col.append_from_string("255.255.255.255").unwrap();
228        col.append_from_string("127.0.0.1").unwrap();
229
230        assert_eq!(col.as_string(0), "255.255.255.255");
231        assert_eq!(col.as_string(1), "127.0.0.1");
232    }
233}