oracle_rs/
row.rs

1//! Row data handling for Oracle query results
2//!
3//! This module provides types and functions for:
4//! - Decoding row data from Oracle wire format
5//! - Representing column values in a type-safe manner
6//! - Converting Oracle types to Rust types
7
8use crate::buffer::ReadBuffer;
9use crate::constants::{length, OracleType};
10use crate::dbobject::DbObject;
11use crate::error::{Error, Result};
12use crate::statement::ColumnInfo;
13use crate::types::{
14    decode_binary_double, decode_binary_float, decode_oracle_date, decode_oracle_number,
15    decode_oracle_timestamp, decode_rowid, LobValue, OracleDate, OracleNumber, OracleTimestamp,
16    OracleVector, RefCursor, RowId,
17};
18
19/// Represents a value from an Oracle column.
20///
21/// This enum covers all the data types that can be returned from Oracle queries.
22/// Values can be accessed using the various `as_*` methods, or converted using
23/// `TryFrom` implementations.
24///
25/// # Example
26///
27/// ```rust,no_run
28/// use oracle_rs::Value;
29///
30/// fn process_value(value: &Value) {
31///     match value {
32///         Value::Null => println!("NULL"),
33///         Value::String(s) => println!("String: {}", s),
34///         Value::Integer(i) => println!("Integer: {}", i),
35///         Value::Float(f) => println!("Float: {}", f),
36///         _ => println!("Other type"),
37///     }
38/// }
39/// ```
40///
41/// # Type Conversions
42///
43/// Values can be converted to Rust types using the accessor methods:
44///
45/// ```rust
46/// use oracle_rs::Value;
47///
48/// let value = Value::Integer(42);
49/// let num: i64 = value.as_i64().unwrap();
50/// assert_eq!(num, 42);
51/// ```
52#[derive(Debug, Clone)]
53pub enum Value {
54    /// NULL value
55    Null,
56    /// String value (VARCHAR2, CHAR, CLOB as string)
57    String(String),
58    /// Byte array (RAW, BLOB as bytes)
59    Bytes(Vec<u8>),
60    /// Integer value (NUMBER that fits in i64)
61    Integer(i64),
62    /// Floating point value (NUMBER, BINARY_FLOAT, BINARY_DOUBLE)
63    Float(f64),
64    /// Oracle NUMBER as string (for full precision)
65    Number(OracleNumber),
66    /// Date value
67    Date(OracleDate),
68    /// Timestamp value (with optional timezone)
69    Timestamp(OracleTimestamp),
70    /// ROWID value
71    RowId(RowId),
72    /// Boolean value
73    Boolean(bool),
74    /// LOB value (CLOB, BLOB, BFILE)
75    Lob(LobValue),
76    /// JSON value (Oracle 21c+, stored as OSON binary format)
77    Json(serde_json::Value),
78    /// Vector value (Oracle 23ai+, for AI/ML embeddings)
79    Vector(OracleVector),
80    /// REF CURSOR value (SYS_REFCURSOR from PL/SQL)
81    /// Contains cursor metadata that can be used for fetching rows
82    Cursor(RefCursor),
83    /// Collection value (VARRAY, Nested Table)
84    /// Contains the collection type name and elements
85    Collection(DbObject),
86}
87
88impl Value {
89    /// Check if this value is NULL
90    pub fn is_null(&self) -> bool {
91        matches!(self, Value::Null)
92    }
93
94    /// Try to get as a string reference
95    pub fn as_str(&self) -> Option<&str> {
96        match self {
97            Value::String(s) => Some(s),
98            _ => None,
99        }
100    }
101
102    /// Try to get as an integer
103    pub fn as_i64(&self) -> Option<i64> {
104        match self {
105            Value::Integer(i) => Some(*i),
106            Value::Float(f) => Some(*f as i64),
107            Value::Number(n) => n.to_i64().ok(),
108            _ => None,
109        }
110    }
111
112    /// Try to get as a float
113    pub fn as_f64(&self) -> Option<f64> {
114        match self {
115            Value::Float(f) => Some(*f),
116            Value::Integer(i) => Some(*i as f64),
117            Value::Number(n) => n.to_f64().ok(),
118            _ => None,
119        }
120    }
121
122    /// Try to get as bytes
123    pub fn as_bytes(&self) -> Option<&[u8]> {
124        match self {
125            Value::Bytes(b) => Some(b),
126            Value::String(s) => Some(s.as_bytes()),
127            _ => None,
128        }
129    }
130
131    /// Try to get as a boolean
132    pub fn as_bool(&self) -> Option<bool> {
133        match self {
134            Value::Boolean(b) => Some(*b),
135            Value::Integer(i) => Some(*i != 0),
136            _ => None,
137        }
138    }
139
140    /// Try to get as a date
141    pub fn as_date(&self) -> Option<&OracleDate> {
142        match self {
143            Value::Date(d) => Some(d),
144            _ => None,
145        }
146    }
147
148    /// Try to get as a timestamp
149    pub fn as_timestamp(&self) -> Option<&OracleTimestamp> {
150        match self {
151            Value::Timestamp(ts) => Some(ts),
152            _ => None,
153        }
154    }
155
156    /// Try to get as a JSON value
157    pub fn as_json(&self) -> Option<&serde_json::Value> {
158        match self {
159            Value::Json(j) => Some(j),
160            _ => None,
161        }
162    }
163
164    /// Try to get as a vector
165    pub fn as_vector(&self) -> Option<&OracleVector> {
166        match self {
167            Value::Vector(v) => Some(v),
168            _ => None,
169        }
170    }
171
172    /// Try to get as a REF CURSOR
173    pub fn as_cursor(&self) -> Option<&RefCursor> {
174        match self {
175            Value::Cursor(cursor) => Some(cursor),
176            _ => None,
177        }
178    }
179
180    /// Try to get cursor ID (for REF CURSOR)
181    pub fn as_cursor_id(&self) -> Option<u16> {
182        match self {
183            Value::Cursor(cursor) => Some(cursor.cursor_id()),
184            _ => None,
185        }
186    }
187
188    /// Try to get as a collection (VARRAY, Nested Table)
189    pub fn as_collection(&self) -> Option<&DbObject> {
190        match self {
191            Value::Collection(obj) => Some(obj),
192            _ => None,
193        }
194    }
195}
196
197// Additional From trait implementations for ergonomic bind parameter creation
198// Note: i64, f64, &str, String, bool, Vec<u8> are already in dbobject.rs
199// Enables: conn.query("SELECT * FROM t WHERE id = :1", &[42.into()])
200
201impl From<i32> for Value {
202    fn from(v: i32) -> Self {
203        Value::Integer(v as i64)
204    }
205}
206
207impl From<f32> for Value {
208    fn from(v: f32) -> Self {
209        Value::Float(v as f64)
210    }
211}
212
213impl From<&[u8]> for Value {
214    fn from(v: &[u8]) -> Self {
215        Value::Bytes(v.to_vec())
216    }
217}
218
219impl<T: Into<Value>> From<Option<T>> for Value {
220    fn from(v: Option<T>) -> Self {
221        match v {
222            Some(inner) => inner.into(),
223            None => Value::Null,
224        }
225    }
226}
227
228impl From<serde_json::Value> for Value {
229    fn from(v: serde_json::Value) -> Self {
230        Value::Json(v)
231    }
232}
233
234impl From<OracleVector> for Value {
235    fn from(v: OracleVector) -> Self {
236        Value::Vector(v)
237    }
238}
239
240impl From<Vec<f32>> for Value {
241    fn from(v: Vec<f32>) -> Self {
242        Value::Vector(OracleVector::float32(v))
243    }
244}
245
246impl From<Vec<f64>> for Value {
247    fn from(v: Vec<f64>) -> Self {
248        Value::Vector(OracleVector::float64(v))
249    }
250}
251
252impl From<DbObject> for Value {
253    fn from(v: DbObject) -> Self {
254        Value::Collection(v)
255    }
256}
257
258impl std::fmt::Display for Value {
259    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260        match self {
261            Value::Null => write!(f, "NULL"),
262            Value::String(s) => write!(f, "{}", s),
263            Value::Bytes(b) => write!(f, "<{} bytes>", b.len()),
264            Value::Integer(i) => write!(f, "{}", i),
265            Value::Float(fl) => write!(f, "{}", fl),
266            Value::Number(n) => write!(f, "{}", n.as_str()),
267            Value::Date(d) => write!(
268                f,
269                "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
270                d.year, d.month, d.day, d.hour, d.minute, d.second
271            ),
272            Value::Timestamp(ts) => {
273                write!(
274                    f,
275                    "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}",
276                    ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.microsecond
277                )?;
278                if ts.has_timezone() {
279                    write!(f, " {:+03}:{:02}", ts.tz_hour_offset, ts.tz_minute_offset)?;
280                }
281                Ok(())
282            }
283            Value::RowId(r) => write!(f, "{}", r),
284            Value::Boolean(b) => write!(f, "{}", b),
285            Value::Lob(lob) => match lob {
286                LobValue::Null => write!(f, "NULL"),
287                LobValue::Empty => write!(f, "<empty LOB>"),
288                LobValue::Inline(data) => write!(f, "<LOB: {} bytes inline>", data.len()),
289                LobValue::Locator(loc) => {
290                    write!(f, "<LOB: {} bytes, locator>", loc.size())
291                }
292            },
293            Value::Json(json) => write!(f, "{}", json),
294            Value::Vector(vec) => write!(f, "<VECTOR: {} dimensions>", vec.dimensions()),
295            Value::Cursor(cursor) => write!(f, "<CURSOR: id={}, {} columns>", cursor.cursor_id(), cursor.column_count()),
296            Value::Collection(obj) => {
297                if obj.is_collection {
298                    write!(f, "<COLLECTION {}: {} elements>", obj.type_name, obj.elements.len())
299                } else {
300                    write!(f, "<OBJECT {}: {} attributes>", obj.type_name, obj.values.len())
301                }
302            }
303        }
304    }
305}
306
307/// A row of data from a query result.
308///
309/// Rows contain values that can be accessed by column index (0-based) or by
310/// column name. Use the `get_*` methods for type-safe value extraction.
311///
312/// # Example
313///
314/// ```rust,no_run
315/// use oracle_rs::{Connection, Row};
316///
317/// # async fn example(conn: Connection) -> oracle_rs::Result<()> {
318/// let result = conn.query("SELECT id, name, salary FROM employees", &[]).await?;
319///
320/// for row in &result.rows {
321///     // Access by index
322///     let id = row.get_i64(0).unwrap_or(0);
323///
324///     // Access by column name
325///     let name = row.get_by_name("name").and_then(|v| v.as_str()).unwrap_or("");
326///     let salary = row.get_by_name("salary").and_then(|v| v.as_f64()).unwrap_or(0.0);
327///
328///     println!("{}: {} (${:.2})", id, name, salary);
329/// }
330/// # Ok(())
331/// # }
332/// ```
333#[derive(Debug, Clone)]
334pub struct Row {
335    /// Column values
336    values: Vec<Value>,
337    /// Column names (optional, for named access)
338    column_names: Option<Vec<String>>,
339}
340
341impl Row {
342    /// Create a new row with values
343    pub fn new(values: Vec<Value>) -> Self {
344        Self {
345            values,
346            column_names: None,
347        }
348    }
349
350    /// Create a new row with values and column names
351    pub fn with_names(values: Vec<Value>, names: Vec<String>) -> Self {
352        Self {
353            values,
354            column_names: Some(names),
355        }
356    }
357
358    /// Get the number of columns in this row
359    pub fn len(&self) -> usize {
360        self.values.len()
361    }
362
363    /// Check if the row is empty
364    pub fn is_empty(&self) -> bool {
365        self.values.is_empty()
366    }
367
368    /// Get a value by column index
369    pub fn get(&self, index: usize) -> Option<&Value> {
370        self.values.get(index)
371    }
372
373    /// Get a value by column name
374    pub fn get_by_name(&self, name: &str) -> Option<&Value> {
375        let names = self.column_names.as_ref()?;
376        let index = names.iter().position(|n| n.eq_ignore_ascii_case(name))?;
377        self.values.get(index)
378    }
379
380    /// Get all values as a slice
381    pub fn values(&self) -> &[Value] {
382        &self.values
383    }
384
385    /// Consume the row and return the values
386    pub fn into_values(self) -> Vec<Value> {
387        self.values
388    }
389
390    /// Try to get a string value by index
391    pub fn get_string(&self, index: usize) -> Option<&str> {
392        self.get(index).and_then(Value::as_str)
393    }
394
395    /// Try to get an integer value by index
396    pub fn get_i64(&self, index: usize) -> Option<i64> {
397        self.get(index).and_then(Value::as_i64)
398    }
399
400    /// Try to get a float value by index
401    pub fn get_f64(&self, index: usize) -> Option<f64> {
402        self.get(index).and_then(Value::as_f64)
403    }
404
405    /// Check if a column value is NULL
406    pub fn is_null(&self, index: usize) -> bool {
407        self.get(index).map(Value::is_null).unwrap_or(true)
408    }
409}
410
411impl std::ops::Index<usize> for Row {
412    type Output = Value;
413
414    fn index(&self, index: usize) -> &Self::Output {
415        &self.values[index]
416    }
417}
418
419/// Decoder for row data from Oracle wire format
420pub struct RowDataDecoder<'a> {
421    columns: &'a [ColumnInfo],
422    bit_vector: Option<Vec<u8>>,
423}
424
425impl<'a> RowDataDecoder<'a> {
426    /// Create a new row data decoder
427    pub fn new(columns: &'a [ColumnInfo]) -> Self {
428        Self {
429            columns,
430            bit_vector: None,
431        }
432    }
433
434    /// Set the bit vector for duplicate data detection
435    pub fn set_bit_vector(&mut self, bit_vector: Vec<u8>) {
436        self.bit_vector = Some(bit_vector);
437    }
438
439    /// Clear the bit vector after row processing
440    pub fn clear_bit_vector(&mut self) {
441        self.bit_vector = None;
442    }
443
444    /// Check if a column has duplicate data (same as previous row)
445    fn is_duplicate(&self, column_index: usize) -> bool {
446        match &self.bit_vector {
447            Some(bv) => {
448                let byte_num = column_index / 8;
449                let bit_num = column_index % 8;
450                if byte_num < bv.len() {
451                    (bv[byte_num] & (1 << bit_num)) == 0
452                } else {
453                    false
454                }
455            }
456            None => false,
457        }
458    }
459
460    /// Decode a single row from the buffer
461    pub fn decode_row(
462        &self,
463        buf: &mut ReadBuffer,
464        previous_row: Option<&Row>,
465    ) -> Result<Row> {
466        let mut values = Vec::with_capacity(self.columns.len());
467
468        for (index, column) in self.columns.iter().enumerate() {
469            let value = if self.is_duplicate(index) {
470                // Use value from previous row
471                previous_row
472                    .and_then(|r| r.get(index))
473                    .cloned()
474                    .unwrap_or(Value::Null)
475            } else {
476                self.decode_column_value(buf, column)?
477            };
478            values.push(value);
479        }
480
481        let names: Vec<String> = self.columns.iter().map(|c| c.name.clone()).collect();
482        Ok(Row::with_names(values, names))
483    }
484
485    /// Decode a single column value from the buffer
486    fn decode_column_value(&self, buf: &mut ReadBuffer, column: &ColumnInfo) -> Result<Value> {
487        // Check if column has zero buffer size (NULL by describe)
488        if column.buffer_size == 0 {
489            match column.oracle_type {
490                OracleType::Long | OracleType::LongRaw | OracleType::Urowid => {
491                    // These types handle their own length
492                }
493                _ => return Ok(Value::Null),
494            }
495        }
496
497        // Read the column data based on type
498        match column.oracle_type {
499            OracleType::Varchar | OracleType::Char | OracleType::Long => {
500                self.decode_string(buf)
501            }
502            OracleType::Number | OracleType::BinaryInteger => {
503                self.decode_number(buf)
504            }
505            OracleType::Date => self.decode_date(buf),
506            OracleType::Timestamp | OracleType::TimestampLtz => {
507                self.decode_timestamp(buf, false)
508            }
509            OracleType::TimestampTz => self.decode_timestamp(buf, true),
510            OracleType::Raw | OracleType::LongRaw => self.decode_raw(buf),
511            OracleType::BinaryFloat => self.decode_binary_float(buf),
512            OracleType::BinaryDouble => self.decode_binary_double(buf),
513            OracleType::Rowid => self.decode_rowid(buf),
514            OracleType::Urowid => self.decode_urowid(buf),
515            OracleType::Boolean => self.decode_boolean(buf),
516            _ => {
517                // For unsupported types, try to read as raw bytes
518                self.decode_raw(buf)
519            }
520        }
521    }
522
523    /// Read Oracle-format data slice from buffer
524    fn read_oracle_slice(&self, buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
525        if buf.remaining() == 0 {
526            return Ok(None);
527        }
528
529        let length = buf.read_u8()?;
530
531        // NULL indicator
532        if length == 0 || length == length::NULL_INDICATOR {
533            return Ok(None);
534        }
535
536        // Long data indicator (chunked)
537        if length == length::LONG_INDICATOR {
538            return self.read_chunked_data(buf);
539        }
540
541        // Regular length-prefixed data
542        let data = buf.read_bytes_vec(length as usize)?;
543        Ok(Some(data))
544    }
545
546    /// Read chunked data (for long values)
547    fn read_chunked_data(&self, buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
548        let mut result = Vec::new();
549
550        loop {
551            let chunk_len = buf.read_ub4()?;
552            if chunk_len == 0 {
553                break;
554            }
555            let chunk = buf.read_bytes_vec(chunk_len as usize)?;
556            result.extend_from_slice(&chunk);
557        }
558
559        if result.is_empty() {
560            Ok(None)
561        } else {
562            Ok(Some(result))
563        }
564    }
565
566    /// Decode a string value
567    fn decode_string(&self, buf: &mut ReadBuffer) -> Result<Value> {
568        match self.read_oracle_slice(buf)? {
569            None => Ok(Value::Null),
570            Some(data) => {
571                let s = String::from_utf8(data).map_err(|e| {
572                    Error::DataConversionError(format!("Invalid UTF-8 in string: {}", e))
573                })?;
574                Ok(Value::String(s))
575            }
576        }
577    }
578
579    /// Decode a number value
580    fn decode_number(&self, buf: &mut ReadBuffer) -> Result<Value> {
581        match self.read_oracle_slice(buf)? {
582            None => Ok(Value::Null),
583            Some(data) => {
584                let num = decode_oracle_number(&data)?;
585                // Try to convert to integer if it's a whole number
586                if num.is_integer {
587                    if let Ok(i) = num.to_i64() {
588                        return Ok(Value::Integer(i));
589                    }
590                }
591                // Keep as OracleNumber for full precision
592                Ok(Value::Number(num))
593            }
594        }
595    }
596
597    /// Decode a date value
598    fn decode_date(&self, buf: &mut ReadBuffer) -> Result<Value> {
599        match self.read_oracle_slice(buf)? {
600            None => Ok(Value::Null),
601            Some(data) => {
602                let date = decode_oracle_date(&data)?;
603                Ok(Value::Date(date))
604            }
605        }
606    }
607
608    /// Decode a timestamp value
609    fn decode_timestamp(&self, buf: &mut ReadBuffer, _with_tz: bool) -> Result<Value> {
610        match self.read_oracle_slice(buf)? {
611            None => Ok(Value::Null),
612            Some(data) => {
613                let ts = decode_oracle_timestamp(&data)?;
614                Ok(Value::Timestamp(ts))
615            }
616        }
617    }
618
619    /// Decode a raw (binary) value
620    fn decode_raw(&self, buf: &mut ReadBuffer) -> Result<Value> {
621        match self.read_oracle_slice(buf)? {
622            None => Ok(Value::Null),
623            Some(data) => Ok(Value::Bytes(data)),
624        }
625    }
626
627    /// Decode a BINARY_FLOAT value
628    fn decode_binary_float(&self, buf: &mut ReadBuffer) -> Result<Value> {
629        match self.read_oracle_slice(buf)? {
630            None => Ok(Value::Null),
631            Some(data) => {
632                let f = decode_binary_float(&data);
633                Ok(Value::Float(f as f64))
634            }
635        }
636    }
637
638    /// Decode a BINARY_DOUBLE value
639    fn decode_binary_double(&self, buf: &mut ReadBuffer) -> Result<Value> {
640        match self.read_oracle_slice(buf)? {
641            None => Ok(Value::Null),
642            Some(data) => {
643                let f = decode_binary_double(&data);
644                Ok(Value::Float(f))
645            }
646        }
647    }
648
649    /// Decode a ROWID value
650    fn decode_rowid(&self, buf: &mut ReadBuffer) -> Result<Value> {
651        let length = buf.read_u8()?;
652
653        if length == 0 || length == length::NULL_INDICATOR {
654            return Ok(Value::Null);
655        }
656
657        // Read ROWID components
658        let rba = buf.read_ub4()?;
659        let partition_id = buf.read_ub2()?;
660        buf.skip(1)?; // skip byte
661        let block_num = buf.read_ub4()?;
662        let slot_num = buf.read_ub2()?;
663
664        let rowid = RowId::new(rba, partition_id as u16, block_num, slot_num as u16);
665        Ok(Value::RowId(rowid))
666    }
667
668    /// Decode a UROWID (universal rowid) value
669    fn decode_urowid(&self, buf: &mut ReadBuffer) -> Result<Value> {
670        // First read the outer length indicator
671        match self.read_oracle_slice(buf)? {
672            None => Ok(Value::Null),
673            Some(data) => {
674                // UROWID data includes a type indicator and the actual rowid data
675                if data.is_empty() {
676                    return Ok(Value::Null);
677                }
678
679                // Check the type indicator
680                if data[0] == 1 && data.len() >= 13 {
681                    // Physical ROWID
682                    let rowid = decode_rowid(&data)?;
683                    Ok(Value::RowId(rowid))
684                } else {
685                    // Logical ROWID - return as string (base64 encoded)
686                    let s = String::from_utf8_lossy(&data[1..]).to_string();
687                    Ok(Value::String(s))
688                }
689            }
690        }
691    }
692
693    /// Decode a boolean value
694    fn decode_boolean(&self, buf: &mut ReadBuffer) -> Result<Value> {
695        match self.read_oracle_slice(buf)? {
696            None => Ok(Value::Null),
697            Some(data) => {
698                // Boolean is typically the last byte being 1 (true) or 0 (false)
699                let b = data.last().copied().unwrap_or(0) == 1;
700                Ok(Value::Boolean(b))
701            }
702        }
703    }
704}
705
706/// Parse row header from buffer
707///
708/// Row header contains:
709/// - flags (1 byte)
710/// - num_requests (2 bytes)
711/// - iteration_number (4 bytes)
712/// - num_iters (4 bytes)
713/// - buffer_length (2 bytes)
714/// - bit_vector_length (4 bytes) + bit_vector
715/// - rxhrid_length (4 bytes) + rxhrid
716pub fn parse_row_header(buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
717    buf.skip(1)?; // flags
718    buf.skip_ub2()?; // num requests
719    buf.skip_ub4()?; // iteration number
720    buf.skip_ub4()?; // num iters
721    buf.skip_ub2()?; // buffer length
722
723    // Read bit vector length
724    let bit_vector_len = buf.read_ub4()? as usize;
725    let bit_vector = if bit_vector_len > 0 {
726        buf.skip(1)?; // skip repeated length byte
727        let data = buf.read_bytes_vec(bit_vector_len - 1)?;
728        Some(data)
729    } else {
730        None
731    };
732
733    // Skip rxhrid if present
734    let rxhrid_len = buf.read_ub4()? as usize;
735    if rxhrid_len > 0 {
736        // Skip chunked data
737        loop {
738            let chunk_len = buf.read_ub4()? as usize;
739            if chunk_len == 0 {
740                break;
741            }
742            buf.skip(chunk_len)?;
743        }
744    }
745
746    Ok(bit_vector)
747}
748
749#[cfg(test)]
750mod tests {
751    use super::*;
752    use crate::buffer::ReadBuffer;
753
754    #[test]
755    fn test_value_null() {
756        let v = Value::Null;
757        assert!(v.is_null());
758        assert!(v.as_str().is_none());
759        assert!(v.as_i64().is_none());
760    }
761
762    #[test]
763    fn test_value_string() {
764        let v = Value::String("hello".to_string());
765        assert!(!v.is_null());
766        assert_eq!(v.as_str(), Some("hello"));
767        assert_eq!(format!("{}", v), "hello");
768    }
769
770    #[test]
771    fn test_value_integer() {
772        let v = Value::Integer(42);
773        assert_eq!(v.as_i64(), Some(42));
774        assert_eq!(v.as_f64(), Some(42.0));
775        assert_eq!(format!("{}", v), "42");
776    }
777
778    #[test]
779    fn test_value_float() {
780        let v = Value::Float(3.14);
781        assert!((v.as_f64().unwrap() - 3.14).abs() < 0.001);
782        assert_eq!(v.as_i64(), Some(3));
783    }
784
785    #[test]
786    fn test_value_boolean() {
787        let v_true = Value::Boolean(true);
788        let v_false = Value::Boolean(false);
789        assert_eq!(v_true.as_bool(), Some(true));
790        assert_eq!(v_false.as_bool(), Some(false));
791    }
792
793    #[test]
794    fn test_row_creation() {
795        let values = vec![
796            Value::String("test".to_string()),
797            Value::Integer(123),
798            Value::Null,
799        ];
800        let row = Row::new(values);
801
802        assert_eq!(row.len(), 3);
803        assert!(!row.is_empty());
804        assert_eq!(row.get_string(0), Some("test"));
805        assert_eq!(row.get_i64(1), Some(123));
806        assert!(row.is_null(2));
807    }
808
809    #[test]
810    fn test_row_with_names() {
811        let values = vec![Value::Integer(1), Value::String("hello".to_string())];
812        let names = vec!["ID".to_string(), "NAME".to_string()];
813        let row = Row::with_names(values, names);
814
815        assert_eq!(row.get_by_name("ID").and_then(Value::as_i64), Some(1));
816        assert_eq!(row.get_by_name("name").and_then(Value::as_str), Some("hello"));
817        assert!(row.get_by_name("nonexistent").is_none());
818    }
819
820    #[test]
821    fn test_row_index() {
822        let values = vec![Value::Integer(42)];
823        let row = Row::new(values);
824        assert!(matches!(&row[0], Value::Integer(42)));
825    }
826
827    fn make_column(name: &str, oracle_type: OracleType, buffer_size: u32) -> ColumnInfo {
828        ColumnInfo {
829            name: name.to_string(),
830            oracle_type,
831            data_size: buffer_size,
832            buffer_size,
833            precision: 0,
834            scale: 0,
835            nullable: true,
836            csfrm: 0,
837            type_schema: None,
838            type_name: None,
839            domain_schema: None,
840            domain_name: None,
841            is_json: false,
842            is_oson: false,
843            vector_dimensions: None,
844            vector_format: None,
845            element_type: None,
846        }
847    }
848
849    #[test]
850    fn test_decode_null_value() {
851        let columns = vec![make_column("TEST", OracleType::Varchar, 100)];
852
853        let decoder = RowDataDecoder::new(&columns);
854        let data = vec![255u8]; // NULL indicator
855        let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
856
857        let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
858        assert!(value.is_null());
859    }
860
861    #[test]
862    fn test_decode_string_value() {
863        let columns = vec![make_column("TEST", OracleType::Varchar, 100)];
864
865        let decoder = RowDataDecoder::new(&columns);
866        let data = vec![5u8, b'h', b'e', b'l', b'l', b'o'];
867        let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
868
869        let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
870        assert_eq!(value.as_str(), Some("hello"));
871    }
872
873    #[test]
874    fn test_decode_integer_value() {
875        let columns = vec![make_column("NUM", OracleType::Number, 22)];
876
877        let decoder = RowDataDecoder::new(&columns);
878        // Oracle NUMBER encoding for 123
879        let data = vec![3u8, 0xc2, 0x02, 0x18];
880        let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
881
882        let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
883        assert_eq!(value.as_i64(), Some(123));
884    }
885
886    #[test]
887    fn test_bit_vector_duplicate_detection() {
888        let columns = vec![
889            make_column("COL1", OracleType::Number, 22),
890            make_column("COL2", OracleType::Number, 22),
891        ];
892
893        let mut decoder = RowDataDecoder::new(&columns);
894
895        // Bit vector: 0b00000010 means column 0 is NOT duplicate (bit=1), column 1 IS duplicate (bit=0)
896        decoder.set_bit_vector(vec![0b00000001]);
897
898        assert!(!decoder.is_duplicate(0)); // bit is 1, not duplicate
899        assert!(decoder.is_duplicate(1)); // bit is 0, is duplicate
900    }
901
902    #[test]
903    fn test_value_display() {
904        assert_eq!(format!("{}", Value::Null), "NULL");
905        assert_eq!(format!("{}", Value::Integer(42)), "42");
906        assert_eq!(format!("{}", Value::Float(3.14)), "3.14");
907        assert_eq!(format!("{}", Value::String("test".into())), "test");
908        assert_eq!(format!("{}", Value::Boolean(true)), "true");
909        assert_eq!(format!("{}", Value::Bytes(vec![1, 2, 3])), "<3 bytes>");
910    }
911
912    #[test]
913    fn test_decode_binary_float() {
914        let columns = vec![make_column("FLOAT_COL", OracleType::BinaryFloat, 4)];
915
916        let decoder = RowDataDecoder::new(&columns);
917
918        // Encoded 1.0f32 in Oracle format
919        let encoded = crate::types::encode_binary_float(1.0f32);
920        let mut data = vec![4u8]; // length
921        data.extend_from_slice(&encoded);
922
923        let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
924        let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
925
926        assert!((value.as_f64().unwrap() - 1.0).abs() < 0.0001);
927    }
928
929    #[test]
930    fn test_decode_binary_double() {
931        let columns = vec![make_column("DOUBLE_COL", OracleType::BinaryDouble, 8)];
932
933        let decoder = RowDataDecoder::new(&columns);
934
935        // Encoded 3.14159 in Oracle format
936        let encoded = crate::types::encode_binary_double(3.14159f64);
937        let mut data = vec![8u8]; // length
938        data.extend_from_slice(&encoded);
939
940        let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
941        let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
942
943        assert!((value.as_f64().unwrap() - 3.14159).abs() < 0.00001);
944    }
945}