use crate::buffer::ReadBuffer;
use crate::constants::{length, OracleType};
use crate::dbobject::DbObject;
use crate::error::{Error, Result};
use crate::statement::ColumnInfo;
use crate::types::{
decode_binary_double, decode_binary_float, decode_oracle_date, decode_oracle_number,
decode_oracle_timestamp, decode_rowid, LobValue, OracleDate, OracleNumber, OracleTimestamp,
OracleVector, RefCursor, RowId,
};
#[derive(Debug, Clone)]
pub enum Value {
Null,
String(String),
Bytes(Vec<u8>),
Integer(i64),
Float(f64),
Number(OracleNumber),
Date(OracleDate),
Timestamp(OracleTimestamp),
RowId(RowId),
Boolean(bool),
Lob(LobValue),
Json(serde_json::Value),
Vector(OracleVector),
Cursor(RefCursor),
Collection(DbObject),
}
impl Value {
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Integer(i) => Some(*i),
Value::Float(f) => Some(*f as i64),
Value::Number(n) => n.to_i64().ok(),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
Value::Integer(i) => Some(*i as f64),
Value::Number(n) => n.to_f64().ok(),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Value::Bytes(b) => Some(b),
Value::String(s) => Some(s.as_bytes()),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Boolean(b) => Some(*b),
Value::Integer(i) => Some(*i != 0),
_ => None,
}
}
pub fn as_date(&self) -> Option<&OracleDate> {
match self {
Value::Date(d) => Some(d),
_ => None,
}
}
pub fn as_timestamp(&self) -> Option<&OracleTimestamp> {
match self {
Value::Timestamp(ts) => Some(ts),
_ => None,
}
}
pub fn as_json(&self) -> Option<&serde_json::Value> {
match self {
Value::Json(j) => Some(j),
_ => None,
}
}
pub fn as_vector(&self) -> Option<&OracleVector> {
match self {
Value::Vector(v) => Some(v),
_ => None,
}
}
pub fn as_cursor(&self) -> Option<&RefCursor> {
match self {
Value::Cursor(cursor) => Some(cursor),
_ => None,
}
}
pub fn as_cursor_id(&self) -> Option<u16> {
match self {
Value::Cursor(cursor) => Some(cursor.cursor_id()),
_ => None,
}
}
pub fn as_collection(&self) -> Option<&DbObject> {
match self {
Value::Collection(obj) => Some(obj),
_ => None,
}
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Value::Integer(v as i64)
}
}
impl From<f32> for Value {
fn from(v: f32) -> Self {
Value::Float(v as f64)
}
}
impl From<&[u8]> for Value {
fn from(v: &[u8]) -> Self {
Value::Bytes(v.to_vec())
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
match v {
Some(inner) => inner.into(),
None => Value::Null,
}
}
}
impl From<serde_json::Value> for Value {
fn from(v: serde_json::Value) -> Self {
Value::Json(v)
}
}
impl From<OracleVector> for Value {
fn from(v: OracleVector) -> Self {
Value::Vector(v)
}
}
impl From<Vec<f32>> for Value {
fn from(v: Vec<f32>) -> Self {
Value::Vector(OracleVector::float32(v))
}
}
impl From<Vec<f64>> for Value {
fn from(v: Vec<f64>) -> Self {
Value::Vector(OracleVector::float64(v))
}
}
impl From<DbObject> for Value {
fn from(v: DbObject) -> Self {
Value::Collection(v)
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "NULL"),
Value::String(s) => write!(f, "{}", s),
Value::Bytes(b) => write!(f, "<{} bytes>", b.len()),
Value::Integer(i) => write!(f, "{}", i),
Value::Float(fl) => write!(f, "{}", fl),
Value::Number(n) => write!(f, "{}", n.as_str()),
Value::Date(d) => write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
d.year, d.month, d.day, d.hour, d.minute, d.second
),
Value::Timestamp(ts) => {
write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}",
ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.microsecond
)?;
if ts.has_timezone() {
write!(f, " {:+03}:{:02}", ts.tz_hour_offset, ts.tz_minute_offset)?;
}
Ok(())
}
Value::RowId(r) => write!(f, "{}", r),
Value::Boolean(b) => write!(f, "{}", b),
Value::Lob(lob) => match lob {
LobValue::Null => write!(f, "NULL"),
LobValue::Empty => write!(f, "<empty LOB>"),
LobValue::Inline(data) => write!(f, "<LOB: {} bytes inline>", data.len()),
LobValue::Locator(loc) => {
write!(f, "<LOB: {} bytes, locator>", loc.size())
}
},
Value::Json(json) => write!(f, "{}", json),
Value::Vector(vec) => write!(f, "<VECTOR: {} dimensions>", vec.dimensions()),
Value::Cursor(cursor) => write!(f, "<CURSOR: id={}, {} columns>", cursor.cursor_id(), cursor.column_count()),
Value::Collection(obj) => {
if obj.is_collection {
write!(f, "<COLLECTION {}: {} elements>", obj.type_name, obj.elements.len())
} else {
write!(f, "<OBJECT {}: {} attributes>", obj.type_name, obj.values.len())
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct Row {
values: Vec<Value>,
column_names: Option<Vec<String>>,
}
impl Row {
pub fn new(values: Vec<Value>) -> Self {
Self {
values,
column_names: None,
}
}
pub fn with_names(values: Vec<Value>, names: Vec<String>) -> Self {
Self {
values,
column_names: Some(names),
}
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn get(&self, index: usize) -> Option<&Value> {
self.values.get(index)
}
pub fn get_by_name(&self, name: &str) -> Option<&Value> {
let names = self.column_names.as_ref()?;
let index = names.iter().position(|n| n.eq_ignore_ascii_case(name))?;
self.values.get(index)
}
pub fn values(&self) -> &[Value] {
&self.values
}
pub fn into_values(self) -> Vec<Value> {
self.values
}
pub fn get_string(&self, index: usize) -> Option<&str> {
self.get(index).and_then(Value::as_str)
}
pub fn get_i64(&self, index: usize) -> Option<i64> {
self.get(index).and_then(Value::as_i64)
}
pub fn get_f64(&self, index: usize) -> Option<f64> {
self.get(index).and_then(Value::as_f64)
}
pub fn is_null(&self, index: usize) -> bool {
self.get(index).map(Value::is_null).unwrap_or(true)
}
}
impl std::ops::Index<usize> for Row {
type Output = Value;
fn index(&self, index: usize) -> &Self::Output {
&self.values[index]
}
}
pub struct RowDataDecoder<'a> {
columns: &'a [ColumnInfo],
bit_vector: Option<Vec<u8>>,
}
impl<'a> RowDataDecoder<'a> {
pub fn new(columns: &'a [ColumnInfo]) -> Self {
Self {
columns,
bit_vector: None,
}
}
pub fn set_bit_vector(&mut self, bit_vector: Vec<u8>) {
self.bit_vector = Some(bit_vector);
}
pub fn clear_bit_vector(&mut self) {
self.bit_vector = None;
}
fn is_duplicate(&self, column_index: usize) -> bool {
match &self.bit_vector {
Some(bv) => {
let byte_num = column_index / 8;
let bit_num = column_index % 8;
if byte_num < bv.len() {
(bv[byte_num] & (1 << bit_num)) == 0
} else {
false
}
}
None => false,
}
}
pub fn decode_row(
&self,
buf: &mut ReadBuffer,
previous_row: Option<&Row>,
) -> Result<Row> {
let mut values = Vec::with_capacity(self.columns.len());
for (index, column) in self.columns.iter().enumerate() {
let value = if self.is_duplicate(index) {
previous_row
.and_then(|r| r.get(index))
.cloned()
.unwrap_or(Value::Null)
} else {
self.decode_column_value(buf, column)?
};
values.push(value);
}
let names: Vec<String> = self.columns.iter().map(|c| c.name.clone()).collect();
Ok(Row::with_names(values, names))
}
fn decode_column_value(&self, buf: &mut ReadBuffer, column: &ColumnInfo) -> Result<Value> {
if column.buffer_size == 0 {
match column.oracle_type {
OracleType::Long | OracleType::LongRaw | OracleType::Urowid => {
}
_ => return Ok(Value::Null),
}
}
match column.oracle_type {
OracleType::Varchar | OracleType::Char | OracleType::Long => {
self.decode_string(buf)
}
OracleType::Number | OracleType::BinaryInteger => {
self.decode_number(buf)
}
OracleType::Date => self.decode_date(buf),
OracleType::Timestamp | OracleType::TimestampLtz => {
self.decode_timestamp(buf, false)
}
OracleType::TimestampTz => self.decode_timestamp(buf, true),
OracleType::Raw | OracleType::LongRaw => self.decode_raw(buf),
OracleType::BinaryFloat => self.decode_binary_float(buf),
OracleType::BinaryDouble => self.decode_binary_double(buf),
OracleType::Rowid => self.decode_rowid(buf),
OracleType::Urowid => self.decode_urowid(buf),
OracleType::Boolean => self.decode_boolean(buf),
_ => {
self.decode_raw(buf)
}
}
}
fn read_oracle_slice(&self, buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
if buf.remaining() == 0 {
return Ok(None);
}
let length = buf.read_u8()?;
if length == 0 || length == length::NULL_INDICATOR {
return Ok(None);
}
if length == length::LONG_INDICATOR {
return self.read_chunked_data(buf);
}
let data = buf.read_bytes_vec(length as usize)?;
Ok(Some(data))
}
fn read_chunked_data(&self, buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
let mut result = Vec::new();
loop {
let chunk_len = buf.read_ub4()?;
if chunk_len == 0 {
break;
}
let chunk = buf.read_bytes_vec(chunk_len as usize)?;
result.extend_from_slice(&chunk);
}
if result.is_empty() {
Ok(None)
} else {
Ok(Some(result))
}
}
fn decode_string(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let s = String::from_utf8(data).map_err(|e| {
Error::DataConversionError(format!("Invalid UTF-8 in string: {}", e))
})?;
Ok(Value::String(s))
}
}
}
fn decode_number(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let num = decode_oracle_number(&data)?;
if num.is_integer {
if let Ok(i) = num.to_i64() {
return Ok(Value::Integer(i));
}
}
Ok(Value::Number(num))
}
}
}
fn decode_date(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let date = decode_oracle_date(&data)?;
Ok(Value::Date(date))
}
}
}
fn decode_timestamp(&self, buf: &mut ReadBuffer, _with_tz: bool) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let ts = decode_oracle_timestamp(&data)?;
Ok(Value::Timestamp(ts))
}
}
}
fn decode_raw(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => Ok(Value::Bytes(data)),
}
}
fn decode_binary_float(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let f = decode_binary_float(&data);
Ok(Value::Float(f as f64))
}
}
}
fn decode_binary_double(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let f = decode_binary_double(&data);
Ok(Value::Float(f))
}
}
}
fn decode_rowid(&self, buf: &mut ReadBuffer) -> Result<Value> {
let length = buf.read_u8()?;
if length == 0 || length == length::NULL_INDICATOR {
return Ok(Value::Null);
}
let rba = buf.read_ub4()?;
let partition_id = buf.read_ub2()?;
buf.skip(1)?; let block_num = buf.read_ub4()?;
let slot_num = buf.read_ub2()?;
let rowid = RowId::new(rba, partition_id as u16, block_num, slot_num as u16);
Ok(Value::RowId(rowid))
}
fn decode_urowid(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
if data.is_empty() {
return Ok(Value::Null);
}
if data[0] == 1 && data.len() >= 13 {
let rowid = decode_rowid(&data)?;
Ok(Value::RowId(rowid))
} else {
let s = String::from_utf8_lossy(&data[1..]).to_string();
Ok(Value::String(s))
}
}
}
}
fn decode_boolean(&self, buf: &mut ReadBuffer) -> Result<Value> {
match self.read_oracle_slice(buf)? {
None => Ok(Value::Null),
Some(data) => {
let b = data.last().copied().unwrap_or(0) == 1;
Ok(Value::Boolean(b))
}
}
}
}
pub fn parse_row_header(buf: &mut ReadBuffer) -> Result<Option<Vec<u8>>> {
buf.skip(1)?; buf.skip_ub2()?; buf.skip_ub4()?; buf.skip_ub4()?; buf.skip_ub2()?;
let bit_vector_len = buf.read_ub4()? as usize;
let bit_vector = if bit_vector_len > 0 {
buf.skip(1)?; let data = buf.read_bytes_vec(bit_vector_len - 1)?;
Some(data)
} else {
None
};
let rxhrid_len = buf.read_ub4()? as usize;
if rxhrid_len > 0 {
loop {
let chunk_len = buf.read_ub4()? as usize;
if chunk_len == 0 {
break;
}
buf.skip(chunk_len)?;
}
}
Ok(bit_vector)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::buffer::ReadBuffer;
#[test]
fn test_value_null() {
let v = Value::Null;
assert!(v.is_null());
assert!(v.as_str().is_none());
assert!(v.as_i64().is_none());
}
#[test]
fn test_value_string() {
let v = Value::String("hello".to_string());
assert!(!v.is_null());
assert_eq!(v.as_str(), Some("hello"));
assert_eq!(format!("{}", v), "hello");
}
#[test]
fn test_value_integer() {
let v = Value::Integer(42);
assert_eq!(v.as_i64(), Some(42));
assert_eq!(v.as_f64(), Some(42.0));
assert_eq!(format!("{}", v), "42");
}
#[test]
fn test_value_float() {
let v = Value::Float(3.14);
assert!((v.as_f64().unwrap() - 3.14).abs() < 0.001);
assert_eq!(v.as_i64(), Some(3));
}
#[test]
fn test_value_boolean() {
let v_true = Value::Boolean(true);
let v_false = Value::Boolean(false);
assert_eq!(v_true.as_bool(), Some(true));
assert_eq!(v_false.as_bool(), Some(false));
}
#[test]
fn test_row_creation() {
let values = vec![
Value::String("test".to_string()),
Value::Integer(123),
Value::Null,
];
let row = Row::new(values);
assert_eq!(row.len(), 3);
assert!(!row.is_empty());
assert_eq!(row.get_string(0), Some("test"));
assert_eq!(row.get_i64(1), Some(123));
assert!(row.is_null(2));
}
#[test]
fn test_row_with_names() {
let values = vec![Value::Integer(1), Value::String("hello".to_string())];
let names = vec!["ID".to_string(), "NAME".to_string()];
let row = Row::with_names(values, names);
assert_eq!(row.get_by_name("ID").and_then(Value::as_i64), Some(1));
assert_eq!(row.get_by_name("name").and_then(Value::as_str), Some("hello"));
assert!(row.get_by_name("nonexistent").is_none());
}
#[test]
fn test_row_index() {
let values = vec![Value::Integer(42)];
let row = Row::new(values);
assert!(matches!(&row[0], Value::Integer(42)));
}
fn make_column(name: &str, oracle_type: OracleType, buffer_size: u32) -> ColumnInfo {
ColumnInfo {
name: name.to_string(),
oracle_type,
data_size: buffer_size,
buffer_size,
precision: 0,
scale: 0,
nullable: true,
csfrm: 0,
type_schema: None,
type_name: None,
domain_schema: None,
domain_name: None,
is_json: false,
is_oson: false,
vector_dimensions: None,
vector_format: None,
element_type: None,
}
}
#[test]
fn test_decode_null_value() {
let columns = vec![make_column("TEST", OracleType::Varchar, 100)];
let decoder = RowDataDecoder::new(&columns);
let data = vec![255u8]; let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
assert!(value.is_null());
}
#[test]
fn test_decode_string_value() {
let columns = vec![make_column("TEST", OracleType::Varchar, 100)];
let decoder = RowDataDecoder::new(&columns);
let data = vec![5u8, b'h', b'e', b'l', b'l', b'o'];
let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
assert_eq!(value.as_str(), Some("hello"));
}
#[test]
fn test_decode_integer_value() {
let columns = vec![make_column("NUM", OracleType::Number, 22)];
let decoder = RowDataDecoder::new(&columns);
let data = vec![3u8, 0xc2, 0x02, 0x18];
let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
assert_eq!(value.as_i64(), Some(123));
}
#[test]
fn test_bit_vector_duplicate_detection() {
let columns = vec![
make_column("COL1", OracleType::Number, 22),
make_column("COL2", OracleType::Number, 22),
];
let mut decoder = RowDataDecoder::new(&columns);
decoder.set_bit_vector(vec![0b00000001]);
assert!(!decoder.is_duplicate(0)); assert!(decoder.is_duplicate(1)); }
#[test]
fn test_value_display() {
assert_eq!(format!("{}", Value::Null), "NULL");
assert_eq!(format!("{}", Value::Integer(42)), "42");
assert_eq!(format!("{}", Value::Float(3.14)), "3.14");
assert_eq!(format!("{}", Value::String("test".into())), "test");
assert_eq!(format!("{}", Value::Boolean(true)), "true");
assert_eq!(format!("{}", Value::Bytes(vec![1, 2, 3])), "<3 bytes>");
}
#[test]
fn test_decode_binary_float() {
let columns = vec![make_column("FLOAT_COL", OracleType::BinaryFloat, 4)];
let decoder = RowDataDecoder::new(&columns);
let encoded = crate::types::encode_binary_float(1.0f32);
let mut data = vec![4u8]; data.extend_from_slice(&encoded);
let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
assert!((value.as_f64().unwrap() - 1.0).abs() < 0.0001);
}
#[test]
fn test_decode_binary_double() {
let columns = vec![make_column("DOUBLE_COL", OracleType::BinaryDouble, 8)];
let decoder = RowDataDecoder::new(&columns);
let encoded = crate::types::encode_binary_double(3.14159f64);
let mut data = vec![8u8]; data.extend_from_slice(&encoded);
let mut buf = ReadBuffer::new(bytes::Bytes::from(data));
let value = decoder.decode_column_value(&mut buf, &columns[0]).unwrap();
assert!((value.as_f64().unwrap() - 3.14159).abs() < 0.00001);
}
}