-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 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;
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)
{ "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;
}
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;
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;
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;
/* Parse and execute operation. */
ovsdb_parser_init(&parser, operation,
- "ovsdb operation %zu of %zu", i + 1, n_operations);
+ "ovsdb operation %zu of %zu", i, n_operations);
op = ovsdb_parser_member(&parser, "op", OP_ID);
result = json_object_create();
if (op) {
op_name);
}
} else {
- assert(ovsdb_parser_has_error(&parser));
+ ovs_assert(ovsdb_parser_has_error(&parser));
}
/* A parse error overrides any other error.
&& 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. */
}
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));
}
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;
}
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)
}
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;
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);
}
}
-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)
{
&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;
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;
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,
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;
return true;
}
-struct ovsdb_error *
+static struct ovsdb_error *
ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
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;
&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) {
/* 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);
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));
+}