Jack Klamer created AVRO-3460:
---------------------------------
Summary: [rust] Value::validate does not validate against Schema
Refs
Key: AVRO-3460
URL: https://issues.apache.org/jira/browse/AVRO-3460
Project: Apache Avro
Issue Type: Bug
Reporter: Jack Klamer
The Value::validate() method is a preprocessing step before encoding the value
into the buffer. The encode function assumes the value to be valid. The
validate method recurses as necessary to validate against the schema provided.
The function does not recurse to validate against schema refs found in the
schema provided meaning that all values are validated to true against all
schema Ref's which is incorrect.
{code}
pub fn validate(&self, schema: &Schema) -> bool {
match (self, schema) {
(_, &Schema::Ref { name: _ }) => true,
....{code}
In practice this failure requires a user to forget to change their Schema when
changing their struct and that struct change happens to be the second use of a
struct within the enclosing struct. (Or fixed or enum).
Because encode assumes the values passed to it are valid, it is possible for
the encoding to complete without error in some situations.
Two tests that should pass and currently fail, useful for anyone interested
{code}
#[test]
fn test_validation_with_refs() {
let schema = Schema::parse_str(
r#"
{
"type":"record",
"name":"TestStruct",
"fields": [
{
"name":"a",
"type":{
"type":"record",
"name": "Inner",
"fields": [ {
"name":"z",
"type":"int"
}]
}
},
{
"name":"b",
"type":"Inner"
}
]
}"#,
)
.unwrap();
let inner_value_right = Value::Record(vec![("z".into(),
Value::Int(3))]);
let inner_value_wrong1 = Value::Record(vec![("z".into(), Value::Null)]);
let inner_value_wrong2 = Value::Record(vec![("a".into(),
Value::String("testing".into()))]);
let outer1 = Value::Record(vec![
("a".into(), inner_value_right.clone()),
("b".into(), inner_value_wrong1),
]);
let outer2 = Value::Record(vec![
("a".into(), inner_value_right),
("b".into(), inner_value_wrong2),
]);
assert!(!outer1.validate(&schema), "field b record is invalid against
the schema"); // this should pass, but doesn't
assert!(!outer2.validate(&schema), "field b record is invalid against
the schema"); // this should pass, but doesn't
}
#[derive(Serialize, Clone)]
struct TestInner {
z: i32
}
#[derive(Serialize)]
struct TestRefSchemaStruct1 {
a: TestInner,
b: String // could be literally anything
}
#[derive(Serialize)]
struct TestRefSchemaStruct2 {
a: TestInner,
b: i32 // could be literally anything
}
#[derive(Serialize)]
struct TestRefSchemaStruct3 {
a: TestInner,
b: Option<TestInner> // could be literally anything
}
#[test]
fn test_validation_with_refs_real_struct() {
let schema = Schema::parse_str(
r#"
{
"type":"record",
"name":"TestStruct",
"fields": [
{
"name":"a",
"type":{
"type":"record",
"name": "Inner",
"fields": [ {
"name":"z",
"type":"int"
}]
}
},
{
"name":"b",
"type":"Inner"
}
]
}"#,
)
.unwrap();
let test_inner = TestInner{z:3};
let test_outer1 = TestRefSchemaStruct1{a:test_inner.clone(),
b:"testing".into()};
let test_outer2 = TestRefSchemaStruct2{a:test_inner.clone(), b:24};
let test_outer3 = TestRefSchemaStruct3{a:test_inner.clone(), b:None};
use crate::ser::Serializer;
let mut ser = Serializer::default();
let test_outer1: Value = test_outer1.serialize(&mut ser ).unwrap();
let mut ser = Serializer::default();
let test_outer2: Value = test_outer2.serialize(&mut ser ).unwrap();
let mut ser = Serializer::default();
let test_outer3: Value = test_outer3.serialize(&mut ser ).unwrap();
assert!(!test_outer1.validate(&schema), "field b record is invalid
against the schema"); // this should pass, but doesn't
assert!(!test_outer2.validate(&schema), "field b record is invalid
against the schema"); // this should pass, but doesn't
assert!(!test_outer3.validate(&schema), "field b record is invalid
against the schema"); // this should pass, but doesn't
}
{code}
--
This message was sent by Atlassian Jira
(v8.20.1#820001)