/* 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. * 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 #include "mutation.h" #include #include #include "column.h" #include "ovsdb-error.h" #include "openvswitch/json.h" #include "row.h" #include #include "table.h" #include "util.h" 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 OVS_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 OVS_WARN_UNUSED_RESULT struct ovsdb_error * ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts, const struct json *json, 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->array.n != 3 || json->array.elems[0]->type != JSON_STRING || json->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); } if (!m->column->mutable) { return ovsdb_syntax_error(json, "constraint violation", "Cannot mutate immutable column %s in " "table %s.", column_name, ts->name); } ovsdb_type_clone(&m->type, &m->column->type); mutator_name = json_string(array->elems[1]); error = ovsdb_mutator_from_string(mutator_name, &m->mutator); if (error) { goto exit; } /* 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); } ovsdb_base_type_clear_constraints(&m->type.key); m->type.n_min = m->type.n_max = 1; error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2], symtab); break; 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); ovsdb_base_type_destroy(&m->type.value); m->type.value.enum_ = NULL; m->type.value.type = OVSDB_TYPE_VOID; error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2], symtab); } break; default: OVS_NOT_REACHED(); } exit: if (error) { ovsdb_type_destroy(&m->type); } return error; } static void ovsdb_mutation_free(struct ovsdb_mutation *m) { ovsdb_datum_destroy(&m->arg, &m->type); ovsdb_type_destroy(&m->type); } struct ovsdb_error * ovsdb_mutation_set_from_json(const struct ovsdb_table_schema *ts, const struct json *json, 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); } 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; ovsdb_datum_unshare(dst, dst_type); 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); 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; case OVSDB_M_INSERT: ovsdb_datum_union(dst, arg, dst_type); error = ovsdb_mutation_check_count(dst, dst_type); break; case OVSDB_M_DELETE: ovsdb_datum_subtract(dst, dst_type, arg, arg_type); error = ovsdb_mutation_check_count(dst, dst_type); break; default: OVS_NOT_REACHED(); } if (error) { return error; } } return NULL; } 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 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 const struct ovsdb_scalar_mutation add_mutation = { add_int, add_double, OVSDB_M_ADD }; static const struct ovsdb_scalar_mutation sub_mutation = { sub_int, sub_double, OVSDB_M_SUB }; static const struct ovsdb_scalar_mutation mul_mutation = { mul_int, mul_double, OVSDB_M_MUL }; static const struct ovsdb_scalar_mutation div_mutation = { div_int, div_double, OVSDB_M_DIV }; static const struct ovsdb_scalar_mutation mod_mutation = { mod_int, NULL, OVSDB_M_MOD };