+\f
+enum ovsdb_mutation_scalar_error {
+ ME_OK,
+ ME_DOM,
+ ME_RANGE
+};
+
+struct ovsdb_scalar_mutation {
+ int (*mutate_integer)(int64_t *x, int64_t y);
+ int (*mutate_real)(double *x, double y);
+ enum ovsdb_mutator mutator;
+};
+
+static const struct ovsdb_scalar_mutation add_mutation;
+static const struct ovsdb_scalar_mutation sub_mutation;
+static const struct ovsdb_scalar_mutation mul_mutation;
+static const struct ovsdb_scalar_mutation div_mutation;
+static const struct ovsdb_scalar_mutation mod_mutation;
+
+static struct ovsdb_error *
+ovsdb_mutation_scalar_error(enum ovsdb_mutation_scalar_error error,
+ enum ovsdb_mutator mutator)
+{
+ switch (error) {
+ case ME_OK:
+ return OVSDB_BUG("unexpected success");
+
+ 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(mutator));
+
+ default:
+ return OVSDB_BUG("unexpected error");
+ }
+}
+
+static int
+check_real_range(double x)
+{
+ return x >= -DBL_MAX && x <= DBL_MAX ? 0 : ME_RANGE;
+}
+
+static struct ovsdb_error *
+mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst,
+ const union ovsdb_atom *arg,
+ const struct ovsdb_scalar_mutation *mutation)
+{
+ const struct ovsdb_base_type *base = &dst_type->key;
+ struct ovsdb_error *error;
+ unsigned int i;
+
+ if (base->type == OVSDB_TYPE_INTEGER) {
+ int64_t y = arg->integer;
+ for (i = 0; i < dst->n; i++) {
+ enum ovsdb_mutation_scalar_error me;
+
+ me = (mutation->mutate_integer)(&dst->keys[i].integer, y);
+ if (me != ME_OK) {
+ return ovsdb_mutation_scalar_error(me, mutation->mutator);
+ }
+ }
+ } else if (base->type == OVSDB_TYPE_REAL) {
+ double y = arg->real;
+ for (i = 0; i < dst->n; i++) {
+ double *x = &dst->keys[i].real;
+ enum ovsdb_mutation_scalar_error me;
+
+ me = (mutation->mutate_real)(x, y);
+ if (me == ME_OK) {
+ me = check_real_range(*x);
+ }
+ if (me != ME_OK) {
+ return ovsdb_mutation_scalar_error(me, mutation->mutator);
+ }
+ }
+ } else {
+ OVS_NOT_REACHED();
+ }
+
+ for (i = 0; i < dst->n; i++) {
+ error = ovsdb_atom_check_constraints(&dst->keys[i], base);
+ if (error) {
+ return error;
+ }
+ }
+
+ error = ovsdb_datum_sort(dst, dst_type->key.type);
+ if (error) {
+ ovsdb_error_destroy(error);
+ return ovsdb_error("constraint violation",
+ "Result of \"%s\" operation contains duplicates.",
+ ovsdb_mutator_to_string(mutation->mutator));
+ }
+ return NULL;
+}
+
+static struct ovsdb_error *
+ovsdb_mutation_check_count(struct ovsdb_datum *dst,
+ const struct ovsdb_type *dst_type)
+{
+ if (!ovsdb_datum_conforms_to_type(dst, dst_type)) {
+ char *s = ovsdb_type_to_english(dst_type);
+ struct ovsdb_error *e = ovsdb_error(
+ "constraint violation",
+ "Attempted to store %u elements in %s.", dst->n, s);
+ free(s);
+ return e;
+ }
+ return NULL;
+}
+
+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;
+ struct ovsdb_error *error;
+
+ switch (m->mutator) {
+ case OVSDB_M_ADD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &add_mutation);
+ break;
+
+ case OVSDB_M_SUB:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &sub_mutation);
+ break;
+
+ case OVSDB_M_MUL:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &mul_mutation);
+ break;
+
+ case OVSDB_M_DIV:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &div_mutation);
+ break;
+
+ case OVSDB_M_MOD:
+ error = mutate_scalar(dst_type, dst, &arg->keys[0], &mod_mutation);
+ break;