You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avro.apache.org by "Jack Klamer (Jira)" <ji...@apache.org> on 2022/03/19 23:50:00 UTC

[jira] [Created] (AVRO-3460) [rust] Value::validate does not validate against Schema Refs

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)