From e1688bc2d1c56fb9a8f68db5ac1383a9757e11ea Mon Sep 17 00:00:00 2001 From: Gor Stepanyan Date: Fri, 8 Jul 2022 19:09:22 +0200 Subject: [PATCH] Add row iterator implementation CassIterator struct modified to simulate iterator hierarchy in C++ dirver. Added RowIterator implementation with necessary functions to get column value and type. --- .github/workflows/build.yml | 2 +- scylla-rust-wrapper/src/cass_types.rs | 54 ++++++ scylla-rust-wrapper/src/external.rs | 9 + scylla-rust-wrapper/src/lib.rs | 1 + scylla-rust-wrapper/src/query_result.rs | 247 +++++++++++++++++------- scylla-rust-wrapper/src/session.rs | 16 +- src/testing_unimplemented.cpp | 20 -- 7 files changed, 252 insertions(+), 97 deletions(-) create mode 100644 scylla-rust-wrapper/src/external.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 292071c3..25b90fee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,4 +33,4 @@ jobs: run: cmake -DCASS_BUILD_INTEGRATION_TESTS=ON . && make - name: Run integration tests on Scylla 5.0.0 - run: valgrind --error-exitcode=123 ./cassandra-integration-tests --version=release:5.0.0 --category=CASSANDRA --verbose=ccm --gtest_filter="ClusterTests.*" + run: valgrind --error-exitcode=123 ./cassandra-integration-tests --version=release:5.0.0 --category=CASSANDRA --verbose=ccm --gtest_filter="ClusterTests.*:BasicsTests.*RowsInRowsOut" diff --git a/scylla-rust-wrapper/src/cass_types.rs b/scylla-rust-wrapper/src/cass_types.rs index 836fbcf6..b0797361 100644 --- a/scylla-rust-wrapper/src/cass_types.rs +++ b/scylla-rust-wrapper/src/cass_types.rs @@ -1,6 +1,7 @@ use crate::argconv::*; use crate::cass_error::CassError; use crate::types::*; +use scylla::frame::response::result::ColumnType; use std::os::raw::c_char; use std::ptr; use std::sync::Arc; @@ -128,6 +129,59 @@ impl CassDataType { } } +pub fn get_column_type(column_type: &ColumnType) -> CassDataType { + match column_type { + ColumnType::Custom(s) => CassDataType::Custom((*s).clone()), + ColumnType::Ascii => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_ASCII), + ColumnType::Boolean => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_BOOLEAN), + ColumnType::Blob => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_BLOB), + ColumnType::Counter => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_COUNTER), + ColumnType::Decimal => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_DECIMAL), + ColumnType::Date => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_DATE), + ColumnType::Double => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_DOUBLE), + ColumnType::Float => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_FLOAT), + ColumnType::Int => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_INT), + ColumnType::BigInt => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_BIGINT), + ColumnType::Text => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_TEXT), + ColumnType::Timestamp => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_TIMESTAMP), + ColumnType::Inet => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_INET), + ColumnType::List(boxed_type) => { + CassDataType::List(Some(Arc::new(get_column_type(boxed_type.as_ref())))) + } + ColumnType::Map(key, value) => CassDataType::Map( + Some(Arc::new(get_column_type(key.as_ref()))), + Some(Arc::new(get_column_type(value.as_ref()))), + ), + ColumnType::Set(boxed_type) => { + CassDataType::Set(Some(Arc::new(get_column_type(boxed_type.as_ref())))) + } + ColumnType::UserDefinedType { + type_name, + keyspace, + field_types, + } => CassDataType::UDT(UDTDataType { + field_types: field_types + .iter() + .map(|(name, col_type)| ((*name).clone(), Arc::new(get_column_type(col_type)))) + .collect(), + keyspace: (*keyspace).clone(), + name: (*type_name).clone(), + }), + ColumnType::SmallInt => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_SMALL_INT), + ColumnType::TinyInt => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_TINY_INT), + ColumnType::Time => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_TIME), + ColumnType::Timeuuid => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_TIMEUUID), + ColumnType::Tuple(v) => CassDataType::Tuple( + v.iter() + .map(|col_type| Arc::new(get_column_type(col_type))) + .collect(), + ), + ColumnType::Uuid => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_UUID), + ColumnType::Varint => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_VARINT), + _ => CassDataType::Value(CassValueType::CASS_VALUE_TYPE_UNKNOWN), + } +} + // Changed return type to const ptr - Arc::into_raw is const. // It's probably not a good idea - but cppdriver doesn't guarantee // thread safety apart from CassSession and CassFuture. diff --git a/scylla-rust-wrapper/src/external.rs b/scylla-rust-wrapper/src/external.rs new file mode 100644 index 00000000..1bf30937 --- /dev/null +++ b/scylla-rust-wrapper/src/external.rs @@ -0,0 +1,9 @@ +use crate::cass_error::*; +use std::os::raw::c_char; + +#[no_mangle] +pub unsafe extern "C" fn cass_error_desc(_error: *const CassError) -> *const c_char { + // FIXME: add proper implementation + let error = "my_custom_error\0"; + error.as_ptr() as *const c_char +} diff --git a/scylla-rust-wrapper/src/lib.rs b/scylla-rust-wrapper/src/lib.rs index daff58f9..a998e609 100644 --- a/scylla-rust-wrapper/src/lib.rs +++ b/scylla-rust-wrapper/src/lib.rs @@ -9,6 +9,7 @@ pub mod cass_error; pub mod cass_types; pub mod cluster; pub mod collection; +mod external; pub mod future; pub mod inet; pub mod prepared; diff --git a/scylla-rust-wrapper/src/query_result.rs b/scylla-rust-wrapper/src/query_result.rs index 4b0de581..65860c65 100644 --- a/scylla-rust-wrapper/src/query_result.rs +++ b/scylla-rust-wrapper/src/query_result.rs @@ -1,5 +1,6 @@ use crate::argconv::*; use crate::cass_error::CassError; +use crate::cass_types::{cass_data_type_type, CassDataType, CassValueType}; use crate::inet::CassInet; use crate::types::*; use crate::uuid::CassUuid; @@ -21,42 +22,31 @@ pub struct CassResultData { pub type CassResult_ = Arc; -pub struct CassIterator { - result: CassResult_, - position: Option, -} +pub type CassRow_ = &'static CassRow; pub struct CassRow { pub columns: Vec, pub result_metadata: Arc, } -pub type CassValue = Option; - -#[no_mangle] -pub unsafe extern "C" fn cass_iterator_from_result( - result_raw: *const CassResult, -) -> *mut CassIterator { - let result: CassResult_ = clone_arced(result_raw); - - let iterator = CassIterator { - result, - position: None, - }; +pub struct CassValue { + pub value: Option, + pub value_type: CassDataType, +} - Box::into_raw(Box::new(iterator)) +pub struct CassResultIterator { + result: CassResult_, + position: Option, } -// This was const for some reason, seems like a mistake in cpp driver -#[no_mangle] -pub unsafe extern "C" fn cass_result_free(result_raw: *mut CassResult) { - free_arced(result_raw); +pub struct CassRowIterator { + row: CassRow_, + position: Option, } -#[no_mangle] -pub unsafe extern "C" fn cass_result_has_more_pages(result: *const CassResult) -> cass_bool_t { - let result = ptr_to_ref(result); - result.metadata.paging_state.is_some() as cass_bool_t +pub enum CassIterator { + CassResultIterator(CassResultIterator), + CassRowIterator(CassRowIterator), } #[no_mangle] @@ -67,41 +57,113 @@ pub unsafe extern "C" fn cass_iterator_free(iterator: *mut CassIterator) { // After creating an iterator we have to call next() before accessing the value #[no_mangle] pub unsafe extern "C" fn cass_iterator_next(iterator: *mut CassIterator) -> cass_bool_t { - let iter: &mut CassIterator = ptr_to_ref_mut(iterator); + let mut iter = ptr_to_ref_mut(iterator); - let new_pos: usize = match iter.position { - Some(prev_pos) => prev_pos + 1, - None => 0, - }; + match &mut iter { + CassIterator::CassResultIterator(result_iterator) => { + let new_pos: usize = result_iterator.position.map_or(0, |prev_pos| prev_pos + 1); - iter.position = Some(new_pos); + result_iterator.position = Some(new_pos); - match &iter.result.rows { - Some(rs) => (new_pos < rs.len()) as cass_bool_t, - None => false as cass_bool_t, + match &result_iterator.result.rows { + Some(rs) => (new_pos < rs.len()) as cass_bool_t, + None => false as cass_bool_t, + } + } + CassIterator::CassRowIterator(row_iterator) => { + let new_pos: usize = row_iterator.position.map_or(0, |prev_pos| prev_pos + 1); + + row_iterator.position = Some(new_pos); + + (new_pos < row_iterator.row.columns.len()) as cass_bool_t + } } } #[no_mangle] pub unsafe extern "C" fn cass_iterator_get_row(iterator: *const CassIterator) -> *const CassRow { - let iter: &CassIterator = ptr_to_ref(iterator); + let iter = ptr_to_ref(iterator); + + // Defined only for result iterator, for other types should return null + if let CassIterator::CassResultIterator(result_iterator) = iter { + let iter_position = match result_iterator.position { + Some(pos) => pos, + None => return std::ptr::null(), + }; + + let row: &CassRow = match result_iterator + .result + .rows + .as_ref() + .and_then(|rs| rs.get(iter_position)) + { + Some(row) => row, + None => return std::ptr::null(), + }; + + return row; + } - let iter_position: usize = match iter.position { - Some(pos) => pos, - None => return std::ptr::null(), + std::ptr::null() +} + +#[no_mangle] +pub unsafe extern "C" fn cass_iterator_get_column( + iterator: *const CassIterator, +) -> *const CassValue { + let iter = ptr_to_ref(iterator); + + // Defined only for row iterator, for other types should return null + if let CassIterator::CassRowIterator(row_iterator) = iter { + let iter_position = match row_iterator.position { + Some(pos) => pos, + None => return std::ptr::null(), + }; + + let value = match row_iterator.row.columns.get(iter_position) { + Some(col) => col, + None => return std::ptr::null(), + }; + + return value as *const CassValue; + } + + std::ptr::null() +} + +#[no_mangle] +pub unsafe extern "C" fn cass_iterator_from_result(result: *const CassResult) -> *mut CassIterator { + let result_from_raw: CassResult_ = clone_arced(result); + + let iterator = CassResultIterator { + result: result_from_raw, + position: None, }; - let row: &CassRow = match iter - .result - .rows - .as_ref() - .and_then(|rs| rs.get(iter_position)) - { - Some(row) => row, - None => return std::ptr::null(), + Box::into_raw(Box::new(CassIterator::CassResultIterator(iterator))) +} + +#[no_mangle] +pub unsafe extern "C" fn cass_iterator_from_row(row: *const CassRow) -> *mut CassIterator { + let row_from_raw: CassRow_ = ptr_to_ref(row); + + let iterator = CassRowIterator { + row: row_from_raw, + position: None, }; - row + Box::into_raw(Box::new(CassIterator::CassRowIterator(iterator))) +} + +#[no_mangle] +pub unsafe extern "C" fn cass_result_free(result_raw: *const CassResult) { + free_arced(result_raw); +} + +#[no_mangle] +pub unsafe extern "C" fn cass_result_has_more_pages(result: *const CassResult) -> cass_bool_t { + let result = ptr_to_ref(result); + result.metadata.paging_state.is_some() as cass_bool_t } #[no_mangle] @@ -112,12 +174,48 @@ pub unsafe extern "C" fn cass_row_get_column( let row: &CassRow = ptr_to_ref(row_raw); let index_usize: usize = index.try_into().unwrap(); - let column_value: &Option = match row.columns.get(index_usize) { + let column_value = match row.columns.get(index_usize) { Some(val) => val, None => return std::ptr::null(), }; - column_value + column_value as *const CassValue +} + +#[no_mangle] +pub unsafe extern "C" fn cass_result_column_name( + result: *const CassResult, + index: size_t, + name: *mut *const c_char, + name_length: *mut size_t, +) -> CassError { + let result_from_raw = ptr_to_ref(result); + let index_usize: usize = index.try_into().unwrap(); + + if index_usize >= result_from_raw.metadata.col_specs.len() { + return CassError::CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS; + } + + let column_spec: &ColumnSpec = result_from_raw.metadata.col_specs.get(index_usize).unwrap(); + let column_name = column_spec.name.as_str(); + + write_str_to_c(column_name, name, name_length); + + CassError::CASS_OK +} + +#[no_mangle] +pub unsafe extern "C" fn cass_value_type(value: *const CassValue) -> CassValueType { + let value_from_raw = ptr_to_ref(value); + + cass_data_type_type(&value_from_raw.value_type) +} + +#[no_mangle] +pub unsafe extern "C" fn cass_value_data_type(value: *const CassValue) -> *const CassDataType { + let value_from_raw = ptr_to_ref(value); + + &value_from_raw.value_type as *const CassDataType } #[no_mangle] @@ -127,8 +225,8 @@ pub unsafe extern "C" fn cass_value_get_float( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_float_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Float(f)) => *out = *f, + match val.value { + Some(CqlValue::Float(f)) => *out = f, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -143,8 +241,8 @@ pub unsafe extern "C" fn cass_value_get_double( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_double_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Double(d)) => *out = *d, + match val.value { + Some(CqlValue::Double(d)) => *out = d, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -159,8 +257,8 @@ pub unsafe extern "C" fn cass_value_get_bool( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_bool_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Boolean(b)) => *out = (*b) as cass_bool_t, + match val.value { + Some(CqlValue::Boolean(b)) => *out = b as cass_bool_t, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -175,8 +273,8 @@ pub unsafe extern "C" fn cass_value_get_int8( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_int8_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::TinyInt(i)) => *out = *i, + match val.value { + Some(CqlValue::TinyInt(i)) => *out = i, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -191,8 +289,8 @@ pub unsafe extern "C" fn cass_value_get_int16( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_int16_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::SmallInt(i)) => *out = *i, + match val.value { + Some(CqlValue::SmallInt(i)) => *out = i, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -207,8 +305,8 @@ pub unsafe extern "C" fn cass_value_get_uint32( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_uint32_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Date(u)) => *out = *u, // FIXME: hack + match val.value { + Some(CqlValue::Date(u)) => *out = u, // FIXME: hack Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -223,8 +321,8 @@ pub unsafe extern "C" fn cass_value_get_int32( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_int32_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Int(i)) => *out = *i, + match val.value { + Some(CqlValue::Int(i)) => *out = i, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -239,8 +337,9 @@ pub unsafe extern "C" fn cass_value_get_int64( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut cass_int64_t = ptr_to_ref_mut(output); - match val { - Some(CqlValue::BigInt(i)) => *out = *i, + match val.value { + Some(CqlValue::BigInt(i)) => *out = i, + Some(CqlValue::Counter(i)) => *out = i.0 as cass_int64_t, Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -255,9 +354,9 @@ pub unsafe extern "C" fn cass_value_get_uuid( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut CassUuid = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Uuid(uuid)) => *out = (*uuid).into(), - Some(CqlValue::Timeuuid(uuid)) => *out = (*uuid).into(), + match val.value { + Some(CqlValue::Uuid(uuid)) => *out = uuid.into(), + Some(CqlValue::Timeuuid(uuid)) => *out = uuid.into(), Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -272,8 +371,8 @@ pub unsafe extern "C" fn cass_value_get_inet( ) -> CassError { let val: &CassValue = ptr_to_ref(value); let out: &mut CassInet = ptr_to_ref_mut(output); - match val { - Some(CqlValue::Inet(inet)) => *out = (*inet).into(), + match val.value { + Some(CqlValue::Inet(inet)) => *out = inet.into(), Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, }; @@ -288,13 +387,13 @@ pub unsafe extern "C" fn cass_value_get_string( output_size: *mut size_t, ) -> CassError { let val: &CassValue = ptr_to_ref(value); - match val { + match &val.value { // It seems that cpp driver doesn't check the type - you can call _get_string // on any type and get internal represenation. I don't see how to do it easily in // a compatible way in rust, so let's do something sensible - only return result // for string values. - Some(CqlValue::Ascii(s)) => write_str_to_c(s, output, output_size), - Some(CqlValue::Text(s)) => write_str_to_c(s, output, output_size), + Some(CqlValue::Ascii(s)) => write_str_to_c(s.as_str(), output, output_size), + Some(CqlValue::Text(s)) => write_str_to_c(s.as_str(), output, output_size), Some(_) => return CassError::CASS_ERROR_LIB_INVALID_VALUE_TYPE, None => return CassError::CASS_ERROR_LIB_NULL_VALUE, } @@ -305,7 +404,7 @@ pub unsafe extern "C" fn cass_value_get_string( #[no_mangle] pub unsafe extern "C" fn cass_value_is_null(value: *const CassValue) -> cass_bool_t { let val: &CassValue = ptr_to_ref(value); - val.is_none() as cass_bool_t + val.value.is_none() as cass_bool_t } #[no_mangle] diff --git a/scylla-rust-wrapper/src/session.rs b/scylla-rust-wrapper/src/session.rs index ab0fb069..5aaec2f6 100644 --- a/scylla-rust-wrapper/src/session.rs +++ b/scylla-rust-wrapper/src/session.rs @@ -1,9 +1,10 @@ use crate::argconv::*; use crate::cass_error::*; +use crate::cass_types::get_column_type; use crate::cluster::build_session_builder; use crate::cluster::CassCluster; use crate::future::{CassFuture, CassResultValue}; -use crate::query_result::{CassResult, CassResultData, CassResult_, CassRow}; +use crate::query_result::{CassResult, CassResultData, CassResult_, CassRow, CassValue}; use crate::statement::CassStatement; use crate::statement::Statement; use crate::types::size_t; @@ -114,7 +115,7 @@ fn create_cass_rows_from_rows( let cass_rows = rows .into_iter() .map(|r| CassRow { - columns: r.columns, + columns: create_cass_row_columns(r, metadata), result_metadata: metadata.clone(), }) .collect(); @@ -122,6 +123,17 @@ fn create_cass_rows_from_rows( Some(cass_rows) } +fn create_cass_row_columns(row: Row, metadata: &Arc) -> Vec { + row.columns + .into_iter() + .zip(metadata.col_specs.iter()) + .map(|(val, col)| CassValue { + value_type: get_column_type(&col.typ), + value: val, + }) + .collect() +} + #[no_mangle] pub unsafe extern "C" fn cass_session_prepare( session: *mut CassSession, diff --git a/src/testing_unimplemented.cpp b/src/testing_unimplemented.cpp index 73ee4f96..02c0e89d 100644 --- a/src/testing_unimplemented.cpp +++ b/src/testing_unimplemented.cpp @@ -244,10 +244,6 @@ cass_custom_payload_set(CassCustomPayload* payload, size_t value_size){ throw std::runtime_error("UNIMPLEMENTED cass_custom_payload_set\n"); } -CASS_EXPORT const char* -cass_error_desc(CassError error){ - throw std::runtime_error("UNIMPLEMENTED cass_error_desc\n"); -} CASS_EXPORT void cass_execution_profile_free(CassExecProfile* profile){ throw std::runtime_error("UNIMPLEMENTED cass_execution_profile_free\n"); @@ -405,17 +401,9 @@ cass_iterator_from_collection(const CassValue* value){ throw std::runtime_error("UNIMPLEMENTED cass_iterator_from_collection\n"); } CASS_EXPORT CassIterator* -cass_iterator_from_row(const CassRow* row){ - throw std::runtime_error("UNIMPLEMENTED cass_iterator_from_row\n"); -} -CASS_EXPORT CassIterator* cass_iterator_from_tuple(const CassValue* value){ throw std::runtime_error("UNIMPLEMENTED cass_iterator_from_tuple\n"); } -CASS_EXPORT const CassValue* -cass_iterator_get_column(const CassIterator* iterator){ - throw std::runtime_error("UNIMPLEMENTED cass_iterator_get_column\n"); -} CASS_EXPORT CassError cass_iterator_get_user_type_field_name(const CassIterator* iterator, const char** name, @@ -819,10 +807,6 @@ cass_user_type_set_duration_by_name(CassUserType* user_type, cass_int64_t nanos){ throw std::runtime_error("UNIMPLEMENTED cass_user_type_set_duration_by_name\n"); } -CASS_EXPORT const CassDataType* -cass_value_data_type(const CassValue* value){ - throw std::runtime_error("UNIMPLEMENTED cass_value_data_type\n"); -} CASS_EXPORT CassError cass_value_get_bytes(const CassValue* value, const cass_byte_t** output, @@ -858,8 +842,4 @@ cass_value_primary_sub_type(const CassValue* collection){ CASS_EXPORT CassValueType cass_value_secondary_sub_type(const CassValue* collection){ throw std::runtime_error("UNIMPLEMENTED cass_value_secondary_sub_type\n"); -} -CASS_EXPORT CassValueType -cass_value_type(const CassValue* value){ - throw std::runtime_error("UNIMPLEMENTED cass_value_type\n"); } \ No newline at end of file