revalidator: Fix ukey stats cache updating.
[sliver-openvswitch.git] / ovsdb / execution.c
index 25b34b1..7a1db0c 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include <config.h>
 
-#include <assert.h>
 #include <limits.h>
 
 #include "column.h"
 #include "condition.h"
 #include "file.h"
 #include "json.h"
+#include "mutation.h"
 #include "ovsdb-data.h"
 #include "ovsdb-error.h"
 #include "ovsdb-parser.h"
 #include "ovsdb.h"
 #include "query.h"
 #include "row.h"
+#include "server.h"
 #include "table.h"
 #include "timeval.h"
 #include "transaction.h"
 
 struct ovsdb_execution {
     struct ovsdb *db;
+    const struct ovsdb_session *session;
     struct ovsdb_txn *txn;
     struct ovsdb_symbol_table *symtab;
     bool durable;
@@ -47,14 +49,16 @@ typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *,
                                                      struct ovsdb_parser *,
                                                      struct json *result);
 
-static struct ovsdb_error *do_commit(struct ovsdb_execution *);
 static ovsdb_operation_executor ovsdb_execute_insert;
 static ovsdb_operation_executor ovsdb_execute_select;
 static ovsdb_operation_executor ovsdb_execute_update;
+static ovsdb_operation_executor ovsdb_execute_mutate;
 static ovsdb_operation_executor ovsdb_execute_delete;
 static ovsdb_operation_executor ovsdb_execute_wait;
 static ovsdb_operation_executor ovsdb_execute_commit;
 static ovsdb_operation_executor ovsdb_execute_abort;
+static ovsdb_operation_executor ovsdb_execute_comment;
+static ovsdb_operation_executor ovsdb_execute_assert;
 
 static ovsdb_operation_executor *
 lookup_executor(const char *name)
@@ -68,10 +72,13 @@ lookup_executor(const char *name)
         { "insert", ovsdb_execute_insert },
         { "select", ovsdb_execute_select },
         { "update", ovsdb_execute_update },
+        { "mutate", ovsdb_execute_mutate },
         { "delete", ovsdb_execute_delete },
         { "wait", ovsdb_execute_wait },
         { "commit", ovsdb_execute_commit },
         { "abort", ovsdb_execute_abort },
+        { "comment", ovsdb_execute_comment },
+        { "assert", ovsdb_execute_assert },
     };
 
     size_t i;
@@ -86,7 +93,8 @@ lookup_executor(const char *name)
 }
 
 struct json *
-ovsdb_execute(struct ovsdb *db, const struct json *params,
+ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session,
+              const struct json *params,
               long long int elapsed_msec, long long int *timeout_msec)
 {
     struct ovsdb_execution x;
@@ -95,16 +103,24 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
     size_t n_operations;
     size_t i;
 
-    if (params->type != JSON_ARRAY) {
-        struct ovsdb_error *error;
+    if (params->type != JSON_ARRAY
+        || !params->u.array.n
+        || params->u.array.elems[0]->type != JSON_STRING
+        || strcmp(params->u.array.elems[0]->u.string, db->schema->name)) {
+        if (params->type != JSON_ARRAY) {
+            error = ovsdb_syntax_error(params, NULL, "array expected");
+        } else {
+            error = ovsdb_syntax_error(params, NULL, "database name expected "
+                                       "as first parameter");
+        }
 
-        error = ovsdb_syntax_error(params, NULL, "array expected");
         results = ovsdb_error_to_json(error);
         ovsdb_error_destroy(error);
         return results;
     }
 
     x.db = db;
+    x.session = session;
     x.txn = ovsdb_txn_create(db);
     x.symtab = ovsdb_symbol_table_create();
     x.durable = false;
@@ -113,9 +129,9 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
     results = NULL;
 
     results = json_array_create_empty();
-    n_operations = params->u.array.n;
+    n_operations = params->u.array.n - 1;
     error = NULL;
-    for (i = 0; i < n_operations; i++) {
+    for (i = 1; i <= n_operations; i++) {
         struct json *operation = params->u.array.elems[i];
         struct ovsdb_error *parse_error;
         struct ovsdb_parser parser;
@@ -124,7 +140,7 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
 
         /* Parse and execute operation. */
         ovsdb_parser_init(&parser, operation,
-                          "ovsdb operation %zu of %zu", i + 1, n_operations);
+                          "ovsdb operation %"PRIuSIZE" of %"PRIuSIZE, i, n_operations);
         op = ovsdb_parser_member(&parser, "op", OP_ID);
         result = json_object_create();
         if (op) {
@@ -133,11 +149,11 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
             if (executor) {
                 error = executor(&x, &parser, result);
             } else {
-                error = ovsdb_syntax_error(operation, "unknown operation",
-                                           "No operation \"%s\"", op_name);
+                ovsdb_parser_raise_error(&parser, "No operation \"%s\"",
+                                         op_name);
             }
         } else {
-            assert(ovsdb_parser_has_error(&parser));
+            ovs_assert(ovsdb_parser_has_error(&parser));
         }
 
         /* A parse error overrides any other error.
@@ -155,9 +171,11 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
             && timeout_msec) {
             ovsdb_txn_abort(x.txn);
             *timeout_msec = x.timeout_msec;
-            ovsdb_error_destroy(error);
+
+            json_destroy(result);
             json_destroy(results);
-            return NULL;
+            results = NULL;
+            goto exit;
         }
 
         /* Add result to array. */
@@ -168,8 +186,7 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
     }
 
     if (!error) {
-        /* Commit transaction.  Bail if commit encounters error.  */
-        error = do_commit(&x);
+        error = ovsdb_txn_commit(x.txn, x.durable);
         if (error) {
             json_array_add(results, ovsdb_error_to_json(error));
         }
@@ -181,15 +198,16 @@ ovsdb_execute(struct ovsdb *db, const struct json *params,
         json_array_add(results, json_null_create());
     }
 
+exit:
     ovsdb_error_destroy(error);
     ovsdb_symbol_table_destroy(x.symtab);
 
     return results;
 }
 
-struct ovsdb_error *
+static struct ovsdb_error *
 ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser,
-                     struct json *result UNUSED)
+                     struct json *result OVS_UNUSED)
 {
     const struct json *durable;
 
@@ -201,45 +219,13 @@ ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 }
 
 static struct ovsdb_error *
-ovsdb_execute_abort(struct ovsdb_execution *x UNUSED,
-                    struct ovsdb_parser *parser UNUSED,
-                    struct json *result UNUSED)
+ovsdb_execute_abort(struct ovsdb_execution *x OVS_UNUSED,
+                    struct ovsdb_parser *parser OVS_UNUSED,
+                    struct json *result OVS_UNUSED)
 {
     return ovsdb_error("aborted", "aborted by request");
 }
 
-static struct ovsdb_error *
-do_commit(struct ovsdb_execution *x)
-{
-    if (x->db->file) {
-        struct ovsdb_error *error;
-        struct json *json;
-
-        json = ovsdb_txn_to_json(x->txn);
-        if (!json) {
-            /* Nothing to commit. */
-            return NULL;
-        }
-
-        error = ovsdb_file_write(x->db->file, json);
-        json_destroy(json);
-        if (error) {
-            return ovsdb_wrap_error(error, "writing transaction failed");
-        }
-
-        if (x->durable) {
-            error = ovsdb_file_commit(x->db->file);
-            if (error) {
-                return ovsdb_wrap_error(error,
-                                        "committing transaction failed");
-            }
-        }
-    }
-
-    ovsdb_txn_commit(x->txn);
-    return NULL;
-}
-
 static struct ovsdb_table *
 parse_table(struct ovsdb_execution *x,
             struct ovsdb_parser *parser, const char *member)
@@ -262,13 +248,11 @@ parse_table(struct ovsdb_execution *x,
 }
 
 static WARN_UNUSED_RESULT struct ovsdb_error *
-parse_row(struct ovsdb_parser *parser, const char *member,
-          const struct ovsdb_table *table,
-          const struct ovsdb_symbol_table *symtab,
+parse_row(const struct json *json, const struct ovsdb_table *table,
+          struct ovsdb_symbol_table *symtab,
           struct ovsdb_row **rowp, struct ovsdb_column_set *columns)
 {
     struct ovsdb_error *error;
-    const struct json *json;
     struct ovsdb_row *row;
 
     *rowp = NULL;
@@ -276,9 +260,8 @@ parse_row(struct ovsdb_parser *parser, const char *member,
     if (!table) {
         return OVSDB_BUG("null table");
     }
-    json = ovsdb_parser_member(parser, member, OP_OBJECT);
     if (!json) {
-        return OVSDB_BUG("null row member");
+        return OVSDB_BUG("null row");
     }
 
     row = ovsdb_row_create(table);
@@ -292,37 +275,78 @@ parse_row(struct ovsdb_parser *parser, const char *member,
     }
 }
 
-struct ovsdb_error *
+static struct ovsdb_error *
 ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                      struct json *result)
 {
     struct ovsdb_table *table;
     struct ovsdb_row *row = NULL;
-    const struct json *uuid_name;
+    const struct json *uuid_name, *row_json;
     struct ovsdb_error *error;
+    struct uuid row_uuid;
 
     table = parse_table(x, parser, "table");
     uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID | OP_OPTIONAL);
+    row_json = ovsdb_parser_member(parser, "row", OP_OBJECT);
     error = ovsdb_parser_get_error(parser);
+    if (error) {
+        return error;
+    }
+
+    if (uuid_name) {
+        struct ovsdb_symbol *symbol;
+
+        symbol = ovsdb_symbol_table_insert(x->symtab, json_string(uuid_name));
+        if (symbol->created) {
+            return ovsdb_syntax_error(uuid_name, "duplicate uuid-name",
+                                      "This \"uuid-name\" appeared on an "
+                                      "earlier \"insert\" operation.");
+        }
+        row_uuid = symbol->uuid;
+        symbol->created = true;
+    } else {
+        uuid_generate(&row_uuid);
+    }
+
     if (!error) {
-        error = parse_row(parser, "row", table, x->symtab, &row, NULL);
+        error = parse_row(row_json, table, x->symtab, &row, NULL);
     }
     if (!error) {
-        uuid_generate(ovsdb_row_get_uuid_rw(row));
-        if (uuid_name) {
-            ovsdb_symbol_table_put(x->symtab, json_string(uuid_name),
-                                   ovsdb_row_get_uuid(row));
+        /* Check constraints for columns not included in "row", in case the
+         * default values do not satisfy the constraints.  We could check only
+         * the columns that have their default values by supplying an
+         * ovsdb_column_set to parse_row() above, but I suspect that this is
+         * cheaper.  */
+        const struct shash_node *node;
+
+        SHASH_FOR_EACH (node, &table->schema->columns) {
+            const struct ovsdb_column *column = node->data;
+            const struct ovsdb_datum *datum = &row->fields[column->index];
+
+            /* If there are 0 keys or pairs, there's nothing to check.
+             * If there is 1, it might be a default value.
+             * If there are more, it can't be a default value, so the value has
+             * already been checked. */
+            if (datum->n == 1) {
+                error = ovsdb_datum_check_constraints(datum, &column->type);
+                if (error) {
+                    ovsdb_row_destroy(row);
+                    break;
+                }
+            }
         }
+    }
+    if (!error) {
+        *ovsdb_row_get_uuid_rw(row) = row_uuid;
         ovsdb_txn_row_insert(x->txn, row);
         json_object_put(result, "uuid",
                         ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID],
                                             &ovsdb_type_uuid));
-        row = NULL;
     }
     return error;
 }
 
-struct ovsdb_error *
+static struct ovsdb_error *
 ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                      struct json *result)
 {
@@ -345,10 +369,11 @@ ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                                           &condition);
     }
     if (!error) {
-        error = ovsdb_column_set_from_json(columns_json, table, &columns);
+        error = ovsdb_column_set_from_json(columns_json, table->schema,
+                                           &columns);
     }
     if (!error) {
-        error = ovsdb_column_set_from_json(sort_json, table, &sort);
+        error = ovsdb_column_set_from_json(sort_json, table->schema, &sort);
     }
     if (!error) {
         struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER;
@@ -389,12 +414,12 @@ update_row_cb(const struct ovsdb_row *row, void *ur_)
     return true;
 }
 
-struct ovsdb_error *
+static struct ovsdb_error *
 ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                      struct json *result)
 {
     struct ovsdb_table *table;
-    const struct json *where;
+    const struct json *where, *row_json;
     struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
     struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER;
     struct ovsdb_row *row = NULL;
@@ -403,9 +428,26 @@ ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 
     table = parse_table(x, parser, "table");
     where = ovsdb_parser_member(parser, "where", OP_ARRAY);
+    row_json = ovsdb_parser_member(parser, "row", OP_OBJECT);
     error = ovsdb_parser_get_error(parser);
     if (!error) {
-        error = parse_row(parser, "row", table, x->symtab, &row, &columns);
+        error = parse_row(row_json, table, x->symtab, &row, &columns);
+    }
+    if (!error) {
+        size_t i;
+
+        for (i = 0; i < columns.n_columns; i++) {
+            const struct ovsdb_column *column = columns.columns[i];
+
+            if (!column->mutable) {
+                error = ovsdb_syntax_error(parser->json,
+                                           "constraint violation",
+                                           "Cannot update immutable column %s "
+                                           "in table %s.",
+                                           column->name, table->schema->name);
+                break;
+            }
+        }
     }
     if (!error) {
         error = ovsdb_condition_from_json(table->schema, where, x->symtab,
@@ -427,6 +469,65 @@ ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser,
     return error;
 }
 
+struct mutate_row_cbdata {
+    size_t n_matches;
+    struct ovsdb_txn *txn;
+    const struct ovsdb_mutation_set *mutations;
+    struct ovsdb_error **error;
+};
+
+static bool
+mutate_row_cb(const struct ovsdb_row *row, void *mr_)
+{
+    struct mutate_row_cbdata *mr = mr_;
+
+    mr->n_matches++;
+    *mr->error = ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row),
+                                            mr->mutations);
+    return *mr->error == NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser,
+                     struct json *result)
+{
+    struct ovsdb_table *table;
+    const struct json *where;
+    const struct json *mutations_json;
+    struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER;
+    struct ovsdb_mutation_set mutations = OVSDB_MUTATION_SET_INITIALIZER;
+    struct ovsdb_row *row = NULL;
+    struct mutate_row_cbdata mr;
+    struct ovsdb_error *error;
+
+    table = parse_table(x, parser, "table");
+    where = ovsdb_parser_member(parser, "where", OP_ARRAY);
+    mutations_json = ovsdb_parser_member(parser, "mutations", OP_ARRAY);
+    error = ovsdb_parser_get_error(parser);
+    if (!error) {
+        error = ovsdb_mutation_set_from_json(table->schema, mutations_json,
+                                             x->symtab, &mutations);
+    }
+    if (!error) {
+        error = ovsdb_condition_from_json(table->schema, where, x->symtab,
+                                          &condition);
+    }
+    if (!error) {
+        mr.n_matches = 0;
+        mr.txn = x->txn;
+        mr.mutations = &mutations;
+        mr.error = &error;
+        ovsdb_query(table, &condition, mutate_row_cb, &mr);
+        json_object_put(result, "count", json_integer_create(mr.n_matches));
+    }
+
+    ovsdb_row_destroy(row);
+    ovsdb_mutation_set_destroy(&mutations);
+    ovsdb_condition_destroy(&condition);
+
+    return error;
+}
+
 struct delete_row_cbdata {
     size_t n_matches;
     const struct ovsdb_table *table;
@@ -444,7 +545,7 @@ delete_row_cb(const struct ovsdb_row *row, void *dr_)
     return true;
 }
 
-struct ovsdb_error *
+static struct ovsdb_error *
 ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                      struct json *result)
 {
@@ -501,7 +602,7 @@ ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_)
 
 static struct ovsdb_error *
 ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
-                   struct json *result UNUSED)
+                   struct json *result OVS_UNUSED)
 {
     struct ovsdb_table *table;
     const struct json *timeout, *where, *columns_json, *until, *rows;
@@ -527,7 +628,8 @@ ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
                                           &condition);
     }
     if (!error) {
-        error = ovsdb_column_set_from_json(columns_json, table, &columns);
+        error = ovsdb_column_set_from_json(columns_json, table->schema,
+                                           &columns);
     }
     if (!error) {
         if (timeout) {
@@ -551,7 +653,6 @@ ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
         /* Parse "rows" into 'expected'. */
         ovsdb_row_hash_init(&expected, &columns);
         for (i = 0; i < rows->u.array.n; i++) {
-            struct ovsdb_error *error;
             struct ovsdb_row *row;
 
             row = ovsdb_row_create(table);
@@ -611,3 +712,43 @@ ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser,
 
     return error;
 }
+
+static struct ovsdb_error *
+ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser,
+                      struct json *result OVS_UNUSED)
+{
+    const struct json *comment;
+
+    comment = ovsdb_parser_member(parser, "comment", OP_STRING);
+    if (!comment) {
+        return NULL;
+    }
+    ovsdb_txn_add_comment(x->txn, json_string(comment));
+
+    return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_execute_assert(struct ovsdb_execution *x, struct ovsdb_parser *parser,
+                     struct json *result OVS_UNUSED)
+{
+    const struct json *lock_name;
+
+    lock_name = ovsdb_parser_member(parser, "lock", OP_ID);
+    if (!lock_name) {
+        return NULL;
+    }
+
+    if (x->session) {
+        const struct ovsdb_lock_waiter *waiter;
+
+        waiter = ovsdb_session_get_lock_waiter(x->session,
+                                               json_string(lock_name));
+        if (waiter && ovsdb_lock_waiter_is_owner(waiter)) {
+            return NULL;
+        }
+    }
+
+    return ovsdb_error("not owner", "Asserted lock %s not held.",
+                       json_string(lock_name));
+}