1 /* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "condition.h"
23 #include "openvswitch/json.h"
24 #include "ovsdb-error.h"
32 static struct ovsdb_error
*
33 ovsdb_clause_from_json(const struct ovsdb_table_schema
*ts
,
34 const struct json
*json
,
35 struct ovsdb_symbol_table
*symtab
,
36 struct ovsdb_clause
*clause
)
38 const struct json_array
*array
;
39 struct ovsdb_error
*error
;
40 const char *function_name
;
41 const char *column_name
;
42 struct ovsdb_type type
;
44 if (json
->type
== JSON_TRUE
|| json
->type
== JSON_FALSE
) {
46 json
->type
== JSON_TRUE
? OVSDB_F_TRUE
: OVSDB_F_FALSE
;
48 /* Column and arg fields are not being used with boolean functions.
50 clause
->column
= ovsdb_table_schema_get_column(ts
, "_uuid");
51 clause
->index
= clause
->column
->index
;
52 ovsdb_datum_init_default(&clause
->arg
, &clause
->column
->type
);
56 if (json
->type
!= JSON_ARRAY
58 || json
->array
.elems
[0]->type
!= JSON_STRING
59 || json
->array
.elems
[1]->type
!= JSON_STRING
) {
60 return ovsdb_syntax_error(json
, NULL
, "Parse error in condition.");
62 array
= json_array(json
);
64 column_name
= json_string(array
->elems
[0]);
65 clause
->column
= ovsdb_table_schema_get_column(ts
, column_name
);
66 if (!clause
->column
) {
67 return ovsdb_syntax_error(json
, "unknown column",
68 "No column %s in table %s.",
69 column_name
, ts
->name
);
71 clause
->index
= clause
->column
->index
;
72 type
= clause
->column
->type
;
74 function_name
= json_string(array
->elems
[1]);
75 error
= ovsdb_function_from_string(function_name
, &clause
->function
);
80 /* Type-check and relax restrictions on 'type' if appropriate. */
81 switch (clause
->function
) {
86 /* Allow these operators for types with n_min == 0, n_max == 1.
87 * (They will always be "false" if the value is missing.) */
88 if (!(ovsdb_type_is_scalar(&type
)
89 || ovsdb_type_is_optional_scalar(&type
))
90 || (type
.key
.type
!= OVSDB_TYPE_INTEGER
91 && type
.key
.type
!= OVSDB_TYPE_REAL
)) {
92 char *s
= ovsdb_type_to_english(&type
);
93 error
= ovsdb_syntax_error(
94 json
, NULL
, "Type mismatch: \"%s\" operator may not be "
95 "applied to column %s of type %s.",
96 ovsdb_function_to_string(clause
->function
),
97 clause
->column
->name
, s
);
102 /* Force the argument to be a scalar. */
111 case OVSDB_F_EXCLUDES
:
112 if (!ovsdb_type_is_scalar(&type
)) {
114 type
.n_max
= UINT_MAX
;
118 case OVSDB_F_INCLUDES
:
119 if (!ovsdb_type_is_scalar(&type
)) {
127 return ovsdb_datum_from_json(&clause
->arg
, &type
, array
->elems
[2], symtab
);
131 ovsdb_clause_free(struct ovsdb_clause
*clause
)
133 ovsdb_datum_destroy(&clause
->arg
, &clause
->column
->type
);
137 compare_clauses_3way(const void *a_
, const void *b_
)
139 const struct ovsdb_clause
*a
= a_
;
140 const struct ovsdb_clause
*b
= b_
;
142 if (a
->function
!= b
->function
) {
143 /* Bring functions to the front based on the fraction of table rows
144 * that they are (heuristically) expected to leave in the query
145 * results. Note that "enum ovsdb_function" is intentionally ordered
146 * to make this trivial. */
147 return a
->function
< b
->function
? -1 : 1;
148 } else if (a
->column
->index
!= b
->column
->index
) {
149 if (a
->column
->index
< OVSDB_N_STD_COLUMNS
150 || b
->column
->index
< OVSDB_N_STD_COLUMNS
) {
151 /* Bring the standard columns and in particular the UUID column
152 * (since OVSDB_COL_UUID has value 0) to the front. We have an
153 * index on the UUID column, so that makes our queries cheaper. */
154 return a
->column
->index
< b
->column
->index
? -1 : 1;
156 /* Order clauses predictably to make testing easier. */
157 return strcmp(a
->column
->name
, b
->column
->name
);
165 compare_clauses_3way_with_data(const void *a_
, const void *b_
)
167 const struct ovsdb_clause
*a
= a_
;
168 const struct ovsdb_clause
*b
= b_
;
171 res
= compare_clauses_3way(a
, b
);
172 return res
? res
: ovsdb_datum_compare_3way(&a
->arg
,
177 struct ovsdb_o_column
{
178 const struct ovsdb_column
*column
;
179 struct hmap o_clauses
;
182 struct ovsdb_o_clause
{
183 struct ovsdb_datum
*arg
;
184 struct hmap_node hmap_node
;
188 ovsdb_condition_optimize(struct ovsdb_condition
*cnd
)
193 if (!cnd
->optimized
) {
197 for(i
= 0; i
< cnd
->n_clauses
; i
++) {
198 struct ovsdb_clause
*clause
= &cnd
->clauses
[i
];
200 if (clause
->function
!= OVSDB_F_EQ
) {
204 struct ovsdb_o_clause
*o_clause
= xzalloc(sizeof *o_clause
);
205 struct ovsdb_o_column
*o_column
=
206 shash_find_data(&cnd
->o_columns
, clause
->column
->name
);
209 o_column
= xzalloc(sizeof *o_column
);
210 o_column
->column
= clause
->column
;
211 hmap_init(&o_column
->o_clauses
);
212 shash_add(&cnd
->o_columns
, clause
->column
->name
, o_column
);
214 o_clause
->arg
= &clause
->arg
;
215 hash
= ovsdb_datum_hash(&clause
->arg
, &clause
->column
->type
, 0);
216 hmap_insert(&o_column
->o_clauses
, &o_clause
->hmap_node
, hash
);
221 ovsdb_condition_optimize_destroy(struct ovsdb_condition
*cnd
)
223 struct shash_node
*node
, *next
;
225 SHASH_FOR_EACH_SAFE (node
, next
, &cnd
->o_columns
) {
226 struct ovsdb_o_column
*o_column
= node
->data
;
227 struct ovsdb_o_clause
*c
, *c_next
;
229 HMAP_FOR_EACH_SAFE(c
, c_next
, hmap_node
, &o_column
->o_clauses
) {
230 hmap_remove(&o_column
->o_clauses
, &c
->hmap_node
);
233 hmap_destroy(&o_column
->o_clauses
);
234 shash_delete(&cnd
->o_columns
, node
);
237 shash_destroy(&cnd
->o_columns
);
241 ovsdb_condition_from_json(const struct ovsdb_table_schema
*ts
,
242 const struct json
*json
,
243 struct ovsdb_symbol_table
*symtab
,
244 struct ovsdb_condition
*cnd
)
246 const struct json_array
*array
= json_array(json
);
249 ovsdb_condition_init(cnd
);
250 cnd
->clauses
= xmalloc(array
->n
* sizeof *cnd
->clauses
);
252 for (i
= 0; i
< array
->n
; i
++) {
253 struct ovsdb_error
*error
;
254 error
= ovsdb_clause_from_json(ts
, array
->elems
[i
], symtab
,
257 ovsdb_condition_destroy(cnd
);
263 if (cnd
->clauses
[i
].function
> OVSDB_F_EQ
) {
264 cnd
->optimized
= false;
268 /* A real database would have a query optimizer here. */
269 qsort(cnd
->clauses
, cnd
->n_clauses
, sizeof *cnd
->clauses
,
270 compare_clauses_3way_with_data
);
272 ovsdb_condition_optimize(cnd
);
278 ovsdb_clause_to_json(const struct ovsdb_clause
*clause
)
280 if (clause
->function
!= OVSDB_F_TRUE
&&
281 clause
->function
!= OVSDB_F_FALSE
) {
282 return json_array_create_3(
283 json_string_create(clause
->column
->name
),
284 json_string_create(ovsdb_function_to_string(clause
->function
)),
285 ovsdb_datum_to_json(&clause
->arg
, &clause
->column
->type
));
288 return json_boolean_create(clause
->function
== OVSDB_F_TRUE
);
292 ovsdb_condition_to_json(const struct ovsdb_condition
*cnd
)
294 struct json
**clauses
;
297 clauses
= xmalloc(cnd
->n_clauses
* sizeof *clauses
);
298 for (i
= 0; i
< cnd
->n_clauses
; i
++) {
299 clauses
[i
] = ovsdb_clause_to_json(&cnd
->clauses
[i
]);
301 return json_array_create(clauses
, cnd
->n_clauses
);
305 ovsdb_clause_evaluate(const struct ovsdb_datum
*fields
,
306 const struct ovsdb_clause
*c
,
307 unsigned int index_map
[])
309 const struct ovsdb_datum
*field
= &fields
[index_map
?
310 index_map
[c
->column
->index
] :
312 const struct ovsdb_datum
*arg
= &c
->arg
;
313 const struct ovsdb_type
*type
= &c
->column
->type
;
315 if (c
->function
== OVSDB_F_TRUE
||
316 c
->function
== OVSDB_F_FALSE
) {
317 return c
->function
== OVSDB_F_TRUE
;
319 if (ovsdb_type_is_optional_scalar(type
)
321 && (c
->function
== OVSDB_F_LT
||
322 c
->function
== OVSDB_F_LE
||
323 c
->function
== OVSDB_F_GT
||
324 c
->function
== OVSDB_F_GE
)) {
326 } else if ((ovsdb_type_is_scalar(type
)
327 || ovsdb_type_is_optional_scalar(type
))
330 int cmp
= ovsdb_atom_compare_3way(&field
->keys
[0], &arg
->keys
[0],
332 switch (c
->function
) {
338 case OVSDB_F_INCLUDES
:
341 case OVSDB_F_EXCLUDES
:
352 switch (c
->function
) {
354 return ovsdb_datum_equals(field
, arg
, type
);
356 return !ovsdb_datum_equals(field
, arg
, type
);
357 case OVSDB_F_INCLUDES
:
358 return ovsdb_datum_includes_all(arg
, field
, type
);
359 case OVSDB_F_EXCLUDES
:
360 return ovsdb_datum_excludes_all(arg
, field
, type
);
375 ovsdb_clause_clone(struct ovsdb_clause
*new, struct ovsdb_clause
*old
)
377 new->function
= old
->function
;
378 new->column
= old
->column
;
379 ovsdb_datum_clone(&new->arg
,
385 ovsdb_condition_match_every_clause(const struct ovsdb_row
*row
,
386 const struct ovsdb_condition
*cnd
)
390 for (i
= 0; i
< cnd
->n_clauses
; i
++) {
391 if (!ovsdb_clause_evaluate(row
->fields
, &cnd
->clauses
[i
], NULL
)) {
400 ovsdb_condition_match_any_clause_optimized(const struct ovsdb_datum
*row_datum
,
401 const struct ovsdb_condition
*cnd
,
402 unsigned int index_map
[])
404 if (ovsdb_condition_is_true(cnd
)) {
408 struct shash_node
*node
;
409 SHASH_FOR_EACH (node
, &cnd
->o_columns
) {
410 struct ovsdb_o_column
*o_column
= node
->data
;
411 const struct ovsdb_column
*column
= o_column
->column
;
412 const struct ovsdb_datum
*arg
= &row_datum
[index_map
?
413 index_map
[column
->index
] :
415 uint32_t hash
= ovsdb_datum_hash(arg
, &column
->type
, 0);
416 struct ovsdb_o_clause
*o_clause
;
418 HMAP_FOR_EACH_WITH_HASH(o_clause
, hmap_node
, hash
, &o_column
->o_clauses
) {
419 if (ovsdb_datum_equals(arg
, o_clause
->arg
, &column
->type
)) {
427 /* Returns true if condition evaluation of one of the clauses is
428 * true. index_map[] is an optional array that if exists indicates a mapping
429 * between indexing row_datum to the indexes in ovsdb_column */
431 ovsdb_condition_match_any_clause(const struct ovsdb_datum
*row_datum
,
432 const struct ovsdb_condition
*cnd
,
433 unsigned int index_map
[])
437 if (cnd
->optimized
) {
438 return ovsdb_condition_match_any_clause_optimized(row_datum
, cnd
,
442 for (i
= 0; i
< cnd
->n_clauses
; i
++) {
443 if (ovsdb_clause_evaluate(row_datum
, &cnd
->clauses
[i
], index_map
)) {
452 ovsdb_condition_destroy(struct ovsdb_condition
*cnd
)
456 for (i
= 0; i
< cnd
->n_clauses
; i
++) {
457 ovsdb_clause_free(&cnd
->clauses
[i
]);
462 ovsdb_condition_optimize_destroy(cnd
);
466 ovsdb_condition_init(struct ovsdb_condition
*cnd
)
470 cnd
->optimized
= true;
471 shash_init(&cnd
->o_columns
);
475 ovsdb_condition_empty(const struct ovsdb_condition
*cnd
)
477 return cnd
->n_clauses
== 0;
481 ovsdb_condition_cmp_3way(const struct ovsdb_condition
*a
,
482 const struct ovsdb_condition
*b
)
487 if (a
->n_clauses
!= b
->n_clauses
) {
488 return a
->n_clauses
< b
->n_clauses
? -1 : 1;
491 /* We assume clauses are sorted */
492 for (i
= 0; i
< a
->n_clauses
; i
++) {
493 res
= (compare_clauses_3way_with_data(&a
->clauses
[i
], &b
->clauses
[i
]));
503 ovsdb_condition_clone(struct ovsdb_condition
*to
,
504 const struct ovsdb_condition
*from
)
508 ovsdb_condition_init(to
);
510 to
->clauses
= xzalloc(from
->n_clauses
* sizeof *to
->clauses
);
512 for (i
= 0; i
< from
->n_clauses
; i
++) {
513 ovsdb_clause_clone(&to
->clauses
[i
], &from
->clauses
[i
]);
515 to
->n_clauses
= from
->n_clauses
;
516 to
->optimized
= from
->optimized
;
518 ovsdb_condition_optimize(to
);
522 /* Return true if ovsdb_condition_match_any_clause() will return true on
525 ovsdb_condition_is_true(const struct ovsdb_condition
*cond
)
527 return (!cond
->n_clauses
||
528 (cond
->n_clauses
>= 1 && (cond
->clauses
[0].function
== OVSDB_F_TRUE
)) ||
529 (cond
->n_clauses
>= 2 && (cond
->clauses
[1].function
== OVSDB_F_TRUE
)));
533 ovsdb_condition_is_false(const struct ovsdb_condition
*cond
)
535 return ((cond
->n_clauses
== 1) &&
536 (cond
->clauses
[0].function
== OVSDB_F_FALSE
));
539 const struct ovsdb_column
**
540 ovsdb_condition_get_columns(const struct ovsdb_condition
*cond
,
543 const struct ovsdb_column
**columns
;
546 columns
= xmalloc(cond
->n_clauses
* sizeof *columns
);
547 for (i
= 0; i
< cond
->n_clauses
; i
++) {
548 columns
[i
] = cond
->clauses
[i
].column
;