2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00
ovs/ovsdb/mutation.c
Ilya Maximets 1de4a08c22 json: Use functions to access json arrays.
Internal implementation of JSON array will be changed in the future
commits.  Add access functions that users can rely on instead of
accessing the internals of 'struct json' directly and convert all the
users.  Structure fields are intentionally renamed to make sure that
no code is using the old fields directly.

json_array() function is removed, as not needed anymore.  Added new
functions:  json_array_size(), json_array_at(), json_array_set()
and json_array_pop().  These are enough to cover all the use cases
within OVS.

The change is fairly large, however, IMO, it's a much overdue cleanup
that we need even without changing the underlying implementation.

Acked-by: Mike Pattrick <mkp@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2025-06-30 16:53:56 +02:00

543 lines
14 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 <config.h>
#include "mutation.h"
#include <float.h>
#include <limits.h>
#include "column.h"
#include "ovsdb-error.h"
#include "openvswitch/json.h"
#include "row.h"
#include <string.h>
#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)
{
struct ovsdb_error *error;
const char *mutator_name;
const char *column_name;
if (json->type != JSON_ARRAY
|| json_array_size(json) != 3
|| json_array_at(json, 0)->type != JSON_STRING
|| json_array_at(json, 1)->type != JSON_STRING) {
return ovsdb_syntax_error(json, NULL, "Parse error in mutation.");
}
column_name = json_string(json_array_at(json, 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(json_array_at(json, 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,
json_array_at(json, 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,
json_array_at(json, 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,
json_array_at(json, 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)
{
size_t i, n = json_array_size(json);
set->mutations = xmalloc(n * sizeof *set->mutations);
set->n_mutations = 0;
for (i = 0; i < n; i++) {
struct ovsdb_error *error;
error = ovsdb_mutation_from_json(ts, json_array_at(json, 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,
ME_NOTSUP
};
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));
case ME_NOTSUP:
return ovsdb_error(NULL, "Operation not supported.");
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 int
mod_double(double *x OVS_UNUSED, double y OVS_UNUSED)
{
return ME_NOTSUP;
}
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, mod_double, OVSDB_M_MOD
};