-/* 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 "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;
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_declare;
+static ovsdb_operation_executor ovsdb_execute_comment;
+static ovsdb_operation_executor ovsdb_execute_assert;
static ovsdb_operation_executor *
lookup_executor(const char *name)
{ "wait", ovsdb_execute_wait },
{ "commit", ovsdb_execute_commit },
{ "abort", ovsdb_execute_abort },
- { "declare", ovsdb_execute_declare },
+ { "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 %"PRIuSIZE" of %"PRIuSIZE, 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. */
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 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_get(x->symtab, json_string(uuid_name));
- if (symbol) {
- if (symbol->used) {
- return ovsdb_syntax_error(uuid_name, "duplicate uuid-name",
- "This \"uuid-name\" appeared on an "
- "earlier \"insert\" operation.");
- }
- row_uuid = symbol->uuid;
- symbol->used = true;
- } else {
- uuid_generate(&row_uuid);
- ovsdb_symbol_table_put(x->symtab, json_string(uuid_name),
- &row_uuid, true);
+ 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) {
+ /* 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;
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,
size_t n_matches;
struct ovsdb_txn *txn;
const struct ovsdb_mutation_set *mutations;
+ struct ovsdb_error **error;
};
static bool
struct mutate_row_cbdata *mr = mr_;
mr->n_matches++;
- ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row),
- mr->mutations);
-
- return true;
+ *mr->error = ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row),
+ mr->mutations);
+ return *mr->error == NULL;
}
-struct ovsdb_error *
+static struct ovsdb_error *
ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser,
struct json *result)
{
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));
}
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);
}
static struct ovsdb_error *
-ovsdb_execute_declare(struct ovsdb_execution *x, struct ovsdb_parser *parser,
- struct json *result)
+ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser,
+ struct json *result OVS_UNUSED)
{
- const struct json *uuid_name;
- struct uuid uuid;
+ const struct json *comment;
- uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID);
- if (!uuid_name) {
+ comment = ovsdb_parser_member(parser, "comment", OP_STRING);
+ if (!comment) {
return NULL;
}
+ ovsdb_txn_add_comment(x->txn, json_string(comment));
- if (ovsdb_symbol_table_get(x->symtab, json_string(uuid_name))) {
- return ovsdb_syntax_error(uuid_name, "duplicate uuid-name",
- "This \"uuid-name\" appeared on an "
- "earlier \"declare\" or \"insert\" "
- "operation.");
+ 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;
}
- uuid_generate(&uuid);
- ovsdb_symbol_table_put(x->symtab, json_string(uuid_name), &uuid, false);
- json_object_put(result, "uuid", json_string_create_nocopy(
- xasprintf(UUID_FMT, UUID_ARGS(&uuid))));
- 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));
}