#![forbid(unsafe_code)]
use super::*;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AcceptInfo {
pub protocol_version: u16,
pub protocol_options: u16,
pub sdu: u32,
pub supports_fast_auth: bool,
pub supports_oob_check: bool,
pub supports_oob: bool,
pub supports_end_of_response: bool,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct AuthResponse {
pub session_data: BTreeMap<String, String>,
pub verifier_type: Option<u32>,
pub capabilities: Option<ClientCapabilities>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ClientCapabilities {
pub ttc_field_version: u8,
pub max_string_size: u32,
pub charset_id: u16,
}
impl Default for ClientCapabilities {
fn default() -> Self {
Self {
ttc_field_version: 24,
max_string_size: 32_767,
charset_id: 873,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ColumnMetadata {
pub(crate) name: String,
pub(crate) ora_type_num: u8,
pub(crate) csfrm: u8,
pub(crate) precision: i8,
pub(crate) scale: i8,
pub(crate) buffer_size: u32,
pub(crate) max_size: u32,
pub(crate) nulls_allowed: bool,
pub(crate) is_json: bool,
pub(crate) is_oson: bool,
pub(crate) object_schema: Option<String>,
pub(crate) object_type_name: Option<String>,
pub(crate) is_array: bool,
pub(crate) vector_dimensions: Option<u32>,
pub(crate) vector_format: u8,
pub(crate) vector_flags: u8,
pub(crate) domain_schema: Option<String>,
pub(crate) domain_name: Option<String>,
pub(crate) annotations: Option<Vec<(String, String)>>,
}
impl ColumnMetadata {
pub fn new(name: impl Into<String>, ora_type_num: u8) -> Self {
Self {
name: name.into(),
ora_type_num,
..Self::default()
}
}
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
pub fn ora_type_num(&self) -> u8 {
self.ora_type_num
}
#[must_use]
pub fn with_ora_type_num(mut self, ora_type_num: u8) -> Self {
self.ora_type_num = ora_type_num;
self
}
pub fn csfrm(&self) -> u8 {
self.csfrm
}
#[must_use]
pub fn with_csfrm(mut self, csfrm: u8) -> Self {
self.csfrm = csfrm;
self
}
pub fn precision(&self) -> i8 {
self.precision
}
#[must_use]
pub fn with_precision(mut self, precision: i8) -> Self {
self.precision = precision;
self
}
pub fn scale(&self) -> i8 {
self.scale
}
#[must_use]
pub fn with_scale(mut self, scale: i8) -> Self {
self.scale = scale;
self
}
pub fn buffer_size(&self) -> u32 {
self.buffer_size
}
#[must_use]
pub fn with_buffer_size(mut self, buffer_size: u32) -> Self {
self.buffer_size = buffer_size;
self
}
pub fn max_size(&self) -> u32 {
self.max_size
}
#[must_use]
pub fn with_max_size(mut self, max_size: u32) -> Self {
self.max_size = max_size;
self
}
pub fn nulls_allowed(&self) -> bool {
self.nulls_allowed
}
#[must_use]
pub fn with_nulls_allowed(mut self, nulls_allowed: bool) -> Self {
self.nulls_allowed = nulls_allowed;
self
}
pub fn is_json(&self) -> bool {
self.is_json
}
#[must_use]
pub fn with_is_json(mut self, is_json: bool) -> Self {
self.is_json = is_json;
self
}
pub fn is_oson(&self) -> bool {
self.is_oson
}
#[must_use]
pub fn with_is_oson(mut self, is_oson: bool) -> Self {
self.is_oson = is_oson;
self
}
pub fn object_schema(&self) -> Option<&str> {
self.object_schema.as_deref()
}
#[must_use]
pub fn with_object_schema(mut self, object_schema: Option<String>) -> Self {
self.object_schema = object_schema;
self
}
pub fn object_type_name(&self) -> Option<&str> {
self.object_type_name.as_deref()
}
#[must_use]
pub fn with_object_type_name(mut self, object_type_name: Option<String>) -> Self {
self.object_type_name = object_type_name;
self
}
pub fn is_array(&self) -> bool {
self.is_array
}
#[must_use]
pub fn with_is_array(mut self, is_array: bool) -> Self {
self.is_array = is_array;
self
}
pub fn vector_dimensions(&self) -> Option<u32> {
self.vector_dimensions
}
#[must_use]
pub fn with_vector_dimensions(mut self, vector_dimensions: Option<u32>) -> Self {
self.vector_dimensions = vector_dimensions;
self
}
pub fn vector_format(&self) -> u8 {
self.vector_format
}
#[must_use]
pub fn with_vector_format(mut self, vector_format: u8) -> Self {
self.vector_format = vector_format;
self
}
pub fn vector_flags(&self) -> u8 {
self.vector_flags
}
#[must_use]
pub fn with_vector_flags(mut self, vector_flags: u8) -> Self {
self.vector_flags = vector_flags;
self
}
pub fn domain_schema(&self) -> Option<&str> {
self.domain_schema.as_deref()
}
#[must_use]
pub fn with_domain_schema(mut self, domain_schema: Option<String>) -> Self {
self.domain_schema = domain_schema;
self
}
pub fn domain_name(&self) -> Option<&str> {
self.domain_name.as_deref()
}
#[must_use]
pub fn with_domain_name(mut self, domain_name: Option<String>) -> Self {
self.domain_name = domain_name;
self
}
pub fn annotations(&self) -> Option<&[(String, String)]> {
self.annotations.as_deref()
}
#[must_use]
pub fn with_annotations(mut self, annotations: Option<Vec<(String, String)>>) -> Self {
self.annotations = annotations;
self
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BindTypeInfo {
pub ora_type_num: u8,
pub csfrm: u8,
pub buffer_size: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CursorValue {
pub columns: Vec<ColumnMetadata>,
pub cursor_id: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectValue {
pub schema: Option<String>,
pub type_name: Option<String>,
pub packed_data: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LobValue {
pub ora_type_num: u8,
pub csfrm: u8,
pub locator: Vec<u8>,
pub size: u64,
pub chunk_size: u32,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum QueryValue {
Text(String),
TextRaw {
bytes: Vec<u8>,
csfrm: u8,
},
Raw(Vec<u8>),
Rowid(String),
BinaryDouble(String),
IntervalDS {
days: i32,
hours: i32,
minutes: i32,
seconds: i32,
fseconds: i32,
},
IntervalYM {
years: i32,
months: i32,
},
Number(OracleNumber),
Boolean(bool),
Cursor(Box<CursorValue>),
DateTime {
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
},
Object(Box<ObjectValue>),
Lob(Box<LobValue>),
Vector(Box<crate::vector::Vector>),
Json(Box<crate::oson::OsonValue>),
Array(Vec<Option<QueryValue>>),
}
const _: () = assert!(core::mem::size_of::<QueryValue>() <= 32);
const _: () = assert!(core::mem::size_of::<OracleNumber>() <= 24);
impl QueryValue {
pub fn number_from_text(text: &str, is_integer: bool) -> Self {
QueryValue::Number(OracleNumber::from_canonical_text_with_flag(
text, is_integer,
))
}
pub fn as_text(&self) -> Option<&str> {
match self {
QueryValue::Text(value) => Some(value.as_str()),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
QueryValue::Number(num) => num
.to_i64()
.or_else(|| num.to_canonical_string().parse::<i64>().ok()),
QueryValue::Boolean(value) => Some(i64::from(*value)),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
QueryValue::Number(num) => num.to_canonical_string().parse::<f64>().ok(),
QueryValue::BinaryDouble(text) => text.parse::<f64>().ok(),
_ => None,
}
}
pub fn as_number_text(&self) -> Option<std::borrow::Cow<'_, str>> {
match self {
QueryValue::Number(num) => Some(num.to_canonical_cow()),
_ => None,
}
}
pub fn as_number(&self) -> Option<&OracleNumber> {
match self {
QueryValue::Number(num) => Some(num),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
QueryValue::Boolean(value) => Some(*value),
_ => None,
}
}
pub fn as_raw(&self) -> Option<&[u8]> {
match self {
QueryValue::Raw(bytes) => Some(bytes.as_slice()),
_ => None,
}
}
pub fn as_rowid(&self) -> Option<&str> {
match self {
QueryValue::Rowid(value) => Some(value.as_str()),
_ => None,
}
}
pub fn variant_name(&self) -> &'static str {
match self {
QueryValue::Text(_) => "Text",
QueryValue::TextRaw { .. } => "TextRaw",
QueryValue::Raw(_) => "Raw",
QueryValue::Rowid(_) => "Rowid",
QueryValue::BinaryDouble(_) => "BinaryDouble",
QueryValue::IntervalDS { .. } => "IntervalDS",
QueryValue::IntervalYM { .. } => "IntervalYM",
QueryValue::Number(_) => "Number",
QueryValue::Boolean(_) => "Boolean",
QueryValue::Cursor(_) => "Cursor",
QueryValue::DateTime { .. } => "DateTime",
QueryValue::Object(_) => "Object",
QueryValue::Lob(_) => "Lob",
QueryValue::Vector(_) => "Vector",
QueryValue::Json(_) => "Json",
QueryValue::Array(_) => "Array",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum QueryValueRef<'buf> {
Text(&'buf str),
Raw(&'buf [u8]),
Number {
text: &'buf str,
is_integer: bool,
},
Boolean(bool),
IntervalDS {
days: i32,
hours: i32,
minutes: i32,
seconds: i32,
fseconds: i32,
},
IntervalYM {
years: i32,
months: i32,
},
DateTime {
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
},
Owned(&'buf QueryValue),
}
impl QueryValueRef<'_> {
pub fn to_owned_value(&self) -> QueryValue {
match *self {
QueryValueRef::Text(text) => QueryValue::Text(text.to_string()),
QueryValueRef::Raw(bytes) => QueryValue::Raw(bytes.to_vec()),
QueryValueRef::Number { text, is_integer } => QueryValue::Number(
OracleNumber::from_canonical_text_with_flag(text, is_integer),
),
QueryValueRef::Boolean(value) => QueryValue::Boolean(value),
QueryValueRef::IntervalDS {
days,
hours,
minutes,
seconds,
fseconds,
} => QueryValue::IntervalDS {
days,
hours,
minutes,
seconds,
fseconds,
},
QueryValueRef::IntervalYM { years, months } => QueryValue::IntervalYM { years, months },
QueryValueRef::DateTime {
year,
month,
day,
hour,
minute,
second,
nanosecond,
} => QueryValue::DateTime {
year,
month,
day,
hour,
minute,
second,
nanosecond,
},
QueryValueRef::Owned(value) => value.clone(),
}
}
pub fn as_text(&self) -> Option<&str> {
match self {
QueryValueRef::Text(value) => Some(value),
QueryValueRef::Owned(QueryValue::Text(value)) => Some(value.as_str()),
_ => None,
}
}
pub fn as_number_text(&self) -> Option<&str> {
match self {
QueryValueRef::Number { text, .. } => Some(text),
QueryValueRef::Owned(QueryValue::Number(num)) => num.as_borrowed_text(),
_ => None,
}
}
pub fn as_raw(&self) -> Option<&[u8]> {
match self {
QueryValueRef::Raw(bytes) => Some(bytes),
QueryValueRef::Owned(QueryValue::Raw(bytes)) => Some(bytes.as_slice()),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum BindValue {
Null,
TypedNull {
ora_type_num: u8,
csfrm: u8,
buffer_size: u32,
},
Output {
ora_type_num: u8,
csfrm: u8,
buffer_size: u32,
},
ReturnOutput {
ora_type_num: u8,
csfrm: u8,
buffer_size: u32,
},
ObjectOutput {
schema: String,
type_name: String,
oid: Vec<u8>,
version: u32,
buffer_size: u32,
is_return: bool,
},
ObjectInput {
schema: String,
type_name: String,
oid: Vec<u8>,
version: u32,
image: Vec<u8>,
buffer_size: u32,
},
Text(String),
Raw(Vec<u8>),
Lob {
ora_type_num: u8,
csfrm: u8,
locator: Vec<u8>,
},
Number(String),
BinaryInteger(String),
BinaryDouble(f64),
BinaryFloat(f64),
Boolean(bool),
IntervalDS {
days: i32,
seconds: i32,
microseconds: i32,
},
IntervalYM {
years: i32,
months: i32,
},
DateTime {
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
},
Timestamp {
ora_type_num: u8,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
},
Array {
ora_type_num: u8,
csfrm: u8,
buffer_size: u32,
max_elements: u32,
values: Vec<Option<BindValue>>,
},
Vector(crate::vector::Vector),
Json(Vec<u8>),
Cursor {
cursor_id: u32,
},
}
impl BindValue {
pub fn variant_name(&self) -> &'static str {
match self {
BindValue::Null => "Null",
BindValue::TypedNull { .. } => "TypedNull",
BindValue::Output { .. } => "Output",
BindValue::ReturnOutput { .. } => "ReturnOutput",
BindValue::ObjectOutput { .. } => "ObjectOutput",
BindValue::ObjectInput { .. } => "ObjectInput",
BindValue::Text(_) => "Text",
BindValue::Raw(_) => "Raw",
BindValue::Lob { .. } => "Lob",
BindValue::Number(_) => "Number",
BindValue::BinaryInteger(_) => "BinaryInteger",
BindValue::BinaryDouble(_) => "BinaryDouble",
BindValue::BinaryFloat(_) => "BinaryFloat",
BindValue::Boolean(_) => "Boolean",
BindValue::IntervalDS { .. } => "IntervalDS",
BindValue::IntervalYM { .. } => "IntervalYM",
BindValue::DateTime { .. } => "DateTime",
BindValue::Timestamp { .. } => "Timestamp",
BindValue::Array { .. } => "Array",
BindValue::Vector(_) => "Vector",
BindValue::Json(_) => "Json",
BindValue::Cursor { .. } => "Cursor",
}
}
pub fn is_null(&self) -> bool {
matches!(self, BindValue::Null)
}
pub fn as_str(&self) -> Option<&str> {
match self {
BindValue::Text(text) => Some(text.as_str()),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
BindValue::Raw(bytes) => Some(bytes.as_slice()),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
BindValue::Boolean(value) => Some(*value),
_ => None,
}
}
pub fn as_number_text(&self) -> Option<&str> {
match self {
BindValue::Number(text) | BindValue::BinaryInteger(text) => Some(text.as_str()),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
BindValue::BinaryDouble(value) | BindValue::BinaryFloat(value) => Some(*value),
BindValue::Number(text) | BindValue::BinaryInteger(text) => text.parse::<f64>().ok(),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
BindValue::Number(text) | BindValue::BinaryInteger(text) => text.parse::<i64>().ok(),
BindValue::Boolean(value) => Some(i64::from(*value)),
_ => None,
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct QueryResult {
pub columns: Vec<ColumnMetadata>,
pub rows: Vec<Vec<Option<QueryValue>>>,
pub out_values: Vec<(usize, Option<QueryValue>)>,
pub return_values: Vec<(usize, Vec<Option<QueryValue>>)>,
pub cursor_id: u32,
pub row_count: u64,
pub more_rows: bool,
pub compilation_error_warning: bool,
pub last_rowid: Option<String>,
pub batch_errors: Vec<BatchServerError>,
pub array_dml_row_counts: Option<Vec<u64>>,
pub implicit_resultsets: Option<Vec<QueryValue>>,
pub token_num: Option<u64>,
pub sessionless_txn_state: Option<SessionlessTxnState>,
pub query_id: Option<u64>,
pub txn_in_progress: Option<bool>,
}
impl QueryResult {
pub fn cell(&self, row: usize, col: usize) -> Option<&QueryValue> {
self.rows.get(row)?.get(col)?.as_ref()
}
pub fn column_index(&self, name: &str) -> Option<usize> {
self.columns
.iter()
.position(|col| col.name.eq_ignore_ascii_case(name))
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LobReadResult {
pub data: Option<Vec<u8>>,
pub locator: Vec<u8>,
pub amount: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SessionlessTxnState {
Set { started_on_server: bool },
Unset,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TpcSwitchResponse {
pub context: Vec<u8>,
pub txn_in_progress: bool,
pub sessionless_state: Option<SessionlessTxnState>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TpcChangeStateResponse {
pub state: u32,
pub txn_in_progress: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExecuteOptions {
pub(crate) batcherrors: bool,
pub(crate) arraydmlrowcounts: bool,
pub(crate) parse_only: bool,
pub(crate) token_num: u64,
pub(crate) cursor_id: u32,
pub(crate) cache_statement: bool,
pub(crate) scrollable: bool,
pub(crate) fetch_orientation: u32,
pub(crate) fetch_pos: u32,
pub(crate) scroll_operation: bool,
pub(crate) suspend_on_success: bool,
pub(crate) no_prefetch: bool,
pub(crate) registration_id: u64,
pub(crate) max_string_size: u32,
}
impl Default for ExecuteOptions {
fn default() -> Self {
Self {
batcherrors: false,
arraydmlrowcounts: false,
parse_only: false,
token_num: 0,
cursor_id: 0,
cache_statement: true,
scrollable: false,
fetch_orientation: 0,
fetch_pos: 0,
scroll_operation: false,
suspend_on_success: false,
no_prefetch: false,
registration_id: 0,
max_string_size: 32_767,
}
}
}
impl ExecuteOptions {
pub fn batcherrors(&self) -> bool {
self.batcherrors
}
#[must_use]
pub fn with_batcherrors(mut self, enabled: bool) -> Self {
self.batcherrors = enabled;
self
}
pub fn arraydmlrowcounts(&self) -> bool {
self.arraydmlrowcounts
}
#[must_use]
pub fn with_arraydmlrowcounts(mut self, enabled: bool) -> Self {
self.arraydmlrowcounts = enabled;
self
}
pub fn parse_only(&self) -> bool {
self.parse_only
}
#[must_use]
pub fn with_parse_only(mut self, enabled: bool) -> Self {
self.parse_only = enabled;
self
}
pub fn token_num(&self) -> u64 {
self.token_num
}
#[must_use]
pub fn with_token_num(mut self, token_num: u64) -> Self {
self.token_num = token_num;
self
}
pub fn cursor_id(&self) -> u32 {
self.cursor_id
}
#[must_use]
pub fn with_cursor_id(mut self, cursor_id: u32) -> Self {
self.cursor_id = cursor_id;
self
}
pub fn cache_statement(&self) -> bool {
self.cache_statement
}
#[must_use]
pub fn with_cache_statement(mut self, enabled: bool) -> Self {
self.cache_statement = enabled;
self
}
pub fn scrollable(&self) -> bool {
self.scrollable
}
#[must_use]
pub fn with_scrollable(mut self, enabled: bool) -> Self {
self.scrollable = enabled;
self
}
pub fn fetch_orientation(&self) -> u32 {
self.fetch_orientation
}
#[must_use]
pub fn with_fetch_orientation(mut self, fetch_orientation: u32) -> Self {
self.fetch_orientation = fetch_orientation;
self
}
pub fn fetch_pos(&self) -> u32 {
self.fetch_pos
}
#[must_use]
pub fn with_fetch_pos(mut self, fetch_pos: u32) -> Self {
self.fetch_pos = fetch_pos;
self
}
pub fn scroll_operation(&self) -> bool {
self.scroll_operation
}
#[must_use]
pub fn with_scroll_operation(mut self, enabled: bool) -> Self {
self.scroll_operation = enabled;
self
}
pub fn suspend_on_success(&self) -> bool {
self.suspend_on_success
}
#[must_use]
pub fn with_suspend_on_success(mut self, enabled: bool) -> Self {
self.suspend_on_success = enabled;
self
}
pub fn no_prefetch(&self) -> bool {
self.no_prefetch
}
#[must_use]
pub fn with_no_prefetch(mut self, enabled: bool) -> Self {
self.no_prefetch = enabled;
self
}
pub fn registration_id(&self) -> u64 {
self.registration_id
}
#[must_use]
pub fn with_registration_id(mut self, registration_id: u64) -> Self {
self.registration_id = registration_id;
self
}
pub fn max_string_size(&self) -> u32 {
self.max_string_size
}
#[must_use]
pub fn with_max_string_size(mut self, max_string_size: u32) -> Self {
self.max_string_size = max_string_size;
self
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BatchServerError {
pub(crate) code: u32,
pub(crate) offset: u32,
pub(crate) message: String,
}
impl BatchServerError {
pub fn new(code: u32, offset: u32, message: impl Into<String>) -> Self {
Self {
code,
offset,
message: message.into(),
}
}
pub fn code(&self) -> u32 {
self.code
}
pub fn offset(&self) -> u32 {
self.offset
}
pub fn message(&self) -> &str {
&self.message
}
pub fn into_parts(self) -> (u32, u32, String) {
(self.code, self.offset, self.message)
}
}
#[cfg(test)]
mod accessor_tests {
use super::*;
#[test]
fn query_value_typed_accessors() {
let text = QueryValue::Text("hello".to_string());
assert_eq!(text.as_text(), Some("hello"));
assert_eq!(text.as_i64(), None);
let int = QueryValue::number_from_text("42", true);
assert_eq!(int.as_i64(), Some(42));
assert_eq!(int.as_f64(), Some(42.0));
assert_eq!(int.as_number_text().as_deref(), Some("42"));
let dbl = QueryValue::BinaryDouble("2.5".to_string());
assert_eq!(dbl.as_f64(), Some(2.5));
let boolean = QueryValue::Boolean(true);
assert_eq!(boolean.as_bool(), Some(true));
assert_eq!(boolean.as_i64(), Some(1));
let raw = QueryValue::Raw(vec![0xDE, 0xAD]);
assert_eq!(raw.as_raw(), Some([0xDE, 0xAD].as_slice()));
let rowid = QueryValue::Rowid("AAAR".to_string());
assert_eq!(rowid.as_rowid(), Some("AAAR"));
}
#[test]
fn query_result_cell_and_column_index() {
let result = QueryResult {
columns: vec![
ColumnMetadata {
name: "ID".to_string(),
..ColumnMetadata::default()
},
ColumnMetadata {
name: "NAME".to_string(),
..ColumnMetadata::default()
},
],
rows: vec![vec![Some(QueryValue::number_from_text("7", true)), None]],
..QueryResult::default()
};
assert_eq!(result.cell(0, 0).and_then(QueryValue::as_i64), Some(7));
assert!(result.cell(0, 1).is_none(), "NULL cell is None");
assert!(result.cell(1, 0).is_none(), "out-of-range row is None");
assert_eq!(result.column_index("name"), Some(1));
assert_eq!(result.column_index("missing"), None);
}
}
#[cfg(test)]
mod query_value_ref_tests {
use super::*;
#[test]
fn borrowed_scalars_to_owned_equal_owned_values() {
let buf = String::from("héllo");
let text = QueryValueRef::Text(buf.as_str());
assert_eq!(text.to_owned_value(), QueryValue::Text("héllo".to_string()));
let raw_buf = [0xDEu8, 0xAD, 0xBE, 0xEF];
let raw = QueryValueRef::Raw(&raw_buf);
assert_eq!(
raw.to_owned_value(),
QueryValue::Raw(vec![0xDE, 0xAD, 0xBE, 0xEF])
);
let num_buf = String::from("-12.5");
let number = QueryValueRef::Number {
text: num_buf.as_str(),
is_integer: false,
};
assert_eq!(
number.to_owned_value(),
QueryValue::number_from_text("-12.5", false)
);
let boolean = QueryValueRef::Boolean(true);
assert_eq!(boolean.to_owned_value(), QueryValue::Boolean(true));
let ds = QueryValueRef::IntervalDS {
days: 1,
hours: 2,
minutes: 3,
seconds: 4,
fseconds: 5,
};
assert_eq!(
ds.to_owned_value(),
QueryValue::IntervalDS {
days: 1,
hours: 2,
minutes: 3,
seconds: 4,
fseconds: 5,
}
);
}
#[test]
fn query_value_ref_is_small_and_copy() {
const fn is_copy<T: Copy>() {}
is_copy::<QueryValueRef<'static>>();
assert!(core::mem::size_of::<QueryValueRef<'static>>() <= 32);
}
}