Skip to main content

voltdb_client_rust/
table.rs

1use std::collections::HashMap;
2use std::io::Read;
3
4use bigdecimal::BigDecimal;
5use bigdecimal::num_bigint::BigInt;
6use bytebuffer::ByteBuffer;
7use chrono::{DateTime, TimeZone, Utc};
8
9use crate::encode::*;
10use crate::response::ResponseStatus::Success;
11use crate::response::VoltResponseInfo;
12
13const MIN_INT8: i8 = -1 << 7;
14
15#[derive(Debug, Clone, PartialEq)]
16pub struct Column {
17    pub header_name: String,
18    pub header_type: i8,
19}
20
21#[derive(Debug)]
22#[allow(dead_code)]
23pub struct VoltTable {
24    info: VoltResponseInfo,
25    column_count: i16,
26    info_bytes: ByteBuffer,
27    column_info_bytes: ByteBuffer,
28    columns: Vec<Column>,
29    num_rows: i32,
30    rows: Vec<ByteBuffer>,
31    row_index: i32,
32    cn_to_ci: HashMap<String, i16>,
33    column_offsets: Vec<i32>,
34    header_size: i32,
35    total_size: i32,
36}
37
38impl VoltTable {
39    pub fn get_row_count(&self) -> i32 {
40        self.num_rows
41    }
42}
43
44impl Value for VoltTable {
45    fn get_write_length(&self) -> i32 {
46        self.total_size + 5
47    }
48
49    fn marshal(&self, bytebuffer: &mut ByteBuffer) {
50        bytebuffer.write_i8(TABLE);
51        bytebuffer.write_u32(self.total_size as u32);
52        bytebuffer.write_u32(self.header_size as u32);
53        bytebuffer.write_u8(128);
54        bytebuffer.write_bytes(self.column_info_bytes.as_bytes());
55        bytebuffer.write_u32(self.num_rows as u32);
56        self.rows.iter().for_each(|f| {
57            bytebuffer.write_u32(f.len() as u32);
58            bytebuffer.write_bytes(f.as_bytes());
59        });
60    }
61
62    fn marshal_in_table(&self, _bytebuffer: &mut ByteBuffer, _column_type: i8) {
63        //
64    }
65
66    fn to_value_string(&self) -> String {
67        "table".to_owned()
68    }
69
70    fn from_bytes(_bs: Vec<u8>, _column: &Column) -> Result<Self, VoltError>
71    where
72        Self: Sized,
73    {
74        Err(VoltError::Other(
75            "VoltTable::from_bytes is not supported. Use new_volt_table() to parse tables from response data.".to_string()
76        ))
77    }
78}
79
80impl VoltTable {
81    /// COLUMN HEADER:
82    /// [int: column header size in bytes (non-inclusive)]
83    /// [byte: table status code]
84    /// [short: num columns]
85    /// [byte: column type] * num columns
86    /// [string: column name] * num columns
87    /// TABLE BODY DATA:
88    /// [int: num tuples]
89    /// [int: row size (non inclusive), blob row data] * num tuples
90    /// Create a new VoltTable with the given column types and headers.
91    ///
92    /// # Panics
93    /// Panics if the length of `types` and `header` vectors don't match.
94    pub fn new_table(types: Vec<i8>, header: Vec<String>) -> Self {
95        assert_eq!(
96            types.len(),
97            header.len(),
98            "Column types and headers must have the same length"
99        );
100        let columns: Vec<Column> = types
101            .iter()
102            .zip(header.iter())
103            .map(|(tp, name)| Column {
104                header_name: name.clone(),
105                header_type: *tp,
106            })
107            .collect();
108        VoltTable::new_voltdb_table(columns)
109    }
110
111    pub fn new_voltdb_table(columns: Vec<Column>) -> Self {
112        let mut column_info_bytes = ByteBuffer::new();
113
114        let column_count = columns.len() as i16;
115        column_info_bytes.write_i16(column_count);
116        columns
117            .iter()
118            .for_each(|f| column_info_bytes.write_i8(f.header_type));
119        columns
120            .iter()
121            .for_each(|f| column_info_bytes.write_string(f.header_name.as_str()));
122        let header_size = (1 + column_info_bytes.len()) as i32;
123        let total_size = header_size + 8;
124        //
125        VoltTable {
126            info: Default::default(),
127            column_count,
128            info_bytes: Default::default(),
129            column_info_bytes,
130            columns,
131            num_rows: 0,
132            rows: vec![],
133            row_index: 0,
134            cn_to_ci: Default::default(),
135            column_offsets: vec![],
136            header_size,
137            total_size,
138        }
139    }
140
141    /// Add a row to the table.
142    ///
143    /// # Arguments
144    /// * `row` - A vector of values matching the table's column count and types.
145    ///
146    /// # Errors
147    /// Returns an error if the row has fewer values than the table has columns.
148    pub fn add_row(&mut self, row: Vec<&dyn Value>) -> Result<i16, VoltError> {
149        if row.len() < self.columns.len() {
150            return Err(VoltError::Other(format!(
151                "Row has {} values but table has {} columns",
152                row.len(),
153                self.columns.len()
154            )));
155        }
156        let mut bf: ByteBuffer = ByteBuffer::new();
157        for (i, col) in self.columns.iter().enumerate() {
158            let value = row[i]; // Safe: we checked row.len() >= columns.len()
159            value.marshal_in_table(&mut bf, col.header_type);
160        }
161        let len = bf.len();
162        self.rows.push(bf);
163        self.num_rows += 1;
164        self.total_size += (len + 4) as i32;
165        Ok(1)
166    }
167
168    pub fn get_column_index(&mut self, column: &str) -> Result<i16, VoltError> {
169        let idx = self
170            .cn_to_ci
171            .get(column.to_uppercase().as_str())
172            .ok_or(VoltError::NoValue(column.to_owned()))?;
173        Ok(*idx)
174    }
175
176    pub fn map_row<'a, T: From<&'a mut VoltTable>>(&'a mut self) -> T {
177        T::from(self)
178    }
179    pub fn take<T: Value>(&mut self, column: i16) -> Result<T, VoltError> {
180        let bs = self.get_bytes_by_idx(column)?;
181        let column = self.get_column_by_index(column)?;
182        T::from_bytes(bs, column)
183    }
184
185    pub fn fetch<T: Value>(&mut self, column: &str) -> Result<T, VoltError> {
186        let idx = self.get_column_index(column)?;
187        let bs = self.get_bytes_by_idx(idx)?;
188        let column = self.get_column_by_index(idx)?;
189        T::from_bytes(bs, column)
190    }
191
192    pub fn debug_row(&mut self) -> String {
193        let x: Vec<String> = self
194            .columns()
195            .into_iter()
196            .enumerate()
197            .map(|(idx, column)| {
198                format!(
199                    "{} {:?}",
200                    column.header_name,
201                    self.get_value_by_idx_type(idx as i16, column.header_type)
202                )
203            })
204            .collect();
205        x.join(" ")
206    }
207
208    pub fn has_error(&mut self) -> Option<VoltError> {
209        if self.info.get_status() == Success {
210            return Option::None;
211        }
212        Option::Some(VoltError::ExecuteFail(self.info.clone()))
213    }
214
215    pub fn advance_row(&mut self) -> bool {
216        self.advance_to_row(self.row_index + 1)
217    }
218
219    pub fn columns(&self) -> Vec<Column> {
220        self.columns.clone()
221    }
222
223    pub fn col_length(r: &mut ByteBuffer, offset: i32, col_type: i8) -> Result<i32, VoltError> {
224        match col_type {
225            crate::encode::ARRAY_COLUMN => Err(VoltError::InvalidColumnType(col_type)),
226            crate::encode::NULL_COLUMN => Ok(0),
227            crate::encode::TINYINT_COLUMN => Ok(1),
228            crate::encode::SHORT_COLUMN => Ok(2),
229            crate::encode::INT_COLUMN => Ok(4),
230            crate::encode::LONG_COLUMN => Ok(8),
231            crate::encode::FLOAT_COLUMN => Ok(8),
232            crate::encode::STRING_COLUMN => {
233                r.set_rpos(offset as usize);
234                let str_len = r.read_i32()?;
235                if str_len == -1 {
236                    // encoding for null string.
237                    return Ok(4);
238                }
239                Ok(4 + str_len)
240            }
241            crate::encode::TIMESTAMP_COLUMN => Ok(8),
242            crate::encode::DECIMAL_COLUMN => Ok(16),
243            crate::encode::VAR_BIN_COLUMN => {
244                r.set_rpos(offset as usize);
245                let str_len = r.read_i32()?;
246                if str_len == -1 {
247                    // encoding for null string.
248                    return Ok(4);
249                }
250                Ok(4 + str_len)
251            }
252            crate::encode::GEOGRAPHY_POINT_COLUMN => Err(VoltError::Other(
253                "GEOGRAPHY_POINT type is not supported. See: https://github.com/johnnywale/voltdb-client-rust".to_string()
254            )),
255            crate::encode::GEOGRAPHY_COLUMN => Err(VoltError::Other(
256                "GEOGRAPHY type is not supported. See: https://github.com/johnnywale/voltdb-client-rust".to_string()
257            )),
258            _ => Err(VoltError::InvalidColumnType(col_type)),
259        }
260    }
261
262    fn calc_offsets(&mut self) -> Result<(), VoltError> {
263        let mut offsets = Vec::with_capacity((self.column_count + 1) as usize);
264        let reader = self
265            .rows
266            .get_mut(self.row_index as usize)
267            .ok_or(VoltError::NoValue(self.row_index.to_string()))?;
268        offsets.push(0);
269        let mut offset = 0;
270        for i in 0..self.column_count {
271            let column = self
272                .columns
273                .get(i as usize)
274                .ok_or(VoltError::NoValue(i.to_string()))?;
275            let length = crate::table::VoltTable::col_length(reader, offset, column.header_type)?;
276            offset += length;
277            offsets.push(offset);
278        }
279        reader.set_rpos(0);
280        self.column_offsets = offsets;
281        Ok(())
282    }
283
284    pub fn get_value_by_column(
285        &mut self,
286        column: &str,
287    ) -> Result<Option<Box<dyn Value>>, VoltError> {
288        let idx = self.get_column_index(column)?;
289        self.get_value_by_idx(idx)
290    }
291
292    pub(crate) fn get_value_by_idx_column(
293        column: &Column,
294        bs: Vec<u8>,
295    ) -> Result<Option<Box<dyn Value>>, VoltError> {
296        match column.header_type {
297            crate::encode::TINYINT_COLUMN => {
298                let res = i8::from_bytes(bs, column)?;
299                match res {
300                    i8::MIN => Ok(None),
301                    _ => Ok(Some(Box::new(res))),
302                }
303            }
304            crate::encode::SHORT_COLUMN => {
305                let res = i16::from_bytes(bs, column)?;
306                match res {
307                    i16::MIN => Ok(None),
308                    _ => Ok(Some(Box::new(res))),
309                }
310            }
311            crate::encode::INT_COLUMN => {
312                let res = i32::from_bytes(bs, column)?;
313                match res {
314                    i32::MIN => Ok(None),
315                    _ => Ok(Some(Box::new(res))),
316                }
317            }
318
319            crate::encode::LONG_COLUMN => {
320                let res = i64::from_bytes(bs, column)?;
321                match res {
322                    i64::MIN => Ok(None),
323                    _ => Ok(Some(Box::new(res))),
324                }
325            }
326
327            crate::encode::FLOAT_COLUMN => {
328                if bs == NULL_FLOAT_VALUE {
329                    return Ok(None);
330                }
331                let res = f64::from_bytes(bs, column)?;
332                Ok(Some(Box::new(res)))
333            }
334
335            crate::encode::STRING_COLUMN => {
336                if bs.len() == 4 {
337                    return Ok(None);
338                }
339                let res = String::from_bytes(bs, column)?;
340                Ok(Some(Box::new(res)))
341            }
342
343            crate::encode::TIMESTAMP_COLUMN => {
344                if bs == NULL_TIMESTAMP {
345                    return Ok(None);
346                }
347                let res = DateTime::from_bytes(bs, column)?;
348                Ok(Some(Box::new(res)))
349            }
350            crate::encode::DECIMAL_COLUMN => {
351                if bs == NULL_DECIMAL {
352                    return Ok(None);
353                }
354                let res = DateTime::from_bytes(bs, column)?;
355                Ok(Some(Box::new(res)))
356            }
357            crate::encode::VAR_BIN_COLUMN => {
358                if bs.len() == 4 {
359                    return Ok(None);
360                }
361                let res = Vec::from_bytes(bs, column)?;
362                Ok(Some(Box::new(res)))
363            }
364            _ => {
365                let res = i16::from_bytes(bs, column)?;
366                // match res {
367                //     Some(v) => {
368                Ok(Some(Box::new(res)))
369                //     }
370                // }
371            }
372        }
373    }
374
375    pub(crate) fn get_value_by_idx_type(
376        &mut self,
377        column: i16,
378        _tp: i8,
379    ) -> Result<Option<Box<dyn Value>>, VoltError> {
380        let bs = self.get_bytes_by_idx(column)?;
381        let column = self.get_column_by_index(column)?;
382        crate::table::VoltTable::get_value_by_idx_column(column, bs)
383    }
384
385    pub fn get_value_by_idx(&mut self, column: i16) -> Result<Option<Box<dyn Value>>, VoltError> {
386        let tp = self.get_column_type_by_idx(column)?;
387        self.get_value_by_idx_type(column, tp)
388    }
389
390    pub fn get_bool_by_column(&mut self, column: &str) -> Result<Option<bool>, VoltError> {
391        let idx = self.get_column_index(column)?;
392        self.get_bool_by_idx(idx)
393    }
394
395    pub fn get_bool_by_idx(&mut self, column: i16) -> Result<Option<bool>, VoltError> {
396        let bs = self.get_bytes_by_idx(column)?;
397        if bs == NULL_BIT_VALUE {
398            return Ok(Option::None);
399        }
400        if bs[0] == 0 {
401            return Ok(Some(false));
402        }
403        Ok(Some(true))
404    }
405
406    pub fn get_bytes_op_by_column(&mut self, column: &str) -> Result<Option<Vec<u8>>, VoltError> {
407        let idx = self.get_column_index(column)?;
408        self.get_bytes_op_by_idx(idx)
409    }
410
411    pub fn get_bytes_op_by_idx(&mut self, column: i16) -> Result<Option<Vec<u8>>, VoltError> {
412        let mut bs = self.get_bytes_by_idx(column)?;
413        if bs == NULL_VARCHAR {
414            return Ok(Option::None);
415        }
416        bs.drain(0..4);
417        Ok(Option::Some(bs))
418    }
419
420    pub fn get_bytes_by_column(&mut self, column: &str) -> Result<Vec<u8>, VoltError> {
421        let idx = self.get_column_index(column)?;
422        self.get_bytes_by_idx(idx)
423    }
424
425    pub fn get_decimal_by_column(&mut self, column: &str) -> Result<Option<BigDecimal>, VoltError> {
426        let idx = self.get_column_index(column)?;
427        self.get_decimal_by_idx(idx)
428    }
429
430    pub fn get_decimal_by_idx(&mut self, column: i16) -> Result<Option<BigDecimal>, VoltError> {
431        let bs = self.get_bytes_by_idx(column)?;
432        if bs == NULL_DECIMAL {
433            return Ok(Option::None);
434        }
435        let int = BigInt::from_signed_bytes_be(&bs);
436        let decimal = BigDecimal::new(int, 12);
437        Ok(Some(decimal))
438    }
439
440    pub fn get_string_by_column(&mut self, column: &str) -> Result<Option<String>, VoltError> {
441        let idx = self.get_column_index(column)?;
442        self.get_string_by_idx(idx)
443    }
444
445    pub fn get_column_by_index(&self, column: i16) -> Result<&Column, VoltError> {
446        let res = self.columns.get(column as usize);
447        match res {
448            None => Err(VoltError::NoValue(self.row_index.to_string())),
449            Some(e) => Ok(e),
450        }
451    }
452
453    pub fn get_string_by_idx(&mut self, column: i16) -> Result<Option<String>, VoltError> {
454        let table_column = self.get_column_by_index(column)?; // will change type and name into one map
455        match table_column.header_type {
456            STRING_COLUMN => {
457                let bs = self.get_bytes_by_idx(column)?;
458                if bs == NULL_VARCHAR {
459                    return Ok(Option::None);
460                }
461                let mut buffer = ByteBuffer::from_bytes(&bs);
462                Ok(Option::Some(buffer.read_string()?))
463            }
464            _ => {
465                let res = self.get_value_by_idx_type(column, table_column.header_type)?;
466                match res {
467                    Some(v) => Ok(Option::Some(v.to_value_string())),
468                    None => Ok(Option::None),
469                }
470            }
471        }
472    }
473
474    pub fn get_time_by_column(&mut self, column: &str) -> Result<Option<DateTime<Utc>>, VoltError> {
475        let idx = self.get_column_index(column)?;
476        self.get_time_by_idx(idx)
477    }
478
479    pub fn get_time_by_idx(&mut self, column: i16) -> Result<Option<DateTime<Utc>>, VoltError> {
480        let bs = self.get_bytes_by_idx(column)?;
481        if bs == NULL_TIMESTAMP {
482            return Ok(Option::None);
483        }
484        let mut buffer = ByteBuffer::from_bytes(&bs);
485        let time = buffer.read_i64()?;
486        let millis = time / 1000;
487        match Utc.timestamp_millis_opt(millis) {
488            chrono::LocalResult::Single(dt) => Ok(Option::Some(dt)),
489            _ => Err(VoltError::Other(format!(
490                "Invalid timestamp value: {}",
491                millis
492            ))),
493        }
494    }
495
496    pub fn get_bytes_by_idx(&mut self, column_index: i16) -> Result<Vec<u8>, VoltError> {
497        if self.column_offsets.is_empty() {
498            self.calc_offsets()?;
499        }
500        let buffer = self
501            .rows
502            .get_mut(self.row_index as usize)
503            .ok_or(VoltError::NoValue(self.row_index.to_string()))?;
504        let start = self
505            .column_offsets
506            .get(column_index as usize)
507            .ok_or(VoltError::NoValue(column_index.to_string()))?;
508        let end = self
509            .column_offsets
510            .get(column_index as usize + 1)
511            .ok_or(VoltError::NoValue(column_index.to_string()))?;
512        buffer.set_rpos(*start as usize);
513        let rsize = (*end - *start) as usize;
514        Ok(buffer.read_bytes(rsize)?)
515    }
516
517    pub fn advance_to_row(&mut self, row_index: i32) -> bool {
518        if row_index >= self.num_rows {
519            return false;
520        }
521        self.column_offsets = vec![];
522        self.row_index = row_index;
523        true
524    }
525
526    fn get_column_type_by_idx(&self, column_idx: i16) -> Result<i8, VoltError> {
527        if let Some(v) = self.columns.get(column_idx as usize) {
528            return Ok(v.header_type);
529        }
530        Err(VoltError::NoValue(column_idx.to_string()))
531    }
532}
533
534pub fn new_volt_table(
535    bytebuffer: &mut ByteBuffer,
536    info: VoltResponseInfo,
537) -> Result<VoltTable, VoltError> {
538    if info.get_status() != Success {
539        return Ok(VoltTable {
540            info,
541            column_count: -1,
542            info_bytes: Default::default(),
543            column_info_bytes: Default::default(),
544            columns: vec![],
545            num_rows: -1,
546            rows: vec![],
547            row_index: -1,
548            cn_to_ci: Default::default(),
549            column_offsets: vec![],
550            header_size: 0,
551            total_size: 0,
552        });
553    }
554
555    let column_counts = decode_table_common(bytebuffer)?;
556    let mut column_types: Vec<i8> = Vec::with_capacity(column_counts as usize);
557    let mut column_info_bytes = ByteBuffer::new();
558    for _ in 0..column_counts {
559        let tp = bytebuffer.read_i8()?;
560        column_types.push(tp);
561        column_info_bytes.write_i8(tp);
562    }
563    let mut columns: Vec<Column> = Vec::with_capacity(column_counts as usize);
564    let mut cn_to_ci = HashMap::with_capacity(column_counts as usize);
565    for i in 0..column_counts {
566        let name = bytebuffer.read_string()?;
567        columns.push(Column {
568            header_name: name.clone(),
569            header_type: *(column_types.get(i as usize).unwrap()),
570        });
571        column_info_bytes.write_string(name.as_str());
572        // Store uppercase key for case-insensitive lookup
573        cn_to_ci.insert(name.to_uppercase(), i);
574    }
575    let row_count = bytebuffer.read_i32()?;
576    let mut rows: Vec<ByteBuffer> = Vec::with_capacity(column_counts as usize);
577    for _ in 0..row_count {
578        let row_len = bytebuffer.read_i32()?;
579        let mut build = vec![0; row_len as usize];
580        bytebuffer.read_exact(&mut build)?;
581        let row = ByteBuffer::from_bytes(&build);
582        rows.push(row);
583    }
584    Ok(VoltTable {
585        info,
586        column_count: column_counts,
587        info_bytes: Default::default(),
588        column_info_bytes,
589        columns,
590        num_rows: row_count,
591        rows,
592        row_index: -1,
593        cn_to_ci,
594        column_offsets: vec![],
595        header_size: 0,
596        total_size: 0,
597    })
598}
599
600fn decode_table_common(bytebuffer: &mut ByteBuffer) -> Result<i16, VoltError> {
601    let _ttl_length = bytebuffer.read_i32();
602    let _meta_length = bytebuffer.read_i32();
603    let status_code = bytebuffer.read_i8()?;
604    if status_code != 0 && status_code != MIN_INT8 {
605        // copy from golang , but why ?
606        return Err(VoltError::BadReturnStatusOnTable(status_code));
607    }
608    Ok(bytebuffer.read_i16()?)
609}
610
611#[cfg(test)]
612mod tests {
613    use crate::encode::{
614        DECIMAL_COLUMN, FLOAT_COLUMN, INT_COLUMN, LONG_COLUMN, SHORT_COLUMN, STRING_COLUMN,
615        TIMESTAMP_COLUMN, TINYINT_COLUMN, VAR_BIN_COLUMN,
616    };
617    use crate::volt_param;
618
619    use super::*;
620
621    fn template(tps: Vec<&str>, none: &str) {
622        for tp in tps {
623            let shader = r#"
624                pub fn get_${type}_by_column (&mut self, column: &str) -> Result<Option<${type}>, VoltError> {
625                    let idx = self.get_column_index(column)?;
626                    return Ok(self.get_${type}_by_idx>](idx)?);
627                }
628
629                pub fn get_${type}_by_idx (&mut self, column: i16) -> Result<Option<${type}>, VoltError> {
630                    let bs = self.get_bytes_by_idx(column)?;
631                    if bs == ${none} {
632                       return Ok(Option::None);
633                    }
634                    let mut buffer = ByteBuffer::from_bytes(&bs);
635                    let value = buffer.read_${type}()?;
636                    return Ok(Some(value));
637                }
638"#;
639            println!("{}", shader.replace("${type}", tp).replace("${none}", none));
640        }
641    }
642
643    #[test]
644    fn test_encode_table() -> Result<(), VoltError> {
645        let bs = vec![
646            21, 0, 0, 0, 86, 0, 0, 0, 49, 128, 0, 4, 6, 6, 3, 6, 0, 0, 0, 2, 73, 68, 0, 0, 0, 7,
647            86, 69, 82, 83, 73, 79, 78, 0, 0, 0, 7, 68, 69, 76, 69, 84, 69, 68, 0, 0, 0, 10, 67,
648            82, 69, 65, 84, 69, 68, 95, 66, 89, 0, 0, 0, 1, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 1, 0,
649            0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
650        ];
651        let header = ["ID", "VERSION", "DELETED", "CREATED_BY"];
652        let tp = vec![LONG_COLUMN, LONG_COLUMN, TINYINT_COLUMN, LONG_COLUMN];
653        let header: Vec<String> = header
654            .iter()
655            .map(|f| f.to_string())
656            .collect::<Vec<String>>();
657        let mut x = VoltTable::new_table(tp, header);
658        let data = volt_param! {1_i64, 1_i64, false, 1_i64};
659        x.add_row(data)?;
660        let mut bf = ByteBuffer::new();
661        x.marshal(&mut bf);
662        assert_eq!(bs, bf.into_vec());
663        Ok(())
664    }
665
666    #[test]
667    fn test_table() {
668        let bs = vec![
669            0_u8, 1, 128, 0, 0, 0, 3, 0, 1, 0, 0, 0, 133, 0, 0, 0, 66, 128, 0, 9, 3, 4, 5, 6, 8,
670            22, 9, 25, 11, 0, 0, 0, 2, 84, 49, 0, 0, 0, 2, 84, 50, 0, 0, 0, 2, 84, 51, 0, 0, 0, 2,
671            84, 52, 0, 0, 0, 2, 84, 53, 0, 0, 0, 2, 84, 54, 0, 0, 0, 2, 84, 55, 0, 0, 0, 2, 84, 56,
672            0, 0, 0, 2, 84, 57, 0, 0, 0, 1, 0, 0, 0, 55, 128, 128, 0, 128, 0, 0, 0, 128, 0, 0, 0,
673            0, 0, 0, 0, 255, 239, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
674            0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0,
675        ];
676        let mut b = ByteBuffer::from_bytes(&bs);
677        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
678        let mut table = new_volt_table(&mut b, info).unwrap();
679        table.advance_row();
680
681        let header = table.columns();
682        assert_eq!(header.len(), 9);
683        assert_eq!(
684            *header.first().unwrap(),
685            Column {
686                header_name: "T1".to_owned(),
687                header_type: TINYINT_COLUMN,
688            }
689        );
690        assert_eq!(
691            *header.get(1).unwrap(),
692            Column {
693                header_name: "T2".to_owned(),
694                header_type: SHORT_COLUMN,
695            }
696        );
697        assert_eq!(
698            *header.get(2).unwrap(),
699            Column {
700                header_name: "T3".to_owned(),
701                header_type: INT_COLUMN,
702            }
703        );
704        assert_eq!(
705            *header.get(3).unwrap(),
706            Column {
707                header_name: "T4".to_owned(),
708                header_type: LONG_COLUMN,
709            }
710        );
711        assert_eq!(
712            *header.get(4).unwrap(),
713            Column {
714                header_name: "T5".to_owned(),
715                header_type: FLOAT_COLUMN,
716            }
717        );
718        assert_eq!(
719            *header.get(5).unwrap(),
720            Column {
721                header_name: "T6".to_owned(),
722                header_type: DECIMAL_COLUMN,
723            }
724        );
725        assert_eq!(
726            *header.get(6).unwrap(),
727            Column {
728                header_name: "T7".to_owned(),
729                header_type: STRING_COLUMN,
730            }
731        );
732        assert_eq!(
733            *header.get(7).unwrap(),
734            Column {
735                header_name: "T8".to_owned(),
736                header_type: VAR_BIN_COLUMN,
737            }
738        );
739        assert_eq!(
740            *header.get(8).unwrap(),
741            Column {
742                header_name: "T9".to_owned(),
743                header_type: TIMESTAMP_COLUMN,
744            }
745        );
746
747        let i1 = table.get_bool_by_idx(0).unwrap();
748        let i2 = table.get_i16_by_idx(1).unwrap();
749        let i3 = table.get_i32_by_idx(2).unwrap();
750        let i4 = table.get_i64_by_idx(3).unwrap();
751        let i5 = table.get_f64_by_idx(4).unwrap();
752        let i6 = table.get_decimal_by_idx(5).unwrap();
753        let i7 = table.get_string_by_idx(6).unwrap();
754        let i8 = table.get_bytes_op_by_idx(7).unwrap();
755        let i9 = table.get_time_by_idx(8).unwrap();
756
757        assert_eq!(i1, None);
758        assert_eq!(i2, None);
759        assert_eq!(i3, None);
760        assert_eq!(i4, None);
761        assert_eq!(i5, None);
762        assert_eq!(i6, None);
763        assert_eq!(i7, None);
764        assert_eq!(i8, None);
765        assert_eq!(i9, None);
766        let offsets = vec![0, 1, 3, 7, 15, 23, 39, 43, 47, 55];
767        assert_eq!(offsets, table.column_offsets);
768    }
769
770    #[test]
771    fn test_big_decimal() {
772        template(vec!["i8", "u8"], "NULL_BYTE_VALUE");
773        template(vec!["i16", "u16"], "NULL_SHORT_VALUE");
774        template(vec!["i32", "u32"], "NULL_INT_VALUE");
775        template(vec!["i64", "u64"], "NULL_LONG_VALUE");
776        template(vec!["f32", "f64"], "NULL_FLOAT_VALUE");
777    }
778
779    // Helper to create a test table with actual non-null values
780    fn get_value_test_bytes() -> Vec<u8> {
781        // Row data: 1 + 2 + 4 + 8 + 8 + 16 + 9 + 7 + 8 = 63 bytes
782        vec![
783            0, 1, 128, 0, 0, 0, 3, 0, 1, 0, 0, 0, 133, 0, 0, 0, 66, 128, 0, 9, 3, 4, 5, 6, 8, 22,
784            9, 25, 11, 0, 0, 0, 2, 84, 49, // T1
785            0, 0, 0, 2, 84, 50, // T2
786            0, 0, 0, 2, 84, 51, // T3
787            0, 0, 0, 2, 84, 52, // T4
788            0, 0, 0, 2, 84, 53, // T5
789            0, 0, 0, 2, 84, 54, // T6
790            0, 0, 0, 2, 84, 55, // T7
791            0, 0, 0, 2, 84, 56, // T8
792            0, 0, 0, 2, 84, 57, // T9
793            0, 0, 0, 1, // 1 row
794            0, 0, 0, 63, // row length = 63 bytes
795            1,  // T1: bool = true (1 byte)
796            0, 100, // T2: short = 100 (2 bytes)
797            0, 0, 1, 0, // T3: int = 256 (4 bytes)
798            0, 0, 0, 0, 0, 0, 0, 42, // T4: long = 42 (8 bytes)
799            64, 9, 33, 251, 84, 68, 45, 24, // T5: f64 ~ 3.14159 (8 bytes)
800            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0, // T6: decimal (16 bytes)
801            0, 0, 0, 5, 104, 101, 108, 108, 111, // T7: string "hello" (4 + 5 = 9 bytes)
802            0, 0, 0, 3, 1, 2, 3, // T8: varbinary [1,2,3] (4 + 3 = 7 bytes)
803            0, 0, 0, 0, 0, 15, 66, 64, // T9: timestamp (8 bytes)
804        ]
805    }
806
807    #[test]
808    fn test_get_bool_by_column() {
809        let bs = get_value_test_bytes();
810        let mut b = ByteBuffer::from_bytes(&bs);
811        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
812        let mut table = new_volt_table(&mut b, info).unwrap();
813        table.advance_row();
814
815        let result = table.get_bool_by_column("T1").unwrap();
816        assert_eq!(result, Some(true));
817    }
818
819    #[test]
820    fn test_get_string_by_column() {
821        let bs = get_value_test_bytes();
822        let mut b = ByteBuffer::from_bytes(&bs);
823        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
824        let mut table = new_volt_table(&mut b, info).unwrap();
825        table.advance_row();
826
827        let result = table.get_string_by_column("T7").unwrap();
828        assert_eq!(result, Some("hello".to_string()));
829    }
830
831    #[test]
832    fn test_get_string_by_idx_from_int() {
833        let bs = get_value_test_bytes();
834        let mut b = ByteBuffer::from_bytes(&bs);
835        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
836        let mut table = new_volt_table(&mut b, info).unwrap();
837        table.advance_row();
838
839        // Get string representation of integer column
840        let result = table.get_string_by_idx(2).unwrap();
841        assert_eq!(result, Some("256".to_string()));
842    }
843
844    #[test]
845    fn test_get_bytes_op_by_column() {
846        let bs = get_value_test_bytes();
847        let mut b = ByteBuffer::from_bytes(&bs);
848        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
849        let mut table = new_volt_table(&mut b, info).unwrap();
850        table.advance_row();
851
852        let result = table.get_bytes_op_by_column("T8").unwrap();
853        assert_eq!(result, Some(vec![1, 2, 3]));
854    }
855
856    #[test]
857    fn test_get_decimal_by_column() {
858        let bs = get_value_test_bytes();
859        let mut b = ByteBuffer::from_bytes(&bs);
860        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
861        let mut table = new_volt_table(&mut b, info).unwrap();
862        table.advance_row();
863
864        let result = table.get_decimal_by_column("T6").unwrap();
865        assert!(result.is_some());
866    }
867
868    #[test]
869    fn test_get_time_by_column() {
870        let bs = get_value_test_bytes();
871        let mut b = ByteBuffer::from_bytes(&bs);
872        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
873        let mut table = new_volt_table(&mut b, info).unwrap();
874        table.advance_row();
875
876        let result = table.get_time_by_column("T9").unwrap();
877        assert!(result.is_some());
878    }
879
880    #[test]
881    fn test_get_bytes_by_column() {
882        let bs = get_value_test_bytes();
883        let mut b = ByteBuffer::from_bytes(&bs);
884        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
885        let mut table = new_volt_table(&mut b, info).unwrap();
886        table.advance_row();
887
888        let result = table.get_bytes_by_column("T1").unwrap();
889        assert_eq!(result, vec![1]); // bool true = 1
890    }
891
892    #[test]
893    fn test_get_value_by_idx_tinyint() {
894        let bs = get_value_test_bytes();
895        let mut b = ByteBuffer::from_bytes(&bs);
896        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
897        let mut table = new_volt_table(&mut b, info).unwrap();
898        table.advance_row();
899
900        let result = table.get_value_by_idx(0).unwrap();
901        assert!(result.is_some());
902    }
903
904    #[test]
905    fn test_get_value_by_idx_short() {
906        let bs = get_value_test_bytes();
907        let mut b = ByteBuffer::from_bytes(&bs);
908        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
909        let mut table = new_volt_table(&mut b, info).unwrap();
910        table.advance_row();
911
912        let result = table.get_value_by_idx(1).unwrap();
913        assert!(result.is_some());
914    }
915
916    #[test]
917    fn test_get_value_by_idx_int() {
918        let bs = get_value_test_bytes();
919        let mut b = ByteBuffer::from_bytes(&bs);
920        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
921        let mut table = new_volt_table(&mut b, info).unwrap();
922        table.advance_row();
923
924        let result = table.get_value_by_idx(2).unwrap();
925        assert!(result.is_some());
926    }
927
928    #[test]
929    fn test_get_value_by_idx_long() {
930        let bs = get_value_test_bytes();
931        let mut b = ByteBuffer::from_bytes(&bs);
932        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
933        let mut table = new_volt_table(&mut b, info).unwrap();
934        table.advance_row();
935
936        let result = table.get_value_by_idx(3).unwrap();
937        assert!(result.is_some());
938    }
939
940    #[test]
941    fn test_get_value_by_idx_float() {
942        let bs = get_value_test_bytes();
943        let mut b = ByteBuffer::from_bytes(&bs);
944        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
945        let mut table = new_volt_table(&mut b, info).unwrap();
946        table.advance_row();
947
948        let result = table.get_value_by_idx(4).unwrap();
949        assert!(result.is_some());
950    }
951
952    #[test]
953    fn test_get_value_by_idx_string() {
954        let bs = get_value_test_bytes();
955        let mut b = ByteBuffer::from_bytes(&bs);
956        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
957        let mut table = new_volt_table(&mut b, info).unwrap();
958        table.advance_row();
959
960        let result = table.get_value_by_idx(6).unwrap();
961        assert!(result.is_some());
962    }
963
964    #[test]
965    fn test_get_value_by_idx_timestamp() {
966        let bs = get_value_test_bytes();
967        let mut b = ByteBuffer::from_bytes(&bs);
968        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
969        let mut table = new_volt_table(&mut b, info).unwrap();
970        table.advance_row();
971
972        let result = table.get_value_by_idx(8).unwrap();
973        assert!(result.is_some());
974    }
975
976    #[test]
977    fn test_advance_to_row() {
978        let bs = get_value_test_bytes();
979        let mut b = ByteBuffer::from_bytes(&bs);
980        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
981        let mut table = new_volt_table(&mut b, info).unwrap();
982
983        // Start at row -1
984        assert!(table.advance_row()); // Move to row 0
985        assert!(!table.advance_row()); // No more rows
986
987        // Reset to row 0
988        assert!(table.advance_to_row(0));
989        assert!(!table.advance_to_row(1)); // Only 1 row, so row 1 doesn't exist
990    }
991
992    #[test]
993    fn test_fetch() {
994        let bs = get_value_test_bytes();
995        let mut b = ByteBuffer::from_bytes(&bs);
996        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
997        let mut table = new_volt_table(&mut b, info).unwrap();
998        table.advance_row();
999
1000        let result: i32 = table.fetch("T3").unwrap();
1001        assert_eq!(result, 256);
1002    }
1003
1004    #[test]
1005    fn test_take() {
1006        let bs = get_value_test_bytes();
1007        let mut b = ByteBuffer::from_bytes(&bs);
1008        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
1009        let mut table = new_volt_table(&mut b, info).unwrap();
1010        table.advance_row();
1011
1012        let result: i32 = table.take(2).unwrap();
1013        assert_eq!(result, 256);
1014    }
1015
1016    #[test]
1017    fn test_columns() {
1018        let bs = get_value_test_bytes();
1019        let mut b = ByteBuffer::from_bytes(&bs);
1020        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
1021        let table = new_volt_table(&mut b, info).unwrap();
1022
1023        let columns = table.columns();
1024        assert_eq!(columns.len(), 9);
1025        assert_eq!(columns[0].header_name, "T1");
1026        assert_eq!(columns[0].header_type, TINYINT_COLUMN);
1027    }
1028
1029    #[test]
1030    fn test_columns_count() {
1031        let bs = get_value_test_bytes();
1032        let mut b = ByteBuffer::from_bytes(&bs);
1033        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
1034        let table = new_volt_table(&mut b, info).unwrap();
1035
1036        // Check column count via columns() method
1037        assert_eq!(table.columns().len(), 9);
1038    }
1039
1040    #[test]
1041    fn test_get_column_by_index() {
1042        let bs = get_value_test_bytes();
1043        let mut b = ByteBuffer::from_bytes(&bs);
1044        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
1045        let table = new_volt_table(&mut b, info).unwrap();
1046
1047        let col = table.get_column_by_index(0).unwrap();
1048        assert_eq!(col.header_name, "T1");
1049        assert_eq!(col.header_type, TINYINT_COLUMN);
1050
1051        // Test non-existent column
1052        let err = table.get_column_by_index(100);
1053        assert!(err.is_err());
1054    }
1055
1056    #[test]
1057    fn test_get_column_index_not_found() {
1058        let bs = get_value_test_bytes();
1059        let mut b = ByteBuffer::from_bytes(&bs);
1060        let info = VoltResponseInfo::new(&mut b, 1).unwrap();
1061        let mut table = new_volt_table(&mut b, info).unwrap();
1062        table.advance_row();
1063
1064        let result = table.get_column_index("NONEXISTENT");
1065        assert!(result.is_err());
1066    }
1067
1068    #[test]
1069    fn test_volt_table_to_value_string() {
1070        let header = vec!["ID".to_string()];
1071        let tp = vec![LONG_COLUMN];
1072        let table = VoltTable::new_table(tp, header);
1073
1074        assert_eq!(table.to_value_string(), "table");
1075    }
1076
1077    #[test]
1078    fn test_volt_table_get_write_length() {
1079        let header = vec!["ID".to_string()];
1080        let tp = vec![LONG_COLUMN];
1081        let table = VoltTable::new_table(tp, header);
1082
1083        // header_size = 1 + column_info_bytes.len()
1084        // column_info_bytes: 2 bytes for count + 1 byte for type + 4 + 2 bytes for string
1085        // total_size = header_size + 8
1086        // get_write_length = total_size + 5
1087        assert!(table.get_write_length() > 0);
1088    }
1089
1090    #[test]
1091    fn test_new_voltdb_table() {
1092        let columns = vec![
1093            Column {
1094                header_name: "COL1".to_string(),
1095                header_type: INT_COLUMN,
1096            },
1097            Column {
1098                header_name: "COL2".to_string(),
1099                header_type: STRING_COLUMN,
1100            },
1101        ];
1102        let table = VoltTable::new_voltdb_table(columns);
1103
1104        assert_eq!(table.columns().len(), 2);
1105    }
1106}