1 /* Copyright (c) 2009 Nicira Networks
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.
22 #include "condition.h"
25 #include "ovsdb-data.h"
26 #include "ovsdb-error.h"
27 #include "ovsdb-parser.h"
33 #include "transaction.h"
35 struct ovsdb_execution
{
37 struct ovsdb_txn
*txn
;
38 struct ovsdb_symbol_table
*symtab
;
42 long long int elapsed_msec
;
43 long long int timeout_msec
;
46 typedef struct ovsdb_error
*ovsdb_operation_executor(struct ovsdb_execution
*,
47 struct ovsdb_parser
*,
50 static struct ovsdb_error
*do_commit(struct ovsdb_execution
*);
51 static ovsdb_operation_executor ovsdb_execute_insert
;
52 static ovsdb_operation_executor ovsdb_execute_select
;
53 static ovsdb_operation_executor ovsdb_execute_update
;
54 static ovsdb_operation_executor ovsdb_execute_delete
;
55 static ovsdb_operation_executor ovsdb_execute_wait
;
56 static ovsdb_operation_executor ovsdb_execute_commit
;
57 static ovsdb_operation_executor ovsdb_execute_abort
;
59 static ovsdb_operation_executor
*
60 lookup_executor(const char *name
)
62 struct ovsdb_operation
{
64 ovsdb_operation_executor
*executor
;
67 static const struct ovsdb_operation operations
[] = {
68 { "insert", ovsdb_execute_insert
},
69 { "select", ovsdb_execute_select
},
70 { "update", ovsdb_execute_update
},
71 { "delete", ovsdb_execute_delete
},
72 { "wait", ovsdb_execute_wait
},
73 { "commit", ovsdb_execute_commit
},
74 { "abort", ovsdb_execute_abort
},
79 for (i
= 0; i
< ARRAY_SIZE(operations
); i
++) {
80 const struct ovsdb_operation
*c
= &operations
[i
];
81 if (!strcmp(c
->name
, name
)) {
89 ovsdb_execute(struct ovsdb
*db
, const struct json
*params
,
90 long long int elapsed_msec
, long long int *timeout_msec
)
92 struct ovsdb_execution x
;
93 struct ovsdb_error
*error
;
98 if (params
->type
!= JSON_ARRAY
) {
99 struct ovsdb_error
*error
;
101 error
= ovsdb_syntax_error(params
, NULL
, "array expected");
102 results
= ovsdb_error_to_json(error
);
103 ovsdb_error_destroy(error
);
108 x
.txn
= ovsdb_txn_create(db
);
109 x
.symtab
= ovsdb_symbol_table_create();
111 x
.elapsed_msec
= elapsed_msec
;
112 x
.timeout_msec
= LLONG_MAX
;
115 results
= json_array_create_empty();
116 n_operations
= params
->u
.array
.n
;
118 for (i
= 0; i
< n_operations
; i
++) {
119 struct json
*operation
= params
->u
.array
.elems
[i
];
120 struct ovsdb_error
*parse_error
;
121 struct ovsdb_parser parser
;
123 const struct json
*op
;
125 /* Parse and execute operation. */
126 ovsdb_parser_init(&parser
, operation
,
127 "ovsdb operation %zu of %zu", i
+ 1, n_operations
);
128 op
= ovsdb_parser_member(&parser
, "op", OP_ID
);
129 result
= json_object_create();
131 const char *op_name
= json_string(op
);
132 ovsdb_operation_executor
*executor
= lookup_executor(op_name
);
134 error
= executor(&x
, &parser
, result
);
136 ovsdb_parser_raise_error(&parser
, "No operation \"%s\"",
140 assert(ovsdb_parser_has_error(&parser
));
143 /* A parse error overrides any other error.
144 * An error overrides any other result. */
145 parse_error
= ovsdb_parser_finish(&parser
);
147 ovsdb_error_destroy(error
);
151 json_destroy(result
);
152 result
= ovsdb_error_to_json(error
);
154 if (error
&& !strcmp(ovsdb_error_get_tag(error
), "not supported")
156 ovsdb_txn_abort(x
.txn
);
157 *timeout_msec
= x
.timeout_msec
;
158 ovsdb_error_destroy(error
);
159 json_destroy(results
);
163 /* Add result to array. */
164 json_array_add(results
, result
);
171 /* Commit transaction. Bail if commit encounters error. */
172 error
= do_commit(&x
);
174 json_array_add(results
, ovsdb_error_to_json(error
));
177 ovsdb_txn_abort(x
.txn
);
180 while (json_array(results
)->n
< n_operations
) {
181 json_array_add(results
, json_null_create());
184 ovsdb_error_destroy(error
);
185 ovsdb_symbol_table_destroy(x
.symtab
);
191 ovsdb_execute_commit(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
192 struct json
*result UNUSED
)
194 const struct json
*durable
;
196 durable
= ovsdb_parser_member(parser
, "durable", OP_BOOLEAN
);
197 if (durable
&& json_boolean(durable
)) {
203 static struct ovsdb_error
*
204 ovsdb_execute_abort(struct ovsdb_execution
*x UNUSED
,
205 struct ovsdb_parser
*parser UNUSED
,
206 struct json
*result UNUSED
)
208 return ovsdb_error("aborted", "aborted by request");
211 static struct ovsdb_error
*
212 do_commit(struct ovsdb_execution
*x
)
215 struct ovsdb_error
*error
;
218 json
= ovsdb_txn_to_json(x
->txn
);
220 /* Nothing to commit. */
224 error
= ovsdb_file_write(x
->db
->file
, json
);
227 return ovsdb_wrap_error(error
, "writing transaction failed");
231 error
= ovsdb_file_commit(x
->db
->file
);
233 return ovsdb_wrap_error(error
,
234 "committing transaction failed");
239 ovsdb_txn_commit(x
->txn
);
243 static struct ovsdb_table
*
244 parse_table(struct ovsdb_execution
*x
,
245 struct ovsdb_parser
*parser
, const char *member
)
247 struct ovsdb_table
*table
;
248 const char *table_name
;
249 const struct json
*json
;
251 json
= ovsdb_parser_member(parser
, member
, OP_ID
);
255 table_name
= json_string(json
);
257 table
= shash_find_data(&x
->db
->tables
, table_name
);
259 ovsdb_parser_raise_error(parser
, "No table named %s.", table_name
);
264 static WARN_UNUSED_RESULT
struct ovsdb_error
*
265 parse_row(struct ovsdb_parser
*parser
, const char *member
,
266 const struct ovsdb_table
*table
,
267 const struct ovsdb_symbol_table
*symtab
,
268 struct ovsdb_row
**rowp
, struct ovsdb_column_set
*columns
)
270 struct ovsdb_error
*error
;
271 const struct json
*json
;
272 struct ovsdb_row
*row
;
277 return OVSDB_BUG("null table");
279 json
= ovsdb_parser_member(parser
, member
, OP_OBJECT
);
281 return OVSDB_BUG("null row member");
284 row
= ovsdb_row_create(table
);
285 error
= ovsdb_row_from_json(row
, json
, symtab
, columns
);
287 ovsdb_row_destroy(row
);
296 ovsdb_execute_insert(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
299 struct ovsdb_table
*table
;
300 struct ovsdb_row
*row
= NULL
;
301 const struct json
*uuid_name
;
302 struct ovsdb_error
*error
;
304 table
= parse_table(x
, parser
, "table");
305 uuid_name
= ovsdb_parser_member(parser
, "uuid-name", OP_ID
| OP_OPTIONAL
);
306 error
= ovsdb_parser_get_error(parser
);
308 error
= parse_row(parser
, "row", table
, x
->symtab
, &row
, NULL
);
311 uuid_generate(ovsdb_row_get_uuid_rw(row
));
313 ovsdb_symbol_table_put(x
->symtab
, json_string(uuid_name
),
314 ovsdb_row_get_uuid(row
));
316 ovsdb_txn_row_insert(x
->txn
, row
);
317 json_object_put(result
, "uuid",
318 ovsdb_datum_to_json(&row
->fields
[OVSDB_COL_UUID
],
326 ovsdb_execute_select(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
329 struct ovsdb_table
*table
;
330 const struct json
*where
, *columns_json
, *sort_json
;
331 struct ovsdb_condition condition
= OVSDB_CONDITION_INITIALIZER
;
332 struct ovsdb_column_set columns
= OVSDB_COLUMN_SET_INITIALIZER
;
333 struct ovsdb_column_set sort
= OVSDB_COLUMN_SET_INITIALIZER
;
334 struct ovsdb_error
*error
;
336 table
= parse_table(x
, parser
, "table");
337 where
= ovsdb_parser_member(parser
, "where", OP_ARRAY
);
338 columns_json
= ovsdb_parser_member(parser
, "columns",
339 OP_ARRAY
| OP_OPTIONAL
);
340 sort_json
= ovsdb_parser_member(parser
, "sort", OP_ARRAY
| OP_OPTIONAL
);
342 error
= ovsdb_parser_get_error(parser
);
344 error
= ovsdb_condition_from_json(table
->schema
, where
, x
->symtab
,
348 error
= ovsdb_column_set_from_json(columns_json
, table
, &columns
);
351 error
= ovsdb_column_set_from_json(sort_json
, table
, &sort
);
354 struct ovsdb_row_set rows
= OVSDB_ROW_SET_INITIALIZER
;
356 ovsdb_query_distinct(table
, &condition
, &columns
, &rows
);
357 ovsdb_row_set_sort(&rows
, &sort
);
358 json_object_put(result
, "rows",
359 ovsdb_row_set_to_json(&rows
, &columns
));
361 ovsdb_row_set_destroy(&rows
);
364 ovsdb_column_set_destroy(&columns
);
365 ovsdb_column_set_destroy(&sort
);
366 ovsdb_condition_destroy(&condition
);
371 struct update_row_cbdata
{
373 struct ovsdb_txn
*txn
;
374 const struct ovsdb_row
*row
;
375 const struct ovsdb_column_set
*columns
;
379 update_row_cb(const struct ovsdb_row
*row
, void *ur_
)
381 struct update_row_cbdata
*ur
= ur_
;
384 if (!ovsdb_row_equal_columns(row
, ur
->row
, ur
->columns
)) {
385 ovsdb_row_update_columns(ovsdb_txn_row_modify(ur
->txn
, row
),
386 ur
->row
, ur
->columns
);
393 ovsdb_execute_update(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
396 struct ovsdb_table
*table
;
397 const struct json
*where
;
398 struct ovsdb_condition condition
= OVSDB_CONDITION_INITIALIZER
;
399 struct ovsdb_column_set columns
= OVSDB_COLUMN_SET_INITIALIZER
;
400 struct ovsdb_row
*row
= NULL
;
401 struct update_row_cbdata ur
;
402 struct ovsdb_error
*error
;
404 table
= parse_table(x
, parser
, "table");
405 where
= ovsdb_parser_member(parser
, "where", OP_ARRAY
);
406 error
= ovsdb_parser_get_error(parser
);
408 error
= parse_row(parser
, "row", table
, x
->symtab
, &row
, &columns
);
411 error
= ovsdb_condition_from_json(table
->schema
, where
, x
->symtab
,
418 ur
.columns
= &columns
;
419 ovsdb_query(table
, &condition
, update_row_cb
, &ur
);
420 json_object_put(result
, "count", json_integer_create(ur
.n_matches
));
423 ovsdb_row_destroy(row
);
424 ovsdb_column_set_destroy(&columns
);
425 ovsdb_condition_destroy(&condition
);
430 struct delete_row_cbdata
{
432 const struct ovsdb_table
*table
;
433 struct ovsdb_txn
*txn
;
437 delete_row_cb(const struct ovsdb_row
*row
, void *dr_
)
439 struct delete_row_cbdata
*dr
= dr_
;
442 ovsdb_txn_row_delete(dr
->txn
, row
);
448 ovsdb_execute_delete(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
451 struct ovsdb_table
*table
;
452 const struct json
*where
;
453 struct ovsdb_condition condition
= OVSDB_CONDITION_INITIALIZER
;
454 struct ovsdb_error
*error
;
456 where
= ovsdb_parser_member(parser
, "where", OP_ARRAY
);
457 table
= parse_table(x
, parser
, "table");
458 error
= ovsdb_parser_get_error(parser
);
460 error
= ovsdb_condition_from_json(table
->schema
, where
, x
->symtab
,
464 struct delete_row_cbdata dr
;
469 ovsdb_query(table
, &condition
, delete_row_cb
, &dr
);
471 json_object_put(result
, "count", json_integer_create(dr
.n_matches
));
474 ovsdb_condition_destroy(&condition
);
479 struct wait_auxdata
{
480 struct ovsdb_row_hash
*actual
;
481 struct ovsdb_row_hash
*expected
;
486 ovsdb_execute_wait_query_cb(const struct ovsdb_row
*row
, void *aux_
)
488 struct wait_auxdata
*aux
= aux_
;
490 if (ovsdb_row_hash_contains(aux
->expected
, row
)) {
491 ovsdb_row_hash_insert(aux
->actual
, row
);
494 /* The query row isn't in the expected result set, so the actual and
495 * expected results sets definitely differ and we can short-circuit the
496 * rest of the query. */
502 static struct ovsdb_error
*
503 ovsdb_execute_wait(struct ovsdb_execution
*x
, struct ovsdb_parser
*parser
,
504 struct json
*result UNUSED
)
506 struct ovsdb_table
*table
;
507 const struct json
*timeout
, *where
, *columns_json
, *until
, *rows
;
508 struct ovsdb_condition condition
= OVSDB_CONDITION_INITIALIZER
;
509 struct ovsdb_column_set columns
= OVSDB_COLUMN_SET_INITIALIZER
;
510 struct ovsdb_row_hash expected
= OVSDB_ROW_HASH_INITIALIZER(expected
);
511 struct ovsdb_row_hash actual
= OVSDB_ROW_HASH_INITIALIZER(actual
);
512 struct ovsdb_error
*error
;
513 struct wait_auxdata aux
;
514 long long int timeout_msec
= 0;
517 timeout
= ovsdb_parser_member(parser
, "timeout", OP_NUMBER
| OP_OPTIONAL
);
518 where
= ovsdb_parser_member(parser
, "where", OP_ARRAY
);
519 columns_json
= ovsdb_parser_member(parser
, "columns",
520 OP_ARRAY
| OP_OPTIONAL
);
521 until
= ovsdb_parser_member(parser
, "until", OP_STRING
);
522 rows
= ovsdb_parser_member(parser
, "rows", OP_ARRAY
);
523 table
= parse_table(x
, parser
, "table");
524 error
= ovsdb_parser_get_error(parser
);
526 error
= ovsdb_condition_from_json(table
->schema
, where
, x
->symtab
,
530 error
= ovsdb_column_set_from_json(columns_json
, table
, &columns
);
534 timeout_msec
= MIN(LLONG_MAX
, json_real(timeout
));
535 if (timeout_msec
< 0) {
536 error
= ovsdb_syntax_error(timeout
, NULL
,
537 "timeout must be nonnegative");
538 } else if (timeout_msec
< x
->timeout_msec
) {
539 x
->timeout_msec
= timeout_msec
;
542 timeout_msec
= LLONG_MAX
;
544 if (strcmp(json_string(until
), "==")
545 && strcmp(json_string(until
), "!=")) {
546 error
= ovsdb_syntax_error(until
, NULL
,
547 "\"until\" must be \"==\" or \"!=\"");
551 /* Parse "rows" into 'expected'. */
552 ovsdb_row_hash_init(&expected
, &columns
);
553 for (i
= 0; i
< rows
->u
.array
.n
; i
++) {
554 struct ovsdb_error
*error
;
555 struct ovsdb_row
*row
;
557 row
= ovsdb_row_create(table
);
558 error
= ovsdb_row_from_json(row
, rows
->u
.array
.elems
[i
], x
->symtab
,
564 if (!ovsdb_row_hash_insert(&expected
, row
)) {
565 /* XXX Perhaps we should abort with an error or log a
567 ovsdb_row_destroy(row
);
574 ovsdb_row_hash_init(&actual
, &columns
);
575 aux
.actual
= &actual
;
576 aux
.expected
= &expected
;
578 ovsdb_query(table
, &condition
, ovsdb_execute_wait_query_cb
, &aux
);
580 /* We know that every row in 'actual' is also in 'expected'. We
581 * also know that all of the rows in 'actual' are distinct and that
582 * all of the rows in 'expected' are distinct. Therefore, if
583 * 'actual' and 'expected' have the same number of rows, then they
584 * have the same content. */
585 size_t n_actual
= ovsdb_row_hash_count(&actual
);
586 size_t n_expected
= ovsdb_row_hash_count(&expected
);
587 equal
= n_actual
== n_expected
;
589 if (!strcmp(json_string(until
), "==") != equal
) {
590 if (timeout
&& x
->elapsed_msec
>= timeout_msec
) {
591 if (x
->elapsed_msec
) {
592 error
= ovsdb_error("timed out",
593 "\"wait\" timed out after %lld ms",
596 error
= ovsdb_error("timed out", "\"wait\" timed out");
599 /* ovsdb_execute() will change this, if triggers really are
601 error
= ovsdb_error("not supported", "triggers not supported");
607 ovsdb_row_hash_destroy(&expected
, true);
608 ovsdb_row_hash_destroy(&actual
, false);
609 ovsdb_column_set_destroy(&columns
);
610 ovsdb_condition_destroy(&condition
);