1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to you under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  * https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14  * implied.  See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 
18 #include "avro_private.h"
19 #include "avro/errors.h"
20 #include <limits.h>
21 #include <errno.h>
22 #include <string.h>
23 #include "schema.h"
24 #include "datum.h"
25 #include "st.h"
26 
27 struct validate_st {
28 	avro_schema_t expected_schema;
29 	int rval;
30 };
31 
32 static int
schema_map_validate_foreach(char * key,avro_datum_t datum,struct validate_st * vst)33 schema_map_validate_foreach(char *key, avro_datum_t datum,
34 			    struct validate_st *vst)
35 {
36 	AVRO_UNUSED(key);
37 
38 	if (!avro_schema_datum_validate(vst->expected_schema, datum)) {
39 		vst->rval = 0;
40 		return ST_STOP;
41 	}
42 	return ST_CONTINUE;
43 }
44 
45 int
avro_schema_datum_validate(avro_schema_t expected_schema,avro_datum_t datum)46 avro_schema_datum_validate(avro_schema_t expected_schema, avro_datum_t datum)
47 {
48 	check_param(EINVAL, expected_schema, "expected schema");
49 	check_param(EINVAL, is_avro_datum(datum), "datum");
50 
51 	int rval;
52 	long i;
53 
54 	switch (avro_typeof(expected_schema)) {
55 	case AVRO_NULL:
56 		return is_avro_null(datum);
57 
58 	case AVRO_BOOLEAN:
59 		return is_avro_boolean(datum);
60 
61 	case AVRO_STRING:
62 		return is_avro_string(datum);
63 
64 	case AVRO_BYTES:
65 		return is_avro_bytes(datum);
66 
67 	case AVRO_INT32:
68 		return is_avro_int32(datum)
69 		    || (is_avro_int64(datum)
70 			&& (INT_MIN <= avro_datum_to_int64(datum)->i64
71 			    && avro_datum_to_int64(datum)->i64 <= INT_MAX));
72 
73 	case AVRO_INT64:
74 		return is_avro_int32(datum) || is_avro_int64(datum);
75 
76 	case AVRO_FLOAT:
77 		return is_avro_int32(datum) || is_avro_int64(datum)
78 		    || is_avro_float(datum);
79 
80 	case AVRO_DOUBLE:
81 		return is_avro_int32(datum) || is_avro_int64(datum)
82 		    || is_avro_float(datum) || is_avro_double(datum);
83 
84 	case AVRO_FIXED:
85 		return (is_avro_fixed(datum)
86 			&& (avro_schema_to_fixed(expected_schema)->size ==
87 			    avro_datum_to_fixed(datum)->size));
88 
89 	case AVRO_ENUM:
90 		if (is_avro_enum(datum)) {
91 			long value = avro_datum_to_enum(datum)->value;
92 			long max_value =
93 			    avro_schema_to_enum(expected_schema)->symbols->
94 			    num_entries;
95 			return 0 <= value && value <= max_value;
96 		}
97 		return 0;
98 
99 	case AVRO_ARRAY:
100 		if (is_avro_array(datum)) {
101 			struct avro_array_datum_t *array =
102 			    avro_datum_to_array(datum);
103 
104 			for (i = 0; i < array->els->num_entries; i++) {
105 				union {
106 					st_data_t data;
107 					avro_datum_t datum;
108 				} val;
109 				st_lookup(array->els, i, &val.data);
110 				if (!avro_schema_datum_validate
111 				    ((avro_schema_to_array
112 				      (expected_schema))->items, val.datum)) {
113 					return 0;
114 				}
115 			}
116 			return 1;
117 		}
118 		return 0;
119 
120 	case AVRO_MAP:
121 		if (is_avro_map(datum)) {
122 			struct validate_st vst =
123 			    { avro_schema_to_map(expected_schema)->values, 1
124 			};
125 			st_foreach(avro_datum_to_map(datum)->map,
126 				   HASH_FUNCTION_CAST schema_map_validate_foreach,
127 				   (st_data_t) & vst);
128 			return vst.rval;
129 		}
130 		break;
131 
132 	case AVRO_UNION:
133 		if (is_avro_union(datum)) {
134 			struct avro_union_schema_t *union_schema =
135 			    avro_schema_to_union(expected_schema);
136 			struct avro_union_datum_t *union_datum =
137 			    avro_datum_to_union(datum);
138 			union {
139 				st_data_t data;
140 				avro_schema_t schema;
141 			} val;
142 
143 			if (!st_lookup
144 			    (union_schema->branches, union_datum->discriminant,
145 			     &val.data)) {
146 				return 0;
147 			}
148 			return avro_schema_datum_validate(val.schema,
149 							  union_datum->value);
150 		}
151 		break;
152 
153 	case AVRO_RECORD:
154 		if (is_avro_record(datum)) {
155 			struct avro_record_schema_t *record_schema =
156 			    avro_schema_to_record(expected_schema);
157 			for (i = 0; i < record_schema->fields->num_entries; i++) {
158 				avro_datum_t field_datum;
159 				union {
160 					st_data_t data;
161 					struct avro_record_field_t *field;
162 				} val;
163 				st_lookup(record_schema->fields, i, &val.data);
164 
165 				rval =
166 				    avro_record_get(datum, val.field->name,
167 						    &field_datum);
168 				if (rval) {
169 					/*
170 					 * TODO: check for default values
171 					 */
172 					return rval;
173 				}
174 				if (!avro_schema_datum_validate
175 				    (val.field->type, field_datum)) {
176 					return 0;
177 				}
178 			}
179 			return 1;
180 		}
181 		break;
182 
183 	case AVRO_LINK:
184 		{
185 			return
186 			    avro_schema_datum_validate((avro_schema_to_link
187 							(expected_schema))->to,
188 						       datum);
189 		}
190 		break;
191 	}
192 	return 0;
193 }
194