Initial implementation of OVSDB.
[cascardo/ovs.git] / ovsdb / execution.c
1 /* Copyright (c) 2009 Nicira Networks
2  *
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 #include <config.h>
17
18 #include <assert.h>
19 #include <limits.h>
20
21 #include "column.h"
22 #include "condition.h"
23 #include "file.h"
24 #include "json.h"
25 #include "ovsdb-data.h"
26 #include "ovsdb-error.h"
27 #include "ovsdb-parser.h"
28 #include "ovsdb.h"
29 #include "query.h"
30 #include "row.h"
31 #include "table.h"
32 #include "timeval.h"
33 #include "transaction.h"
34
35 struct ovsdb_execution {
36     struct ovsdb *db;
37     struct ovsdb_txn *txn;
38     struct ovsdb_symbol_table *symtab;
39     bool durable;
40
41     /* Triggers. */
42     long long int elapsed_msec;
43     long long int timeout_msec;
44 };
45
46 typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *,
47                                                      struct ovsdb_parser *,
48                                                      struct json *result);
49
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;
58
59 static ovsdb_operation_executor *
60 lookup_executor(const char *name)
61 {
62     struct ovsdb_operation {
63         const char *name;
64         ovsdb_operation_executor *executor;
65     };
66
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 },
75     };
76
77     size_t i;
78
79     for (i = 0; i < ARRAY_SIZE(operations); i++) {
80         const struct ovsdb_operation *c = &operations[i];
81         if (!strcmp(c->name, name)) {
82             return c->executor;
83         }
84     }
85     return NULL;
86 }
87
88 struct json *
89 ovsdb_execute(struct ovsdb *db, const struct json *params,
90               long long int elapsed_msec, long long int *timeout_msec)
91 {
92     struct ovsdb_execution x;
93     struct ovsdb_error *error;
94     struct json *results;
95     size_t n_operations;
96     size_t i;
97
98     if (params->type != JSON_ARRAY) {
99         struct ovsdb_error *error;
100
101         error = ovsdb_syntax_error(params, NULL, "array expected");
102         results = ovsdb_error_to_json(error);
103         ovsdb_error_destroy(error);
104         return results;
105     }
106
107     x.db = db;
108     x.txn = ovsdb_txn_create(db);
109     x.symtab = ovsdb_symbol_table_create();
110     x.durable = false;
111     x.elapsed_msec = elapsed_msec;
112     x.timeout_msec = LLONG_MAX;
113     results = NULL;
114
115     results = json_array_create_empty();
116     n_operations = params->u.array.n;
117     error = NULL;
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;
122         struct json *result;
123         const struct json *op;
124
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();
130         if (op) {
131             const char *op_name = json_string(op);
132             ovsdb_operation_executor *executor = lookup_executor(op_name);
133             if (executor) {
134                 error = executor(&x, &parser, result);
135             } else {
136                 error = ovsdb_syntax_error(operation, "unknown operation",
137                                            "No operation \"%s\"", op_name);
138             }
139         } else {
140             assert(ovsdb_parser_has_error(&parser));
141         }
142
143         /* A parse error overrides any other error.
144          * An error overrides any other result. */
145         parse_error = ovsdb_parser_finish(&parser);
146         if (parse_error) {
147             ovsdb_error_destroy(error);
148             error = parse_error;
149         }
150         if (error) {
151             json_destroy(result);
152             result = ovsdb_error_to_json(error);
153         }
154         if (error && !strcmp(ovsdb_error_get_tag(error), "not supported")
155             && timeout_msec) {
156             ovsdb_txn_abort(x.txn);
157             *timeout_msec = x.timeout_msec;
158             ovsdb_error_destroy(error);
159             json_destroy(results);
160             return NULL;
161         }
162
163         /* Add result to array. */
164         json_array_add(results, result);
165         if (error) {
166             break;
167         }
168     }
169
170     if (!error) {
171         /* Commit transaction.  Bail if commit encounters error.  */
172         error = do_commit(&x);
173         if (error) {
174             json_array_add(results, ovsdb_error_to_json(error));
175         }
176     } else {
177         ovsdb_txn_abort(x.txn);
178     }
179
180     while (json_array(results)->n < n_operations) {
181         json_array_add(results, json_null_create());
182     }
183
184     ovsdb_error_destroy(error);
185     ovsdb_symbol_table_destroy(x.symtab);
186
187     return results;
188 }
189
190 struct ovsdb_error *
191 ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser,
192                      struct json *result UNUSED)
193 {
194     const struct json *durable;
195
196     durable = ovsdb_parser_member(parser, "durable", OP_BOOLEAN);
197     if (durable && json_boolean(durable)) {
198         x->durable = true;
199     }
200     return NULL;
201 }
202
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)
207 {
208     return ovsdb_error("aborted", "aborted by request");
209 }
210
211 static struct ovsdb_error *
212 do_commit(struct ovsdb_execution *x)
213 {
214     if (x->db->file) {
215         struct ovsdb_error *error;
216         struct json *json;
217
218         json = ovsdb_txn_to_json(x->txn);
219         if (!json) {
220             /* Nothing to commit. */
221             return NULL;
222         }
223
224         error = ovsdb_file_write(x->db->file, json);
225         json_destroy(json);
226         if (error) {
227             return ovsdb_wrap_error(error, "writing transaction failed");
228         }
229
230         if (x->durable) {
231             error = ovsdb_file_commit(x->db->file);
232             if (error) {
233                 return ovsdb_wrap_error(error,
234                                         "committing transaction failed");
235             }
236         }
237     }
238
239     ovsdb_txn_commit(x->txn);
240     return NULL;
241 }
242
243 static struct ovsdb_table *
244 parse_table(struct ovsdb_execution *x,
245             struct ovsdb_parser *parser, const char *member)
246 {
247     struct ovsdb_table *table;
248     const char *table_name;
249     const struct json *json;
250
251     json = ovsdb_parser_member(parser, member, OP_ID);
252     if (!json) {
253         return NULL;
254     }
255     table_name = json_string(json);
256
257     table = shash_find_data(&x->db->tables, table_name);
258     if (!table) {
259         ovsdb_parser_raise_error(parser, "No table named %s.", table_name);
260     }
261     return table;
262 }
263
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)
269 {
270     struct ovsdb_error *error;
271     const struct json *json;
272     struct ovsdb_row *row;
273
274     *rowp = NULL;
275
276     if (!table) {
277         return OVSDB_BUG("null table");
278     }
279     json = ovsdb_parser_member(parser, member, OP_OBJECT);
280     if (!json) {
281         return OVSDB_BUG("null row member");
282     }
283
284     row = ovsdb_row_create(table);
285     error = ovsdb_row_from_json(row, json, symtab, columns);
286     if (error) {
287         ovsdb_row_destroy(row);
288         return error;
289     } else {
290         *rowp = row;
291         return NULL;
292     }
293 }
294
295 struct ovsdb_error *
296 ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
297                      struct json *result)
298 {
299     struct ovsdb_table *table;
300     struct ovsdb_row *row = NULL;
301     const struct json *uuid_name;
302     struct ovsdb_error *error;
303
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);
307     if (!error) {
308         error = parse_row(parser, "row", table, x->symtab, &row, NULL);
309     }
310     if (!error) {
311         uuid_generate(ovsdb_row_get_uuid_rw(row));
312         if (uuid_name) {
313             ovsdb_symbol_table_put(x->symtab, json_string(uuid_name),
314                                    ovsdb_row_get_uuid(row));
315         }
316         ovsdb_txn_row_insert(x->txn, row);
317         json_object_put(result, "uuid",
318                         ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID],
319                                             &ovsdb_type_uuid));
320         row = NULL;
321     }
322     return error;
323 }
324
325 struct ovsdb_error *
326 ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser,
327                      struct json *result)
328 {
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;
335
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);
341
342     error = ovsdb_parser_get_error(parser);
343     if (!error) {
344         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
345                                           &condition);
346     }
347     if (!error) {
348         error = ovsdb_column_set_from_json(columns_json, table, &columns);
349     }
350     if (!error) {
351         error = ovsdb_column_set_from_json(sort_json, table, &sort);
352     }
353     if (!error) {
354         struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER;
355
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));
360
361         ovsdb_row_set_destroy(&rows);
362     }
363
364     ovsdb_column_set_destroy(&columns);
365     ovsdb_column_set_destroy(&sort);
366     ovsdb_condition_destroy(&condition);
367
368     return error;
369 }
370
371 struct update_row_cbdata {
372     size_t n_matches;
373     struct ovsdb_txn *txn;
374     const struct ovsdb_row *row;
375     const struct ovsdb_column_set *columns;
376 };
377
378 static bool
379 update_row_cb(const struct ovsdb_row *row, void *ur_)
380 {
381     struct update_row_cbdata *ur = ur_;
382
383     ur->n_matches++;
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);
387     }
388
389     return true;
390 }
391
392 struct ovsdb_error *
393 ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
394                      struct json *result)
395 {
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;
403
404     table = parse_table(x, parser, "table");
405     where = ovsdb_parser_member(parser, "where", OP_ARRAY);
406     error = ovsdb_parser_get_error(parser);
407     if (!error) {
408         error = parse_row(parser, "row", table, x->symtab, &row, &columns);
409     }
410     if (!error) {
411         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
412                                           &condition);
413     }
414     if (!error) {
415         ur.n_matches = 0;
416         ur.txn = x->txn;
417         ur.row = row;
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));
421     }
422
423     ovsdb_row_destroy(row);
424     ovsdb_column_set_destroy(&columns);
425     ovsdb_condition_destroy(&condition);
426
427     return error;
428 }
429
430 struct delete_row_cbdata {
431     size_t n_matches;
432     const struct ovsdb_table *table;
433     struct ovsdb_txn *txn;
434 };
435
436 static bool
437 delete_row_cb(const struct ovsdb_row *row, void *dr_)
438 {
439     struct delete_row_cbdata *dr = dr_;
440
441     dr->n_matches++;
442     ovsdb_txn_row_delete(dr->txn, row);
443
444     return true;
445 }
446
447 struct ovsdb_error *
448 ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
449                      struct json *result)
450 {
451     struct ovsdb_table *table;
452     const struct json *where;
453     struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
454     struct ovsdb_error *error;
455
456     where = ovsdb_parser_member(parser, "where", OP_ARRAY);
457     table = parse_table(x, parser, "table");
458     error = ovsdb_parser_get_error(parser);
459     if (!error) {
460         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
461                                           &condition);
462     }
463     if (!error) {
464         struct delete_row_cbdata dr;
465
466         dr.n_matches = 0;
467         dr.table = table;
468         dr.txn = x->txn;
469         ovsdb_query(table, &condition, delete_row_cb, &dr);
470
471         json_object_put(result, "count", json_integer_create(dr.n_matches));
472     }
473
474     ovsdb_condition_destroy(&condition);
475
476     return error;
477 }
478
479 struct wait_auxdata {
480     struct ovsdb_row_hash *actual;
481     struct ovsdb_row_hash *expected;
482     bool *equal;
483 };
484
485 static bool
486 ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_)
487 {
488     struct wait_auxdata *aux = aux_;
489
490     if (ovsdb_row_hash_contains(aux->expected, row)) {
491         ovsdb_row_hash_insert(aux->actual, row);
492         return true;
493     } else {
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. */
497         *aux->equal = false;
498         return false;
499     }
500 }
501
502 static struct ovsdb_error *
503 ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
504                    struct json *result UNUSED)
505 {
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;
515     size_t i;
516
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);
525     if (!error) {
526         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
527                                           &condition);
528     }
529     if (!error) {
530         error = ovsdb_column_set_from_json(columns_json, table, &columns);
531     }
532     if (!error) {
533         if (timeout) {
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;
540             }
541         } else {
542             timeout_msec = LLONG_MAX;
543         }
544         if (strcmp(json_string(until), "==")
545             && strcmp(json_string(until), "!=")) {
546             error = ovsdb_syntax_error(until, NULL,
547                                        "\"until\" must be \"==\" or \"!=\"");
548         }
549     }
550     if (!error) {
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;
556
557             row = ovsdb_row_create(table);
558             error = ovsdb_row_from_json(row, rows->u.array.elems[i], x->symtab,
559                                         NULL);
560             if (error) {
561                 break;
562             }
563
564             if (!ovsdb_row_hash_insert(&expected, row)) {
565                 /* XXX Perhaps we should abort with an error or log a
566                  * warning. */
567                 ovsdb_row_destroy(row);
568             }
569         }
570     }
571     if (!error) {
572         /* Execute query. */
573         bool equal = true;
574         ovsdb_row_hash_init(&actual, &columns);
575         aux.actual = &actual;
576         aux.expected = &expected;
577         aux.equal = &equal;
578         ovsdb_query(table, &condition, ovsdb_execute_wait_query_cb, &aux);
579         if (equal) {
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;
588         }
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",
594                                         x->elapsed_msec);
595                 } else {
596                     error = ovsdb_error("timed out", "\"wait\" timed out");
597                 }
598             } else {
599                 /* ovsdb_execute() will change this, if triggers really are
600                  * supported. */
601                 error = ovsdb_error("not supported", "triggers not supported");
602             }
603         }
604     }
605
606
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);
611
612     return error;
613 }