Skip to content

Commit c728d68

Browse files
Feat: Add Unsupported type for bolt 6 (#47)
1 parent 904a774 commit c728d68

File tree

9 files changed

+148
-10
lines changed

9 files changed

+148
-10
lines changed

neo4j/src/driver/io/bolt/bolt6x0/translator.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::collections::VecDeque;
1516
use std::mem::size_of;
1617

1718
use usize_cast::FromUsize;
@@ -91,9 +92,9 @@ impl BoltStructTranslator for Bolt6x0StructTranslator {
9192
}
9293

9394
fn deserialize_struct(&self, tag: u8, mut fields: Vec<ValueReceive>) -> ValueReceive {
95+
let size = fields.len();
9496
match tag {
9597
TAG_VECTOR => {
96-
let size = fields.len();
9798
if size != 2 {
9899
return invalid_struct(format!(
99100
"expected 2 fields for vector struct b'V', found {size}"
@@ -181,6 +182,44 @@ impl BoltStructTranslator for Bolt6x0StructTranslator {
181182
}
182183
})
183184
}
185+
TAG_UNSUPPORTED_TYPE => {
186+
let mut fields = VecDeque::from(fields);
187+
if size != 4 {
188+
return invalid_struct(format!(
189+
"expected 4 fields for unsupported type struct b'?', found {size}"
190+
));
191+
}
192+
let name = as_string!(fields.pop_front().unwrap(), "unsupported type name");
193+
let min_major = as_int!(fields.pop_front().unwrap(), "unsupported type min major");
194+
let min_minor = as_int!(fields.pop_front().unwrap(), "unsupported type min minor");
195+
196+
let Ok(min_major) = u8::try_from(min_major) else {
197+
return invalid_struct(format!(
198+
"unsupported type minimum major version must be u8, found {min_major}"
199+
));
200+
};
201+
202+
let Ok(min_minor) = u8::try_from(min_minor) else {
203+
return invalid_struct(format!(
204+
"unsupported type minimum minor version must be u8, found {min_minor}"
205+
));
206+
};
207+
208+
let mut extra = as_map!(fields.pop_front().unwrap(), "unsupported type extra");
209+
210+
let message = extra.remove("message");
211+
212+
let message = match message {
213+
Some(text) => Some(as_string!(text, "unsupported type extra::message")),
214+
None => None,
215+
};
216+
217+
ValueReceive::UnsupportedType(crate::value::unsupported_type::UnsupportedType {
218+
name,
219+
minimum_protocol_version: (min_major, min_minor),
220+
message,
221+
})
222+
}
184223
_ => self.bolt5x8_translator.deserialize_struct(tag, fields),
185224
}
186225
}

neo4j/src/driver/io/bolt/bolt_common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub(super) const TAG_LEGACY_DATE_TIME_ZONE_ID: u8 = b'f';
3636
pub(super) const TAG_LOCAL_DATE_TIME: u8 = b'd';
3737
pub(super) const TAG_DURATION: u8 = b'E';
3838
pub(super) const TAG_VECTOR: u8 = b'V';
39+
pub(super) const TAG_UNSUPPORTED_TYPE: u8 = b'?';
3940
pub(super) const VEC_TYPE_MARKER_F64: &[u8] = &[0xC1];
4041
pub(super) const VEC_TYPE_MARKER_F32: &[u8] = &[0xC6];
4142
pub(super) const VEC_TYPE_MARKER_I64: &[u8] = &[0xCB];

neo4j/src/test_data/public-api.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,24 @@ impl core::marker::Sync for neo4j::value::time::TimeComponents
16801680
impl core::marker::Unpin for neo4j::value::time::TimeComponents
16811681
impl core::panic::unwind_safe::RefUnwindSafe for neo4j::value::time::TimeComponents
16821682
impl core::panic::unwind_safe::UnwindSafe for neo4j::value::time::TimeComponents
1683+
pub mod neo4j::value::unsupported_type
1684+
pub struct neo4j::value::unsupported_type::UnsupportedType
1685+
impl neo4j::value::unsupported_type::UnsupportedType
1686+
pub fn neo4j::value::unsupported_type::UnsupportedType::message(&self) -> core::option::Option<&str>
1687+
pub fn neo4j::value::unsupported_type::UnsupportedType::minimum_protocol_version(&self) -> (u8, u8)
1688+
pub fn neo4j::value::unsupported_type::UnsupportedType::name(&self) -> &str
1689+
impl core::clone::Clone for neo4j::value::unsupported_type::UnsupportedType
1690+
pub fn neo4j::value::unsupported_type::UnsupportedType::clone(&self) -> neo4j::value::unsupported_type::UnsupportedType
1691+
impl core::cmp::PartialEq for neo4j::value::unsupported_type::UnsupportedType
1692+
pub fn neo4j::value::unsupported_type::UnsupportedType::eq(&self, _: &Self) -> bool
1693+
impl core::fmt::Debug for neo4j::value::unsupported_type::UnsupportedType
1694+
pub fn neo4j::value::unsupported_type::UnsupportedType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
1695+
impl core::marker::Freeze for neo4j::value::unsupported_type::UnsupportedType
1696+
impl core::marker::Send for neo4j::value::unsupported_type::UnsupportedType
1697+
impl core::marker::Sync for neo4j::value::unsupported_type::UnsupportedType
1698+
impl core::marker::Unpin for neo4j::value::unsupported_type::UnsupportedType
1699+
impl core::panic::unwind_safe::RefUnwindSafe for neo4j::value::unsupported_type::UnsupportedType
1700+
impl core::panic::unwind_safe::UnwindSafe for neo4j::value::unsupported_type::UnsupportedType
16831701
pub mod neo4j::value::vector
16841702
#[non_exhaustive] pub enum neo4j::value::vector::Vector
16851703
pub neo4j::value::vector::Vector::F32(alloc::vec::Vec<f32>)
@@ -1784,6 +1802,7 @@ pub neo4j::value::ValueReceive::Path(neo4j::value::graph::Path)
17841802
pub neo4j::value::ValueReceive::Relationship(neo4j::value::graph::Relationship)
17851803
pub neo4j::value::ValueReceive::String(alloc::string::String)
17861804
pub neo4j::value::ValueReceive::Time(neo4j::value::time::Time)
1805+
pub neo4j::value::ValueReceive::UnsupportedType(neo4j::value::unsupported_type::UnsupportedType)
17871806
pub neo4j::value::ValueReceive::Vector(neo4j::value::vector::Vector)
17881807
pub neo4j::value::ValueReceive::WGS84_2D(neo4j::value::spatial::WGS84_2D)
17891808
pub neo4j::value::ValueReceive::WGS84_3D(neo4j::value::spatial::WGS84_3D)
@@ -2314,6 +2333,7 @@ pub neo4j::ValueReceive::Path(neo4j::value::graph::Path)
23142333
pub neo4j::ValueReceive::Relationship(neo4j::value::graph::Relationship)
23152334
pub neo4j::ValueReceive::String(alloc::string::String)
23162335
pub neo4j::ValueReceive::Time(neo4j::value::time::Time)
2336+
pub neo4j::ValueReceive::UnsupportedType(neo4j::value::unsupported_type::UnsupportedType)
23172337
pub neo4j::ValueReceive::Vector(neo4j::value::vector::Vector)
23182338
pub neo4j::ValueReceive::WGS84_2D(neo4j::value::spatial::WGS84_2D)
23192339
pub neo4j::ValueReceive::WGS84_3D(neo4j::value::spatial::WGS84_3D)

neo4j/src/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
pub mod graph;
1616
pub mod spatial;
1717
pub mod time;
18+
pub mod unsupported_type;
1819
mod value_receive;
1920
mod value_send;
2021
pub mod vector;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright Rouven Bauer
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// Represents a type unknown to the driver, received from the server.
16+
///
17+
/// This type is used, for instance, when a newer DBMS produces a result containing a type that the
18+
/// current version of the driver does not yet understand.
19+
///
20+
/// The attributes exposed by this type are meant for displaying and debugging purposes.
21+
/// They may change in future versions of the server and should not be relied upon for any logic in
22+
/// your application.
23+
/// If your application requires handling this type, you must upgrade your driver to a version that
24+
/// supports it.
25+
#[derive(Debug, Clone)]
26+
pub struct UnsupportedType {
27+
pub(crate) name: String,
28+
pub(crate) minimum_protocol_version: (u8, u8),
29+
pub(crate) message: Option<String>,
30+
}
31+
32+
impl UnsupportedType {
33+
pub fn name(&self) -> &str {
34+
self.name.as_str()
35+
}
36+
37+
pub fn minimum_protocol_version(&self) -> (u8, u8) {
38+
self.minimum_protocol_version
39+
}
40+
41+
pub fn message(&self) -> Option<&str> {
42+
self.message.as_deref()
43+
}
44+
}
45+
46+
impl PartialEq for UnsupportedType {
47+
fn eq(&self, _: &Self) -> bool {
48+
false
49+
}
50+
}

neo4j/src/value/value_receive.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ use std::collections::{HashMap, VecDeque};
1616

1717
use itertools::Itertools;
1818

19-
use super::graph;
2019
use super::spatial;
2120
use super::time;
2221
use super::value_send::ValueSend;
2322
use super::vector;
23+
use super::{graph, unsupported_type};
2424

2525
/// A value received from the database.
2626
#[derive(Debug, Clone, PartialEq)]
@@ -49,6 +49,7 @@ pub enum ValueReceive {
4949
DateTime(time::DateTime),
5050
DateTimeFixed(time::DateTimeFixed),
5151
Vector(vector::Vector),
52+
UnsupportedType(unsupported_type::UnsupportedType),
5253
/// A value that could not be received.
5354
/// This can have multiple reasons, for example:
5455
/// * Unexpected struct data (likely a driver or server bug)
@@ -1206,10 +1207,11 @@ impl ValueReceive {
12061207
ValueReceive::LocalDateTime(v) => format!("{v:?}"),
12071208
ValueReceive::DateTime(v) => format!("{v:?}"),
12081209
ValueReceive::DateTimeFixed(v) => format!("{v:?}"),
1210+
ValueReceive::Vector(v) => format!("{v:?}"),
1211+
ValueReceive::UnsupportedType(v) => format!("{v:?}"),
12091212
ValueReceive::BrokenValue(broken_value) => {
12101213
format!("BrokenValue({})", broken_value.reason())
12111214
}
1212-
ValueReceive::Vector(v) => format!("{v:?}"),
12131215
}
12141216
}
12151217
}

neo4j/src/value/value_send.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ impl TryFrom<ValueReceive> for ValueSend {
257257
ValueReceive::Relationship(_) => return Err("cannot convert Relationship".into()),
258258
ValueReceive::Path(_) => return Err("cannot convert Path".into()),
259259
ValueReceive::Vector(v) => Self::Vector(v),
260+
ValueReceive::UnsupportedType(_) => return Err("cannot convert UnsupportedType".into()),
260261
})
261262
}
262263
}

testkit_backend/src/testkit_backend/cypher_value.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use neo4j::value::graph::{
3636
UnboundRelationship as Neo4jUnboundRelationship,
3737
};
3838
use neo4j::value::spatial::{Cartesian2D, Cartesian3D, WGS84_2D, WGS84_3D};
39-
use neo4j::value::{time, vector, ValueReceive, ValueSend};
39+
use neo4j::value::{time, unsupported_type, vector, ValueReceive, ValueSend};
4040

4141
#[derive(Debug)]
4242
#[repr(transparent)]
@@ -121,6 +121,12 @@ pub(super) enum CypherValue {
121121
dtype: String,
122122
data: String,
123123
},
124+
#[serde(rename_all = "camelCase")]
125+
CypherUnsupportedType {
126+
name: String,
127+
minimum_protocol: String,
128+
message: Option<String>,
129+
},
124130
}
125131

126132
#[derive(Debug, Deserialize, Serialize)]
@@ -511,6 +517,11 @@ impl TryFrom<CypherValue> for ValueSend {
511517
}
512518
}
513519
}
520+
CypherValue::CypherUnsupportedType { .. } => {
521+
return Err(NotADriverValueError::new(
522+
"UnsupportedType cannot be used as input type",
523+
))
524+
}
514525
})
515526
}
516527
}
@@ -690,7 +701,7 @@ impl TryFrom<ValueSend> for CypherValue {
690701
ValueSend::LocalDateTime(value) => local_date_time_to_cypher_value(value)?,
691702
ValueSend::DateTime(value) => date_time_to_cypher_value(value)?,
692703
ValueSend::DateTimeFixed(value) => date_time_fixed_to_cypher_value(value)?,
693-
ValueSend::Vector(value) => vector_cypher_value(value)?,
704+
ValueSend::Vector(value) => vector_to_cypher_value(value)?,
694705
_ => {
695706
return Err(TestKitError::backend_err(format!(
696707
"Failed to serialize ValueSend to json: {v:?}",
@@ -834,7 +845,8 @@ impl TryFrom<ValueReceive> for CypherValue {
834845
ValueReceive::LocalDateTime(value) => local_date_time_to_cypher_value(value)?,
835846
ValueReceive::DateTime(value) => date_time_to_cypher_value(value)?,
836847
ValueReceive::DateTimeFixed(value) => date_time_fixed_to_cypher_value(value)?,
837-
ValueReceive::Vector(value) => vector_cypher_value(value)?,
848+
ValueReceive::Vector(value) => vector_to_cypher_value(value)?,
849+
ValueReceive::UnsupportedType(value) => unsupported_type_to_cypher_value(value),
838850
ValueReceive::BrokenValue(v) => {
839851
return Err(BrokenValueError::BrokenValue {
840852
reason: v.reason().into(),
@@ -953,7 +965,7 @@ fn date_time_fixed_to_cypher_value(
953965
}))
954966
}
955967

956-
fn vector_cypher_value(value: vector::Vector) -> Result<CypherValue, BrokenValueError> {
968+
fn vector_to_cypher_value(value: vector::Vector) -> Result<CypherValue, BrokenValueError> {
957969
Ok(match value {
958970
vector::Vector::F64(value) => CypherValue::CypherVector {
959971
dtype: String::from("f64"),
@@ -1021,6 +1033,18 @@ fn vector_cypher_value(value: vector::Vector) -> Result<CypherValue, BrokenValue
10211033
})
10221034
}
10231035

1036+
fn unsupported_type_to_cypher_value(value: unsupported_type::UnsupportedType) -> CypherValue {
1037+
CypherValue::CypherUnsupportedType {
1038+
name: value.name().to_owned(),
1039+
minimum_protocol: format!(
1040+
"{}.{}",
1041+
value.minimum_protocol_version().0,
1042+
value.minimum_protocol_version().1
1043+
),
1044+
message: value.message().map(String::from),
1045+
}
1046+
}
1047+
10241048
#[allow(clippy::result_large_err)]
10251049
fn try_into_node(n: Neo4jNode) -> Result<CypherNode, TestKitError> {
10261050
Ok(CypherNode {

testkit_backend/src/testkit_backend/responses.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use super::{BackendId, TestKitResultT};
3838

3939
// [bolt-version-bump] search tag when changing bolt version support
4040
// https://github.com/rust-lang/rust/issues/85077
41-
const FEATURE_LIST: [&str; 55] = [
41+
const FEATURE_LIST: &[&str] = &[
4242
// === FUNCTIONAL FEATURES ===
4343
"Feature:API:BookmarkManager",
4444
"Feature:API:ConnectionAcquisitionTimeout",
@@ -65,7 +65,7 @@ const FEATURE_LIST: [&str; 55] = [
6565
"Feature:API:Summary:GqlStatusObjects",
6666
"Feature:API:Type.Spatial",
6767
"Feature:API:Type.Temporal",
68-
// "Feature:API:Type.UnsupportedType",
68+
"Feature:API:Type.UnsupportedType",
6969
"Feature:API:Type.Vector",
7070
"Feature:Auth:Bearer",
7171
"Feature:Auth:Custom",
@@ -850,7 +850,7 @@ impl TryFrom<EagerResult> for Response {
850850
impl Response {
851851
pub(super) fn feature_list() -> Self {
852852
Self::FeatureList {
853-
features: FEATURE_LIST.into_iter().map(String::from).collect(),
853+
features: FEATURE_LIST.iter().map(|x| String::from(*x)).collect(),
854854
}
855855
}
856856

0 commit comments

Comments
 (0)