#include "ovsdb-data.h"
#include <assert.h>
+#include <limits.h>
#include "hash.h"
#include "ovsdb-error.h"
}
}
-static struct ovsdb_error *
+struct ovsdb_error *
ovsdb_datum_sort(struct ovsdb_datum *datum, const struct ovsdb_type *type)
{
if (datum->n < 2) {
a->n));
}
-static bool
-ovsdb_datum_contains(const struct ovsdb_datum *a, int i,
- const struct ovsdb_datum *b,
- const struct ovsdb_type *type)
+/* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise
+ * UINT_MAX. 'type' must be the type of 'a' and 'b', except that
+ * type->value_type may be set to OVSDB_TYPE_VOID to compare keys but not
+ * values. */
+static unsigned int
+ovsdb_datum_find(const struct ovsdb_datum *a, int i,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *type)
{
int low = 0;
int high = b->n;
while (low < high) {
int j = (low + high) / 2;
- int cmp = ovsdb_atom_compare_3way(&a->keys[i], &b->keys[j], type->key_type);
+ int cmp = ovsdb_atom_compare_3way(&a->keys[i], &b->keys[j],
+ type->key_type);
if (cmp < 0) {
high = j;
} else if (cmp > 0) {
low = j + 1;
} else {
- return (type->value_type == OVSDB_TYPE_VOID
- || ovsdb_atom_equals(&a->values[i], &b->values[j],
- type->value_type));
+ bool eq_value = (type->value_type == OVSDB_TYPE_VOID
+ || ovsdb_atom_equals(&a->values[i], &b->values[j],
+ type->value_type));
+ return eq_value ? j : UINT_MAX;
}
}
- return false;
+ return UINT_MAX;
}
/* Returns true if every element in 'a' is also in 'b', false otherwise. */
size_t i;
for (i = 0; i < a->n; i++) {
- if (!ovsdb_datum_contains(a, i, b, type)) {
+ if (ovsdb_datum_find(a, i, b, type) == UINT_MAX) {
return false;
}
}
size_t i;
for (i = 0; i < a->n; i++) {
- if (ovsdb_datum_contains(a, i, b, type)) {
+ if (ovsdb_datum_find(a, i, b, type) != UINT_MAX) {
return false;
}
}
return true;
}
+
+static void
+ovsdb_datum_reallocate(struct ovsdb_datum *a, const struct ovsdb_type *type,
+ unsigned int capacity)
+{
+ a->keys = xrealloc(a->keys, capacity * sizeof *a->keys);
+ if (type->value_type != OVSDB_TYPE_VOID) {
+ a->values = xrealloc(a->values, capacity * sizeof *a->values);
+ }
+}
+
+static void
+ovsdb_datum_remove(struct ovsdb_datum *a, size_t i,
+ const struct ovsdb_type *type)
+{
+ ovsdb_atom_destroy(&a->keys[i], type->key_type);
+ a->keys[i] = a->keys[a->n - 1];
+ if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_destroy(&a->values[i], type->value_type);
+ a->values[i] = a->values[a->n - 1];
+ }
+ a->n--;
+}
+
+void
+ovsdb_datum_union(struct ovsdb_datum *a,
+ const struct ovsdb_datum *b, const struct ovsdb_type *type)
+{
+ struct ovsdb_type type_without_value;
+ unsigned int n;
+ size_t i;
+
+ type_without_value = *type;
+ type_without_value.value_type = OVSDB_TYPE_VOID;
+ n = a->n;
+ for (i = 0; i < b->n; i++) {
+ if (ovsdb_datum_find(b, i, a, &type_without_value) == UINT_MAX) {
+ if (n == a->n) {
+ ovsdb_datum_reallocate(a, type, a->n + (b->n - i));
+ }
+ ovsdb_atom_clone(&a->keys[n], &b->keys[i], type->key_type);
+ if (type->value_type != OVSDB_TYPE_VOID) {
+ ovsdb_atom_clone(&a->values[n], &b->values[i],
+ type->value_type);
+ }
+ n++;
+ }
+ }
+ if (n != a->n) {
+ struct ovsdb_error *error;
+ a->n = n;
+ error = ovsdb_datum_sort(a, type);
+ assert(!error);
+ }
+}
+
+void
+ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *b_type)
+{
+ bool changed = false;
+ size_t i;
+
+ assert(a_type->key_type == b_type->key_type);
+ assert(a_type->value_type == b_type->value_type
+ || b_type->value_type == OVSDB_TYPE_VOID);
+
+ /* XXX The big-O of this could easily be improved. */
+ for (i = 0; i < a->n; ) {
+ unsigned int idx = ovsdb_datum_find(a, i, b, b_type);
+ if (idx != UINT_MAX) {
+ changed = true;
+ ovsdb_datum_remove(a, i, a_type);
+ } else {
+ i++;
+ }
+ }
+ if (changed) {
+ struct ovsdb_error *error = ovsdb_datum_sort(a, a_type);
+ assert(!error);
+ }
+}
\f
struct ovsdb_symbol_table {
struct shash sh;
const struct ovsdb_type *);
void ovsdb_datum_destroy(struct ovsdb_datum *, const struct ovsdb_type *);
void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *);
+struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *,
+ const struct ovsdb_type *);
struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *,
const struct ovsdb_type *,
const struct ovsdb_datum *,
const struct ovsdb_type *);
+void ovsdb_datum_union(struct ovsdb_datum *,
+ const struct ovsdb_datum *,
+ const struct ovsdb_type *);
+void ovsdb_datum_subtract(struct ovsdb_datum *a,
+ const struct ovsdb_type *a_type,
+ const struct ovsdb_datum *b,
+ const struct ovsdb_type *b_type);
+
static inline bool
ovsdb_datum_conforms_to_type(const struct ovsdb_datum *datum,
const struct ovsdb_type *type)
One of "<", "<=", "==", "!=", ">=", ">", "includes", "excludes".
+<mutation>
+
+ A 3-element JSON array of the form [<column>, <mutator>, <value>]
+ that represents a change to a column value.
+
+ Except as otherwise specified below, <value> must have the same
+ type as <column>.
+
+ The meaning depends on the type of <column>:
+
+ integer
+ real
+
+ <mutator> must be "+=", "-=", "*=", "/=" or (integer only)
+ "%=". The value of <column> is changed to the sum,
+ difference, product, quotient, or remainder, respectively,
+ of <column> and <value>.
+
+ boolean
+ string
+ uuid
+
+ No valid <mutator>s are currently defined for these types.
+
+ set
+
+ Any <mutator> valid for the set's element type may be
+ applied to the set, in which case the mutation is applied
+ to each member of the set individually. <value> must be a
+ scalar value of the same type as the set's element type.
+
+ If <mutator> is "insert", then each of the values in the
+ set in <value> is added to <column> if it is not already
+ present. The required type of <value> is slightly
+ relaxed, in that it may have fewer than the minimum number
+ of elements specified by the column's type.
+
+ If <mutator> is "delete", then each of the values in the
+ set in <value> is removed from <column> if it is present
+ there. The required type is slightly relaxed in that
+ <value> may have more or less than the maximum number of
+ elements specified by the column's type.
+
+ map
+
+ <mutator> must be "insert" or "delete".
+
+ If <mutator> is "insert", then each of the key-value pairs
+ in the map in <value> is added to <column> if its key is
+ not already present. The required type of <value> is
+ slightly relaxed, in that it may have fewer than the
+ minimum number of elements specified by the column's type.
+
+ If <mutator> is "delete", then <value> may have the same
+ type as <column> (a map type) or it may be a set whose
+ element type is the same as <column>'s key type:
+
+ - If <value> is a map, the mutation deletes each
+ key-value pair in <column> whose key and value equal
+ one of the key-value pairs in <value>.
+
+ - If <value> is a set, the mutation deletes each
+ key-value pair in <column> whose key equals one of
+ the values in <value>.
+
+ For "delete", <value> may have any number of elements,
+ regardless of restrictions on the number of elements in
+ <column>.
+
+<mutator>
+
+ One of "+=", "-=", "*=", "/=", "%=", "insert", "delete".
+
Operations
----------
The "count" member of the result specifies the number of rows
that matched.
+mutate
+......
+
+Request object members:
+
+ "op": "mutate" required
+ "table": <table> required
+ "where": [<condition>*] required
+ "mutations": [<mutation>*] required
+
+Result object members:
+
+ "count": <integer>
+
+Semantics:
+
+ Mutates rows in a table.
+
+ Searches "table" for rows that match all the conditions specified
+ in "where". For each matching row, mutates its columns as
+ specified by each <mutation> in "mutations", in the order
+ specified.
+
+ The "_uuid" and "_version" columns of a table may not be directly
+ modified with this operation. Columns designated read-only in the
+ schema also may not be updated.
+
+ The "count" member of the result specifies the number of rows
+ that matched.
+
+Errors:
+
+ "error": "domain error"
+
+ The result of the mutation is not mathematically defined,
+ e.g. division by zero.
+
+ "error": "range error"
+
+ The result of the mutation is not representable within the
+ database's format, e.g. an integer result outside the range
+ INT64_MIN...INT64_MAX or a real result outside the range
+ -DBL_MAX...DBL_MAX.
+
+ "error": "constraint violation"
+
+ The mutation caused the column's value to violate a
+ constraint, e.g. it caused a column to have more or fewer
+ values than are allowed or an arithmetic operation caused a
+ set or map to have duplicate elements.
+
delete
......
ovsdb/jsonrpc-server.h \
ovsdb/log.c \
ovsdb/log.h \
+ ovsdb/mutation.c \
+ ovsdb/mutation.h \
ovsdb/ovsdb-server.c \
ovsdb/ovsdb.c \
ovsdb/ovsdb.h \
return NULL;
}
-
static WARN_UNUSED_RESULT struct ovsdb_error *
ovsdb_clause_from_json(const struct ovsdb_table_schema *ts,
const struct json *json,
#include "condition.h"
#include "file.h"
#include "json.h"
+#include "mutation.h"
#include "ovsdb-data.h"
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
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;
{ "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 },
return error;
}
+struct mutate_row_cbdata {
+ size_t n_matches;
+ struct ovsdb_txn *txn;
+ const struct ovsdb_mutation_set *mutations;
+};
+
+static bool
+mutate_row_cb(const struct ovsdb_row *row, void *mr_)
+{
+ struct mutate_row_cbdata *mr = mr_;
+
+ mr->n_matches++;
+ ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row),
+ mr->mutations);
+
+ return true;
+}
+
+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;
+ 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;
--- /dev/null
+/* Copyright (c) 2009 Nicira Networks
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "mutation.h"
+
+#include <float.h>
+#include <limits.h>
+
+#include "column.h"
+#include "ovsdb-error.h"
+#include "json.h"
+#include "row.h"
+#include "table.h"
+
+enum mutate_error {
+ ME_OK,
+ ME_DOM,
+ ME_RANGE,
+ ME_COUNT,
+ ME_DUP
+};
+
+struct ovsdb_error *
+ovsdb_mutator_from_string(const char *name, enum ovsdb_mutator *mutator)
+{
+#define OVSDB_MUTATOR(ENUM, NAME) \
+ if (!strcmp(name, NAME)) { \
+ *mutator = ENUM; \
+ return NULL; \
+ }
+ OVSDB_MUTATORS;
+#undef OVSDB_MUTATOR
+
+ return ovsdb_syntax_error(NULL, "unknown mutator",
+ "No mutator named %s.", name);
+}
+
+const char *
+ovsdb_mutator_to_string(enum ovsdb_mutator mutator)
+{
+ switch (mutator) {
+#define OVSDB_MUTATOR(ENUM, NAME) case ENUM: return NAME;
+ OVSDB_MUTATORS;
+#undef OVSDB_MUTATOR
+ }
+
+ return NULL;
+}
+
+static WARN_UNUSED_RESULT struct ovsdb_error *
+type_mismatch(const struct ovsdb_mutation *m, const struct json *json)
+{
+ struct ovsdb_error *error;
+ char *s;
+
+ s = ovsdb_type_to_english(&m->column->type);
+ error = ovsdb_syntax_error(
+ json, NULL, "Type mismatch: \"%s\" operator may not be "
+ "applied to column %s of type %s.",
+ ovsdb_mutator_to_string(m->mutator), m->column->name, s);
+ free(s);
+
+ return error;
+}
+
+static WARN_UNUSED_RESULT struct ovsdb_error *
+ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab,
+ struct ovsdb_mutation *m)
+{
+ const struct json_array *array;
+ struct ovsdb_error *error;
+ const char *mutator_name;
+ const char *column_name;
+
+ if (json->type != JSON_ARRAY
+ || json->u.array.n != 3
+ || json->u.array.elems[0]->type != JSON_STRING
+ || json->u.array.elems[1]->type != JSON_STRING) {
+ return ovsdb_syntax_error(json, NULL, "Parse error in mutation.");
+ }
+ array = json_array(json);
+
+ column_name = json_string(array->elems[0]);
+ m->column = ovsdb_table_schema_get_column(ts, column_name);
+ if (!m->column) {
+ return ovsdb_syntax_error(json, "unknown column",
+ "No column %s in table %s.",
+ column_name, ts->name);
+ }
+ m->type = m->column->type;
+
+ mutator_name = json_string(array->elems[1]);
+ error = ovsdb_mutator_from_string(mutator_name, &m->mutator);
+ if (error) {
+ return error;
+ }
+
+ /* Type-check and relax restrictions on 'type' if appropriate. */
+ switch (m->mutator) {
+ case OVSDB_M_ADD:
+ case OVSDB_M_SUB:
+ case OVSDB_M_MUL:
+ case OVSDB_M_DIV:
+ case OVSDB_M_MOD:
+ if ((!ovsdb_type_is_scalar(&m->type) && !ovsdb_type_is_set(&m->type))
+ || (m->type.key_type != OVSDB_TYPE_INTEGER
+ && m->type.key_type != OVSDB_TYPE_REAL)
+ || (m->mutator == OVSDB_M_MOD
+ && m->type.key_type == OVSDB_TYPE_REAL)) {
+ return type_mismatch(m, json);
+ }
+ m->type.n_min = m->type.n_max = 1;
+ return ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
+ symtab);
+
+ case OVSDB_M_INSERT:
+ case OVSDB_M_DELETE:
+ if (!ovsdb_type_is_set(&m->type) && !ovsdb_type_is_map(&m->type)) {
+ return type_mismatch(m, json);
+ }
+ m->type.n_min = 0;
+ if (m->mutator == OVSDB_M_DELETE) {
+ m->type.n_max = UINT_MAX;
+ }
+ error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
+ symtab);
+ if (error && ovsdb_type_is_map(&m->type)
+ && m->mutator == OVSDB_M_DELETE) {
+ ovsdb_error_destroy(error);
+ m->type.value_type = OVSDB_TYPE_VOID;
+ error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
+ symtab);
+ }
+ return error;
+ }
+
+ NOT_REACHED();
+}
+
+static void
+ovsdb_mutation_free(struct ovsdb_mutation *m)
+{
+ ovsdb_datum_destroy(&m->arg, &m->type);
+}
+
+struct ovsdb_error *
+ovsdb_mutation_set_from_json(const struct ovsdb_table_schema *ts,
+ const struct json *json,
+ const struct ovsdb_symbol_table *symtab,
+ struct ovsdb_mutation_set *set)
+{
+ const struct json_array *array = json_array(json);
+ size_t i;
+
+ set->mutations = xmalloc(array->n * sizeof *set->mutations);
+ set->n_mutations = 0;
+ for (i = 0; i < array->n; i++) {
+ struct ovsdb_error *error;
+ error = ovsdb_mutation_from_json(ts, array->elems[i], symtab,
+ &set->mutations[i]);
+ if (error) {
+ ovsdb_mutation_set_destroy(set);
+ set->mutations = NULL;
+ set->n_mutations = 0;
+ return error;
+ }
+ set->n_mutations++;
+ }
+
+ return NULL;
+}
+
+static struct json *
+ovsdb_mutation_to_json(const struct ovsdb_mutation *m)
+{
+ return json_array_create_3(
+ json_string_create(m->column->name),
+ json_string_create(ovsdb_mutator_to_string(m->mutator)),
+ ovsdb_datum_to_json(&m->arg, &m->type));
+}
+
+struct json *
+ovsdb_mutation_set_to_json(const struct ovsdb_mutation_set *set)
+{
+ struct json **mutations;
+ size_t i;
+
+ mutations = xmalloc(set->n_mutations * sizeof *mutations);
+ for (i = 0; i < set->n_mutations; i++) {
+ mutations[i] = ovsdb_mutation_to_json(&set->mutations[i]);
+ }
+ return json_array_create(mutations, set->n_mutations);
+}
+
+void
+ovsdb_mutation_set_destroy(struct ovsdb_mutation_set *set)
+{
+ size_t i;
+
+ for (i = 0; i < set->n_mutations; i++) {
+ ovsdb_mutation_free(&set->mutations[i]);
+ }
+ free(set->mutations);
+}
+
+static int
+add_int(int64_t *x, int64_t y)
+{
+ /* Check for overflow. See _Hacker's Delight_ pp. 27. */
+ int64_t z = ~(*x ^ y) & INT64_MIN;
+ if ((~(*x ^ y) & ~(((*x ^ z) + y) ^ y)) >> 63) {
+ return ME_RANGE;
+ } else {
+ *x += y;
+ return 0;
+ }
+}
+
+static int
+sub_int(int64_t *x, int64_t y)
+{
+ /* Check for overflow. See _Hacker's Delight_ pp. 27. */
+ int64_t z = (*x ^ y) & INT64_MIN;
+ if (((*x ^ y) & (((*x ^ z) - y) ^ y)) >> 63) {
+ return ME_RANGE;
+ } else {
+ *x -= y;
+ return 0;
+ }
+}
+
+static int
+mul_int(int64_t *x, int64_t y)
+{
+ /* Check for overflow. See _Hacker's Delight_ pp. 30. */
+ if (*x > 0
+ ? (y > 0
+ ? *x >= INT64_MAX / y
+ : y < INT64_MIN / *x)
+ : (y > 0
+ ? *x < INT64_MIN / y
+ : *x != 0 && y < INT64_MAX / y)) {
+ return ME_RANGE;
+ } else {
+ *x *= y;
+ return 0;
+ }
+}
+
+static int
+check_int_div(int64_t x, int64_t y)
+{
+ /* Check for overflow. See _Hacker's Delight_ pp. 32. */
+ if (!y) {
+ return ME_DOM;
+ } else if (x == INT64_MIN && y == -1) {
+ return ME_RANGE;
+ } else {
+ return 0;
+ }
+}
+
+static int
+div_int(int64_t *x, int64_t y)
+{
+ int error = check_int_div(*x, y);
+ if (!error) {
+ *x /= y;
+ }
+ return error;
+}
+
+static int
+mod_int(int64_t *x, int64_t y)
+{
+ int error = check_int_div(*x, y);
+ if (!error) {
+ *x %= y;
+ }
+ return error;
+}
+
+static int
+check_real_range(double x)
+{
+ return x >= -DBL_MAX && x <= DBL_MAX ? 0 : ME_RANGE;
+}
+
+static int
+add_double(double *x, double y)
+{
+ *x += y;
+ return 0;
+}
+
+static int
+sub_double(double *x, double y)
+{
+ *x -= y;
+ return 0;
+}
+
+static int
+mul_double(double *x, double y)
+{
+ *x *= y;
+ return 0;
+}
+
+static int
+div_double(double *x, double y)
+{
+ if (y == 0) {
+ return ME_DOM;
+ } else {
+ *x /= y;
+ return 0;
+ }
+}
+
+static int
+mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst,
+ const union ovsdb_atom *arg,
+ int (*mutate_integer)(int64_t *x, int64_t y),
+ int (*mutate_real)(double *x, double y))
+{
+ struct ovsdb_error *error;
+ unsigned int i;
+
+ if (dst_type->key_type == OVSDB_TYPE_INTEGER) {
+ int64_t y = arg->integer;
+ for (i = 0; i < dst->n; i++) {
+ int error = mutate_integer(&dst->keys[i].integer, y);
+ if (error) {
+ return error;
+ }
+ }
+ } else if (dst_type->key_type == OVSDB_TYPE_REAL) {
+ double y = arg->real;
+ for (i = 0; i < dst->n; i++) {
+ double *x = &dst->keys[i].real;
+ int error = mutate_real(x, y);
+ if (!error) {
+ error = check_real_range(*x);
+ }
+ if (error) {
+ return error;
+ }
+ }
+ } else {
+ NOT_REACHED();
+ }
+
+ error = ovsdb_datum_sort(dst, dst_type);
+ if (error) {
+ ovsdb_error_destroy(error);
+ return ME_DUP;
+ }
+ return 0;
+}
+
+struct ovsdb_error *
+ovsdb_mutation_set_execute(struct ovsdb_row *row,
+ const struct ovsdb_mutation_set *set)
+{
+ size_t i;
+
+ for (i = 0; i < set->n_mutations; i++) {
+ const struct ovsdb_mutation *m = &set->mutations[i];
+ struct ovsdb_datum *dst = &row->fields[m->column->index];
+ const struct ovsdb_type *dst_type = &m->column->type;
+ const struct ovsdb_datum *arg = &set->mutations[i].arg;
+ const struct ovsdb_type *arg_type = &m->type;
+ int error;
+
+ switch (m->mutator) {
+ case OVSDB_M_ADD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0],
+ add_int, add_double);
+ break;
+
+ case OVSDB_M_SUB:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0],
+ sub_int, sub_double);
+ break;
+
+ case OVSDB_M_MUL:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0],
+ mul_int, mul_double);
+ break;
+
+ case OVSDB_M_DIV:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0],
+ div_int, div_double);
+ break;
+
+ case OVSDB_M_MOD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0],
+ mod_int, NULL);
+ break;
+
+ case OVSDB_M_INSERT:
+ ovsdb_datum_union(dst, arg, dst_type);
+ error = ovsdb_datum_conforms_to_type(dst, dst_type) ? 0 : ME_COUNT;
+ break;
+
+ case OVSDB_M_DELETE:
+ ovsdb_datum_subtract(dst, dst_type, arg, arg_type);
+ error = ovsdb_datum_conforms_to_type(dst, dst_type) ? 0 : ME_COUNT;
+ break;
+ }
+
+ switch (error) {
+ case 0:
+ break;
+
+ case ME_DOM:
+ return ovsdb_error("domain error", "Division by zero.");
+
+ case ME_RANGE:
+ return ovsdb_error("range error",
+ "Result of \"%s\" operation is out of range.",
+ ovsdb_mutator_to_string(m->mutator));
+
+ case ME_DUP:
+ return ovsdb_error("constraint violation",
+ "Result of \"%s\" operation contains "
+ "duplicates.",
+ ovsdb_mutator_to_string(m->mutator));
+
+ case ME_COUNT: {
+ char *s = ovsdb_type_to_english(dst_type);
+ struct ovsdb_error *e = ovsdb_error(
+ "constaint violation",
+ "Attempted to store %u elements in %s.", dst->n, s);
+ free(s);
+ return e;
+ }
+
+ default:
+ return OVSDB_BUG("unexpected errno");
+ }
+ }
+
+ return NULL;
+}
--- /dev/null
+/* Copyright (c) 2009 Nicira Networks
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVSDB_MUTATION_H
+#define OVSDB_MUTATION_H 1
+
+#include <stddef.h>
+#include "compiler.h"
+#include "ovsdb-data.h"
+
+struct json;
+struct ovsdb_table_schema;
+struct ovsdb_row;
+
+/* These list is ordered in ascending order of the fraction of tables row that
+ * they are (heuristically) expected to leave in query results. */
+#define OVSDB_MUTATORS \
+ OVSDB_MUTATOR(OVSDB_M_ADD, "+=") \
+ OVSDB_MUTATOR(OVSDB_M_SUB, "-=") \
+ OVSDB_MUTATOR(OVSDB_M_MUL, "*=") \
+ OVSDB_MUTATOR(OVSDB_M_DIV, "/=") \
+ OVSDB_MUTATOR(OVSDB_M_MOD, "%=") \
+ OVSDB_MUTATOR(OVSDB_M_INSERT, "insert") \
+ OVSDB_MUTATOR(OVSDB_M_DELETE, "delete")
+
+enum ovsdb_mutator {
+#define OVSDB_MUTATOR(ENUM, NAME) ENUM,
+ OVSDB_MUTATORS
+#undef OVSDB_MUTATOR
+};
+
+struct ovsdb_error *ovsdb_mutator_from_string(const char *,
+ enum ovsdb_mutator *)
+ WARN_UNUSED_RESULT;
+const char *ovsdb_mutator_to_string(enum ovsdb_mutator);
+
+struct ovsdb_mutation {
+ enum ovsdb_mutator mutator;
+ const struct ovsdb_column *column;
+ struct ovsdb_datum arg;
+ struct ovsdb_type type;
+};
+
+struct ovsdb_mutation_set {
+ struct ovsdb_mutation *mutations;
+ size_t n_mutations;
+};
+
+#define OVSDB_MUTATION_SET_INITIALIZER { NULL, 0 }
+
+struct ovsdb_error *ovsdb_mutation_set_from_json(
+ const struct ovsdb_table_schema *,
+ const struct json *, const struct ovsdb_symbol_table *,
+ struct ovsdb_mutation_set *) WARN_UNUSED_RESULT;
+struct json *ovsdb_mutation_set_to_json(const struct ovsdb_mutation_set *);
+void ovsdb_mutation_set_destroy(struct ovsdb_mutation_set *);
+struct ovsdb_error *ovsdb_mutation_set_execute(
+ struct ovsdb_row *, const struct ovsdb_mutation_set *);
+
+#endif /* ovsdb/mutation.h */
tests/ovsdb-table.at \
tests/ovsdb-row.at \
tests/ovsdb-condition.at \
+ tests/ovsdb-mutation.at \
tests/ovsdb-query.at \
tests/ovsdb-transaction.at \
tests/ovsdb-execution.at \
[{"rows":[{"_uuid":["uuid","<0>"],"_version":["uuid","<2>"],"name":"nought","number":0},{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}]
]])
+OVSDB_CHECK_EXECUTION([insert rows, mutate rows],
+ [ORDINAL_SCHEMA],
+ [[[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"},
+ "uuid-name": "first"}]]],
+ [[[{"op": "mutate",
+ "table": "ordinals",
+ "where": [["name", "==", "zero"]],
+ "mutations": [["number", "+=", 2]]}]]],
+ [[[{"op": "select",
+ "table": "ordinals",
+ "where": [],
+ "sort": ["number"]}]]]],
+ [[[{"uuid":["uuid","<0>"]}]
+[{"uuid":["uuid","<1>"]}]
+[{"count":1}]
+[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<2>"],"name":"one","number":1},{"_uuid":["uuid","<0>"],"_version":["uuid","<3>"],"name":"zero","number":2}]}]
+]])
+
OVSDB_CHECK_EXECUTION([insert rows, delete by named-uuid],
[ORDINAL_SCHEMA],
[[[[{"op": "insert",
--- /dev/null
+AT_BANNER([OVSDB -- mutations])
+
+OVSDB_CHECK_POSITIVE([null mutation],
+ [[parse-mutations \
+ '{"columns": {"name": {"type": "string"}}}' \
+ '[]']],
+ [[[]]])
+
+OVSDB_CHECK_POSITIVE([mutations on scalars],
+ [[parse-mutations \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[["i", "+=", 0]]' \
+ '[["i", "-=", 1]]' \
+ '[["i", "*=", 2]]' \
+ '[["i", "/=", 3]]' \
+ '[["i", "%=", 4]]' \
+ '[["r", "+=", 0.5]]' \
+ '[["r", "-=", 1.5]]' \
+ '[["r", "*=", 2.5]]' \
+ '[["r", "/=", 3.5]]']],
+ [[[["i","+=",0]]
+[["i","-=",1]]
+[["i","*=",2]]
+[["i","/=",3]]
+[["i","%=",4]]
+[["r","+=",0.5]]
+[["r","-=",1.5]]
+[["r","*=",2.5]]
+[["r","/=",3.5]]]],
+ [mutation])
+
+AT_SETUP([disallowed mutations on scalars])
+AT_KEYWORDS([ovsdb negative mutation])
+OVS_CHECK_LCOV([[test-ovsdb parse-mutations \
+ '{"columns":
+ {"i": {"type": "integer"},
+ "r": {"type": "real"},
+ "b": {"type": "boolean"},
+ "s": {"type": "string"},
+ "u": {"type": "uuid"}}}' \
+ '[["i", "xxx", 1]]' \
+ '[["i", "insert", 1]]' \
+ '[["i", "delete", 2]]' \
+ '[["r", "%=", 0.5]]' \
+ '[["r", "insert", 1.5]]' \
+ '[["r", "delete", 2.5]]' \
+ '[["b", "+=", true]]' \
+ '[["b", "-=", false]]' \
+ '[["b", "*=", true]]' \
+ '[["b", "/=", false]]' \
+ '[["b", "%=", true]]' \
+ '[["b", "insert", false]]' \
+ '[["b", "delete", true]]' \
+ '[["s", "+=", "a"]]' \
+ '[["s", "-=", "b"]]' \
+ '[["s", "*=", "c"]]' \
+ '[["s", "/=", "d"]]' \
+ '[["s", "%=", "e"]]' \
+ '[["s", "insert", "f"]]' \
+ '[["s", "delete", "g"]]' \
+ '[["u", "+=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "-=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "*=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "/=", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "insert", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]' \
+ '[["u", "delete", ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]']],
+ [1], [],
+ [[test-ovsdb: unknown mutator: No mutator named xxx.
+test-ovsdb: syntax "["i","insert",1]": syntax error: Type mismatch: "insert" operator may not be applied to column i of type integer.
+test-ovsdb: syntax "["i","delete",2]": syntax error: Type mismatch: "delete" operator may not be applied to column i of type integer.
+test-ovsdb: syntax "["r","%=",0.5]": syntax error: Type mismatch: "%=" operator may not be applied to column r of type real.
+test-ovsdb: syntax "["r","insert",1.5]": syntax error: Type mismatch: "insert" operator may not be applied to column r of type real.
+test-ovsdb: syntax "["r","delete",2.5]": syntax error: Type mismatch: "delete" operator may not be applied to column r of type real.
+test-ovsdb: syntax "["b","+=",true]": syntax error: Type mismatch: "+=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","-=",false]": syntax error: Type mismatch: "-=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","*=",true]": syntax error: Type mismatch: "*=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","/=",false]": syntax error: Type mismatch: "/=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","%=",true]": syntax error: Type mismatch: "%=" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","insert",false]": syntax error: Type mismatch: "insert" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["b","delete",true]": syntax error: Type mismatch: "delete" operator may not be applied to column b of type boolean.
+test-ovsdb: syntax "["s","+=","a"]": syntax error: Type mismatch: "+=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","-=","b"]": syntax error: Type mismatch: "-=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","*=","c"]": syntax error: Type mismatch: "*=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","/=","d"]": syntax error: Type mismatch: "/=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","%=","e"]": syntax error: Type mismatch: "%=" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","insert","f"]": syntax error: Type mismatch: "insert" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["s","delete","g"]": syntax error: Type mismatch: "delete" operator may not be applied to column s of type string.
+test-ovsdb: syntax "["u","+=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "+=" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","-=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "-=" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","*=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "*=" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","/=",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "/=" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","insert",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "insert" operator may not be applied to column u of type uuid.
+test-ovsdb: syntax "["u","delete",["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"]]": syntax error: Type mismatch: "delete" operator may not be applied to column u of type uuid.
+]])
+AT_CLEANUP
+
+OVSDB_CHECK_POSITIVE([mutations on sets],
+ [[parse-mutations \
+ '{"columns":
+ {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}},
+ "r": {"type": {"key": "real", "min": 0, "max": "unlimited"}},
+ "b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}},
+ "s": {"type": {"key": "string", "min": 0, "max": "unlimited"}},
+ "u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
+ '[["i", "+=", 1]]' \
+ '[["i", "-=", 2]]' \
+ '[["i", "*=", 3]]' \
+ '[["i", "/=", 4]]' \
+ '[["i", "%=", 5]]' \
+ '[["i", "insert", ["set", [1, 2]]]]' \
+ '[["i", "delete", ["set", [1, 2, 3]]]]' \
+ '[["r", "+=", 1]]' \
+ '[["r", "-=", 2]]' \
+ '[["r", "*=", 3]]' \
+ '[["r", "/=", 4]]' \
+ '[["r", "insert", ["set", [1, 2]]]]' \
+ '[["r", "delete", ["set", [1, 2, 3]]]]' \
+ '[["b", "insert", ["set", [true]]]]' \
+ '[["b", "delete", ["set", [false]]]]' \
+ '[["s", "insert", ["set", ["a"]]]]' \
+ '[["s", "delete", ["set", ["a", "b"]]]]' \
+ '[["u", "insert",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]' \
+ '[["u", "delete",
+ ["set", [["uuid", "b10d28f7-af18-4a67-9e78-2a6394516c59"],
+ ["uuid", "9179ca6d-6d65-400a-b455-3ad92783a099"]]]]]' \
+]],
+ [[[["i","+=",1]]
+[["i","-=",2]]
+[["i","*=",3]]
+[["i","/=",4]]
+[["i","%=",5]]
+[["i","insert",["set",[1,2]]]]
+[["i","delete",["set",[1,2,3]]]]
+[["r","+=",1]]
+[["r","-=",2]]
+[["r","*=",3]]
+[["r","/=",4]]
+[["r","insert",["set",[1,2]]]]
+[["r","delete",["set",[1,2,3]]]]
+[["b","insert",["set",[true]]]]
+[["b","delete",["set",[false]]]]
+[["s","insert",["set",["a"]]]]
+[["s","delete",["set",["a","b"]]]]
+[["u","insert",["set",[["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]
+[["u","delete",["set",[["uuid","9179ca6d-6d65-400a-b455-3ad92783a099"],["uuid","b10d28f7-af18-4a67-9e78-2a6394516c59"]]]]]]],
+ [mutation])
+
+OVSDB_CHECK_POSITIVE([executing null mutation],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[[]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": 2}']]],
+ [mutation 0:
+row 0: no change
+row 1: no change
+row 2: no change
+])
+
+OVSDB_CHECK_POSITIVE([executing mutations on integers],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[[["i", "+=", 1]],
+ [["i", "-=", 2]],
+ [["i", "*=", 3]],
+ [["i", "/=", 4]],
+ [["i", "%=", 2]]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": 2}']]],
+ [mutation 0:
+row 0: {"i":1}
+row 1: {"i":2}
+row 2: {"i":3}
+
+mutation 1:
+row 0: {"i":-2}
+row 1: {"i":-1}
+row 2: {"i":0}
+
+mutation 2:
+row 0: no change
+row 1: {"i":3}
+row 2: {"i":6}
+
+mutation 3:
+row 0: no change
+row 1: {"i":0}
+row 2: {"i":0}
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: {"i":0}
+], [mutation])
+
+OVSDB_CHECK_POSITIVE([integer overflow detection],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": "integer"}}}' \
+ '[[["i", "+=", 9223372036854775807]],
+ [["i", "+=", -9223372036854775808]],
+ [["i", "-=", -9223372036854775808]],
+ [["i", "-=", 9223372036854775807]],
+ [["i", "*=", 3037000500]],
+ [["i", "/=", -1]],
+ [["i", "/=", 0]]]' \
+ '[{"i": 0},
+ {"i": 1},
+ {"i": -1},
+ {"i": 9223372036854775807},
+ {"i": -9223372036854775808},
+ {"i": 3037000500},
+ {"i": -3037000500}']]],
+ [mutation 0:
+row 0: {"i":9223372036854775807}
+row 1: range error: Result of "+=" operation is out of range.
+row 2: {"i":9223372036854775806}
+row 3: range error: Result of "+=" operation is out of range.
+row 4: {"i":-1}
+row 5: range error: Result of "+=" operation is out of range.
+row 6: {"i":9223372033817775307}
+
+mutation 1:
+row 0: {"i":-9223372036854775808}
+row 1: {"i":-9223372036854775807}
+row 2: range error: Result of "+=" operation is out of range.
+row 3: {"i":-1}
+row 4: range error: Result of "+=" operation is out of range.
+row 5: {"i":-9223372033817775308}
+row 6: range error: Result of "+=" operation is out of range.
+
+mutation 2:
+row 0: range error: Result of "-=" operation is out of range.
+row 1: range error: Result of "-=" operation is out of range.
+row 2: {"i":9223372036854775807}
+row 3: range error: Result of "-=" operation is out of range.
+row 4: {"i":0}
+row 5: range error: Result of "-=" operation is out of range.
+row 6: {"i":9223372033817775308}
+
+mutation 3:
+row 0: {"i":-9223372036854775807}
+row 1: {"i":-9223372036854775806}
+row 2: {"i":-9223372036854775808}
+row 3: {"i":0}
+row 4: range error: Result of "-=" operation is out of range.
+row 5: {"i":-9223372033817775307}
+row 6: range error: Result of "-=" operation is out of range.
+
+mutation 4:
+row 0: no change
+row 1: {"i":3037000500}
+row 2: {"i":-3037000500}
+row 3: range error: Result of "*=" operation is out of range.
+row 4: range error: Result of "*=" operation is out of range.
+row 5: range error: Result of "*=" operation is out of range.
+row 6: range error: Result of "*=" operation is out of range.
+
+mutation 5:
+row 0: no change
+row 1: {"i":-1}
+row 2: {"i":1}
+row 3: {"i":-9223372036854775807}
+row 4: range error: Result of "/=" operation is out of range.
+row 5: {"i":-3037000500}
+row 6: {"i":3037000500}
+
+mutation 6:
+row 0: domain error: Division by zero.
+row 1: domain error: Division by zero.
+row 2: domain error: Division by zero.
+row 3: domain error: Division by zero.
+row 4: domain error: Division by zero.
+row 5: domain error: Division by zero.
+row 6: domain error: Division by zero.
+], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on reals],
+ [[execute-mutations \
+ '{"columns": {"r": {"type": "real"}}}' \
+ '[[["r", "+=", 0.5]],
+ [["r", "-=", 1.5]],
+ [["r", "*=", 2.5]],
+ [["r", "/=", 4]]]' \
+ '[{"r": 0},
+ {"r": -2.5},
+ {"r": 1.25}']]],
+ [mutation 0:
+row 0: {"r":0.5}
+row 1: {"r":-2}
+row 2: {"r":1.75}
+
+mutation 1:
+row 0: {"r":-1.5}
+row 1: {"r":-4}
+row 2: {"r":-0.25}
+
+mutation 2:
+row 0: no change
+row 1: {"r":-6.25}
+row 2: {"r":3.125}
+
+mutation 3:
+row 0: no change
+row 1: {"r":-0.625}
+row 2: {"r":0.3125}
+], [mutation])
+
+OVSDB_CHECK_POSITIVE([real overflow detection],
+ [[execute-mutations \
+ '{"columns": {"r": {"type": "real"}}}' \
+ '[[["r", "+=", 1.7976931348623157e+308]],
+ [["r", "-=", 1.7976931348623157e+308]],
+ [["r", "*=", 2]],
+ [["r", "/=", 4]],
+ [["r", "/=", 0.5]],
+ [["r", "/=", 0]]]' \
+ '[{"r": 0},
+ {"r": 1.7976931348623157e+308},
+ {"r": -1.7976931348623157e+308}']]],
+ [mutation 0:
+row 0: {"r":1.79769313486232e+308}
+row 1: range error: Result of "+=" operation is out of range.
+row 2: {"r":0}
+
+mutation 1:
+row 0: {"r":-1.79769313486232e+308}
+row 1: {"r":0}
+row 2: range error: Result of "-=" operation is out of range.
+
+mutation 2:
+row 0: no change
+row 1: range error: Result of "*=" operation is out of range.
+row 2: range error: Result of "*=" operation is out of range.
+
+mutation 3:
+row 0: no change
+row 1: {"r":4.49423283715579e+307}
+row 2: {"r":-4.49423283715579e+307}
+
+mutation 4:
+row 0: no change
+row 1: range error: Result of "/=" operation is out of range.
+row 2: range error: Result of "/=" operation is out of range.
+
+mutation 5:
+row 0: domain error: Division by zero.
+row 1: domain error: Division by zero.
+row 2: domain error: Division by zero.
+], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on integer sets],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": {"key": "integer", "min": 0, "max": "unlimited"}}}}' \
+ '[[["i", "+=", 1]],
+ [["i", "-=", 2]],
+ [["i", "*=", 3]],
+ [["i", "/=", 4]],
+ [["i", "%=", 2]],
+ [["i", "insert", ["set", [1]]]],
+ [["i", "insert", ["set", [2, 3]]]],
+ [["i", "delete", ["set", [1]]]],
+ [["i", "delete", ["set", [2, 3]]]]]' \
+ '[{"i": ["set", []]},
+ {"i": ["set", [0]]},
+ {"i": ["set", [0, 1]]},
+ {"i": ["set", [0, 1, 2]]}']]],
+ [[mutation 0:
+row 0: no change
+row 1: {"i":["set",[1]]}
+row 2: {"i":["set",[1,2]]}
+row 3: {"i":["set",[1,2,3]]}
+
+mutation 1:
+row 0: no change
+row 1: {"i":["set",[-2]]}
+row 2: {"i":["set",[-2,-1]]}
+row 3: {"i":["set",[-2,-1,0]]}
+
+mutation 2:
+row 0: no change
+row 1: no change
+row 2: {"i":["set",[0,3]]}
+row 3: {"i":["set",[0,3,6]]}
+
+mutation 3:
+row 0: no change
+row 1: no change
+row 2: constraint violation: Result of "/=" operation contains duplicates.
+row 3: constraint violation: Result of "/=" operation contains duplicates.
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: no change
+row 3: constraint violation: Result of "%=" operation contains duplicates.
+
+mutation 5:
+row 0: {"i":["set",[1]]}
+row 1: {"i":["set",[0,1]]}
+row 2: no change
+row 3: no change
+
+mutation 6:
+row 0: {"i":["set",[2,3]]}
+row 1: {"i":["set",[0,2,3]]}
+row 2: {"i":["set",[0,1,2,3]]}
+row 3: {"i":["set",[0,1,2,3]]}
+
+mutation 7:
+row 0: no change
+row 1: no change
+row 2: {"i":["set",[0]]}
+row 3: {"i":["set",[0,2]]}
+
+mutation 8:
+row 0: no change
+row 1: no change
+row 2: no change
+row 3: {"i":["set",[0,1]]}
+]], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on real sets],
+ [[execute-mutations \
+ '{"columns": {"r": {"type": {"key": "real", "min": 0, "max": "unlimited"}}}}' \
+ '[[["r", "+=", 0.5]],
+ [["r", "-=", 1.5]],
+ [["r", "*=", 2.5]],
+ [["r", "/=", 4]],
+ [["r", "*=", 0]],
+ [["r", "insert", ["set", [1.5]]]],
+ [["r", "insert", ["set", [3]]]],
+ [["r", "delete", ["set", [1.5, 3.5]]]],
+ [["r", "delete", ["set", [0.5, 1.5, 2.5]]]]]' \
+ '[{"r": ["set", []]},
+ {"r": ["set", [0.5]]},
+ {"r": ["set", [0.5, 1.5]]},
+ {"r": ["set", [0.5, 1.5, 2.5]]}']]],
+ [[mutation 0:
+row 0: no change
+row 1: {"r":["set",[1]]}
+row 2: {"r":["set",[1,2]]}
+row 3: {"r":["set",[1,2,3]]}
+
+mutation 1:
+row 0: no change
+row 1: {"r":["set",[-1]]}
+row 2: {"r":["set",[-1,0]]}
+row 3: {"r":["set",[-1,0,1]]}
+
+mutation 2:
+row 0: no change
+row 1: {"r":["set",[1.25]]}
+row 2: {"r":["set",[1.25,3.75]]}
+row 3: {"r":["set",[1.25,3.75,6.25]]}
+
+mutation 3:
+row 0: no change
+row 1: {"r":["set",[0.125]]}
+row 2: {"r":["set",[0.125,0.375]]}
+row 3: {"r":["set",[0.125,0.375,0.625]]}
+
+mutation 4:
+row 0: no change
+row 1: {"r":["set",[0]]}
+row 2: constraint violation: Result of "*=" operation contains duplicates.
+row 3: constraint violation: Result of "*=" operation contains duplicates.
+
+mutation 5:
+row 0: {"r":["set",[1.5]]}
+row 1: {"r":["set",[0.5,1.5]]}
+row 2: no change
+row 3: no change
+
+mutation 6:
+row 0: {"r":["set",[3]]}
+row 1: {"r":["set",[0.5,3]]}
+row 2: {"r":["set",[0.5,1.5,3]]}
+row 3: {"r":["set",[0.5,1.5,2.5,3]]}
+
+mutation 7:
+row 0: no change
+row 1: no change
+row 2: {"r":["set",[0.5]]}
+row 3: {"r":["set",[0.5,2.5]]}
+
+mutation 8:
+row 0: no change
+row 1: {"r":["set",[]]}
+row 2: {"r":["set",[]]}
+row 3: {"r":["set",[]]}
+]], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on boolean sets],
+ [[execute-mutations \
+ '{"columns": {"b": {"type": {"key": "boolean", "min": 0, "max": "unlimited"}}}}' \
+ '[[["b", "insert", ["set", [false]]]],
+ [["b", "insert", ["set", [true]]]],
+ [["b", "insert", ["set", [false, true]]]],
+ [["b", "delete", ["set", [false]]]],
+ [["b", "delete", ["set", [true]]]],
+ [["b", "delete", ["set", [true, false]]]]]' \
+ '[{"b": ["set", []]},
+ {"b": ["set", [false]]},
+ {"b": ["set", [true]]},
+ {"b": ["set", [false, true]]}']]],
+ [[mutation 0:
+row 0: {"b":["set",[false]]}
+row 1: no change
+row 2: {"b":["set",[false,true]]}
+row 3: no change
+
+mutation 1:
+row 0: {"b":["set",[true]]}
+row 1: {"b":["set",[false,true]]}
+row 2: no change
+row 3: no change
+
+mutation 2:
+row 0: {"b":["set",[false,true]]}
+row 1: {"b":["set",[false,true]]}
+row 2: {"b":["set",[false,true]]}
+row 3: no change
+
+mutation 3:
+row 0: no change
+row 1: {"b":["set",[]]}
+row 2: no change
+row 3: {"b":["set",[true]]}
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: {"b":["set",[]]}
+row 3: {"b":["set",[false]]}
+
+mutation 5:
+row 0: no change
+row 1: {"b":["set",[]]}
+row 2: {"b":["set",[]]}
+row 3: {"b":["set",[]]}
+]], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on string sets],
+ [[execute-mutations \
+ '{"columns": {"s": {"type": {"key": "string", "min": 0, "max": "unlimited"}}}}' \
+ '[[["s", "insert", ["set", ["a"]]]],
+ [["s", "insert", ["set", ["b"]]]],
+ [["s", "insert", ["set", ["c", "d"]]]],
+ [["s", "delete", ["set", ["a"]]]],
+ [["s", "delete", ["set", ["b"]]]],
+ [["s", "delete", ["set", ["c", "d"]]]]]' \
+ '[{"s": ["set", []]},
+ {"s": ["set", ["a"]]},
+ {"s": ["set", ["a", "b"]]},
+ {"s": ["set", ["a", "b", "c", "d"]]}']]],
+ [[mutation 0:
+row 0: {"s":["set",["a"]]}
+row 1: no change
+row 2: no change
+row 3: no change
+
+mutation 1:
+row 0: {"s":["set",["b"]]}
+row 1: {"s":["set",["a","b"]]}
+row 2: no change
+row 3: no change
+
+mutation 2:
+row 0: {"s":["set",["c","d"]]}
+row 1: {"s":["set",["a","c","d"]]}
+row 2: {"s":["set",["a","b","c","d"]]}
+row 3: no change
+
+mutation 3:
+row 0: no change
+row 1: {"s":["set",[]]}
+row 2: {"s":["set",["b"]]}
+row 3: {"s":["set",["b","c","d"]]}
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: {"s":["set",["a"]]}
+row 3: {"s":["set",["a","c","d"]]}
+
+mutation 5:
+row 0: no change
+row 1: no change
+row 2: no change
+row 3: {"s":["set",["a","b"]]}
+]], [mutation])
+
+OVSDB_CHECK_POSITIVE([executing mutations on uuid sets],
+ [[execute-mutations \
+ '{"columns": {"u": {"type": {"key": "uuid", "min": 0, "max": "unlimited"}}}}' \
+ '[[["u", "insert", ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]]],
+ [["u", "insert", ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]]],
+ [["u", "insert", ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]]],
+ [["u", "delete", ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]]],
+ [["u", "delete", ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]]],
+ [["u", "delete", ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]]]]' \
+ '[{"u": ["set", []]},
+ {"u": ["set", [["uuid", "ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]},
+ {"u": ["set", [["uuid", "a60fe7ff-317b-4568-9106-892b37445313"]]]},
+ {"u": ["set", [["uuid", "2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]}']]],
+ [[mutation 0:
+row 0: {"u":["set",[["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}
+row 1: no change
+row 2: {"u":["set",[["uuid","a60fe7ff-317b-4568-9106-892b37445313"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}
+row 3: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}
+
+mutation 1:
+row 0: {"u":["set",[["uuid","a60fe7ff-317b-4568-9106-892b37445313"]]]}
+row 1: {"u":["set",[["uuid","a60fe7ff-317b-4568-9106-892b37445313"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}
+row 2: no change
+row 3: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","a60fe7ff-317b-4568-9106-892b37445313"]]]}
+
+mutation 2:
+row 0: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"]]]}
+row 1: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","ddd9e79d-7782-414c-8b22-1046c60b6ec2"]]]}
+row 2: {"u":["set",[["uuid","2607d30e-e652-4927-9fec-8bbf1b60c7e9"],["uuid","a60fe7ff-317b-4568-9106-892b37445313"]]]}
+row 3: no change
+
+mutation 3:
+row 0: no change
+row 1: {"u":["set",[]]}
+row 2: no change
+row 3: no change
+
+mutation 4:
+row 0: no change
+row 1: no change
+row 2: {"u":["set",[]]}
+row 3: no change
+
+mutation 5:
+row 0: no change
+row 1: no change
+row 2: no change
+row 3: {"u":["set",[]]}
+]], [mutation])
+
+
+OVSDB_CHECK_POSITIVE([executing mutations on integer maps],
+ [[execute-mutations \
+ '{"columns": {"i": {"type": {"key": "integer", "value": "integer", "min": 0, "max": "unlimited"}}}}' \
+ '[[["i", "insert", ["map", [[1, 2]]]]],
+ [["i", "insert", ["map", [[2, 4], [3, 5]]]]],
+ [["i", "delete", ["map", [[1, 2]]]]],
+ [["i", "delete", ["map", [[2, 3]]]]],
+ [["i", "delete", ["set", [1]]]],
+ [["i", "delete", ["set", [2, 3]]]]]' \
+ '[{"i": ["map", []]},
+ {"i": ["map", [[1, 2]]]},
+ {"i": ["map", [[1, 3], [2, 3]]]},
+ {"i": ["map", [[3, 5]]]}']]],
+ [[mutation 0:
+row 0: {"i":["map",[[1,2]]]}
+row 1: no change
+row 2: no change
+row 3: {"i":["map",[[1,2],[3,5]]]}
+
+mutation 1:
+row 0: {"i":["map",[[2,4],[3,5]]]}
+row 1: {"i":["map",[[1,2],[2,4],[3,5]]]}
+row 2: {"i":["map",[[1,3],[2,3],[3,5]]]}
+row 3: {"i":["map",[[2,4],[3,5]]]}
+
+mutation 2:
+row 0: no change
+row 1: {"i":["map",[]]}
+row 2: no change
+row 3: no change
+
+mutation 3:
+row 0: no change
+row 1: no change
+row 2: {"i":["map",[[1,3]]]}
+row 3: no change
+
+mutation 4:
+row 0: no change
+row 1: {"i":["map",[]]}
+row 2: {"i":["map",[[2,3]]]}
+row 3: no change
+
+mutation 5:
+row 0: no change
+row 1: no change
+row 2: {"i":["map",[[1,3]]]}
+row 3: {"i":["map",[]]}
+]], [mutation])
m4_include([tests/ovsdb-table.at])
m4_include([tests/ovsdb-row.at])
m4_include([tests/ovsdb-condition.at])
+m4_include([tests/ovsdb-mutation.at])
m4_include([tests/ovsdb-query.at])
m4_include([tests/ovsdb-transaction.at])
m4_include([tests/ovsdb-execution.at])
#include "ovsdb/condition.h"
#include "ovsdb/file.h"
#include "ovsdb/log.h"
+#include "ovsdb/mutation.h"
#include "ovsdb/ovsdb.h"
#include "ovsdb/query.h"
#include "ovsdb/row.h"
" parse each CONDITION on TABLE, and re-serialize\n"
" evaluate-conditions TABLE [CONDITION,...] [ROW,...]\n"
" test CONDITIONS on TABLE against each ROW, print results\n"
+ " parse-mutations TABLE MUTATION...\n"
+ " parse each MUTATION on TABLE, and re-serialize\n"
+ " execute-mutations TABLE [MUTATION,...] [ROW,...]\n"
+ " execute MUTATIONS on TABLE on each ROW, print results\n"
" query TABLE [ROW,...] [CONDITION,...]\n"
" add each ROW to TABLE, then query and print the rows that\n"
" satisfy each CONDITION.\n"
free(string);
}
+static void
+print_and_free_ovsdb_error(struct ovsdb_error *error)
+{
+ char *string = ovsdb_error_to_string(error);
+ ovsdb_error_destroy(error);
+ puts(string);
+ free(string);
+}
+
static void
check_ovsdb_error(struct ovsdb_error *error)
{
ovsdb_table_destroy(table); /* Also destroys 'ts'. */
}
+static void
+do_parse_mutations(int argc, char *argv[])
+{
+ struct ovsdb_table_schema *ts;
+ struct json *json;
+ int exit_code = 0;
+ int i;
+
+ json = unbox_json(parse_json(argv[1]));
+ check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
+ json_destroy(json);
+
+ for (i = 2; i < argc; i++) {
+ struct ovsdb_mutation_set set;
+ struct ovsdb_error *error;
+
+ json = parse_json(argv[i]);
+ error = ovsdb_mutation_set_from_json(ts, json, NULL, &set);
+ if (!error) {
+ print_and_free_json(ovsdb_mutation_set_to_json(&set));
+ } else {
+ char *s = ovsdb_error_to_string(error);
+ ovs_error(0, "%s", s);
+ free(s);
+ ovsdb_error_destroy(error);
+ exit_code = 1;
+ }
+ json_destroy(json);
+
+ ovsdb_mutation_set_destroy(&set);
+ }
+ ovsdb_table_schema_destroy(ts);
+
+ exit(exit_code);
+}
+
+static void
+do_execute_mutations(int argc UNUSED, char *argv[])
+{
+ struct ovsdb_table_schema *ts;
+ struct ovsdb_table *table;
+ struct ovsdb_mutation_set *sets;
+ size_t n_sets;
+ struct ovsdb_row **rows;
+ size_t n_rows;
+ struct json *json;
+ size_t i, j;
+
+ /* Parse table schema, create table. */
+ json = unbox_json(parse_json(argv[1]));
+ check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
+ json_destroy(json);
+
+ table = ovsdb_table_create(ts);
+
+ /* Parse mutations. */
+ json = parse_json(argv[2]);
+ if (json->type != JSON_ARRAY) {
+ ovs_fatal(0, "MUTATION argument is not JSON array");
+ }
+ n_sets = json->u.array.n;
+ sets = xmalloc(n_sets * sizeof *sets);
+ for (i = 0; i < n_sets; i++) {
+ check_ovsdb_error(ovsdb_mutation_set_from_json(ts,
+ json->u.array.elems[i],
+ NULL, &sets[i]));
+ }
+ json_destroy(json);
+
+ /* Parse rows. */
+ json = parse_json(argv[3]);
+ if (json->type != JSON_ARRAY) {
+ ovs_fatal(0, "ROW argument is not JSON array");
+ }
+ n_rows = json->u.array.n;
+ rows = xmalloc(n_rows * sizeof *rows);
+ for (i = 0; i < n_rows; i++) {
+ rows[i] = ovsdb_row_create(table);
+ check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i],
+ NULL, NULL));
+ }
+ json_destroy(json);
+
+ for (i = 0; i < n_sets; i++) {
+ printf("mutation %2d:\n", i);
+ for (j = 0; j < n_rows; j++) {
+ struct ovsdb_error *error;
+ struct ovsdb_row *row;
+
+ row = ovsdb_row_clone(rows[j]);
+ error = ovsdb_mutation_set_execute(row, &sets[i]);
+
+ printf("row %zu: ", j);
+ if (error) {
+ print_and_free_ovsdb_error(error);
+ } else {
+ struct ovsdb_column_set columns;
+ struct shash_node *node;
+
+ ovsdb_column_set_init(&columns);
+ SHASH_FOR_EACH (node, &ts->columns) {
+ struct ovsdb_column *c = node->data;
+ if (!ovsdb_datum_equals(&row->fields[c->index],
+ &rows[j]->fields[c->index],
+ &c->type)) {
+ ovsdb_column_set_add(&columns, c);
+ }
+ }
+ if (columns.n_columns) {
+ print_and_free_json(ovsdb_row_to_json(row, &columns));
+ } else {
+ printf("no change\n");
+ }
+ ovsdb_column_set_destroy(&columns);
+ }
+ ovsdb_row_destroy(row);
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i < n_sets; i++) {
+ ovsdb_mutation_set_destroy(&sets[i]);
+ }
+ for (i = 0; i < n_rows; i++) {
+ ovsdb_row_destroy(rows[i]);
+ }
+ ovsdb_table_destroy(table); /* Also destroys 'ts'. */
+}
+
struct do_query_cbdata {
struct uuid *row_uuids;
int *counts;
{ "compare-rows", 2, INT_MAX, do_compare_rows },
{ "parse-conditions", 2, INT_MAX, do_parse_conditions },
{ "evaluate-conditions", 3, 3, do_evaluate_conditions },
+ { "parse-mutations", 2, INT_MAX, do_parse_mutations },
+ { "execute-mutations", 3, 3, do_execute_mutations },
{ "query", 3, 3, do_query },
{ "query-distinct", 4, 4, do_query_distinct },
{ "transact", 1, INT_MAX, do_transact },