diff options
author | Ben Pfaff <blp@nicira.com> | 2009-12-16 10:49:31 -0800 |
---|---|---|
committer | Ben Pfaff <blp@nicira.com> | 2009-12-16 10:56:04 -0800 |
commit | e9f8f9367e9e654b9582279608af86ea3744a2a0 (patch) | |
tree | cf2e666f93b00d94d673c1569299d8cee0ce8c16 /ovsdb/mutation.c | |
parent | a105c27b4e24ac0d29ba131eca00793bc3385dca (diff) | |
download | openvswitch-e9f8f9367e9e654b9582279608af86ea3744a2a0.tar.gz |
ovsdb: Add new "mutation" operation to transactions.
Diffstat (limited to 'ovsdb/mutation.c')
-rw-r--r-- | ovsdb/mutation.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/ovsdb/mutation.c b/ovsdb/mutation.c new file mode 100644 index 000000000..ba56827c4 --- /dev/null +++ b/ovsdb/mutation.c @@ -0,0 +1,462 @@ +/* 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; +} |