2012-09-05 10:35:20 -07:00
|
|
|
|
/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
|
2009-12-16 10:49:31 -08:00
|
|
|
|
*
|
|
|
|
|
* 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"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/json.h"
|
2009-12-16 10:49:31 -08:00
|
|
|
|
#include "row.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2009-12-16 10:49:31 -08:00
|
|
|
|
#include "table.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "util.h"
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
|
static OVS_WARN_UNUSED_RESULT struct ovsdb_error *
|
2009-12-16 10:49:31 -08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
|
static OVS_WARN_UNUSED_RESULT struct ovsdb_error *
|
2009-12-16 10:49:31 -08:00
|
|
|
|
ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts,
|
|
|
|
|
const struct json *json,
|
2010-02-08 16:03:21 -08:00
|
|
|
|
struct ovsdb_symbol_table *symtab,
|
2009-12-16 10:49:31 -08:00
|
|
|
|
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
|
2018-05-24 10:32:59 -07:00
|
|
|
|
|| json->array.n != 3
|
|
|
|
|
|| json->array.elems[0]->type != JSON_STRING
|
|
|
|
|
|| json->array.elems[1]->type != JSON_STRING) {
|
2009-12-16 10:49:31 -08:00
|
|
|
|
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);
|
|
|
|
|
}
|
2012-09-05 10:35:20 -07:00
|
|
|
|
if (!m->column->mutable) {
|
|
|
|
|
return ovsdb_syntax_error(json, "constraint violation",
|
|
|
|
|
"Cannot mutate immutable column %s in "
|
|
|
|
|
"table %s.", column_name, ts->name);
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
ovsdb_type_clone(&m->type, &m->column->type);
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
|
|
|
|
mutator_name = json_string(array->elems[1]);
|
|
|
|
|
error = ovsdb_mutator_from_string(mutator_name, &m->mutator);
|
|
|
|
|
if (error) {
|
2010-02-08 14:09:36 -08:00
|
|
|
|
goto exit;
|
2009-12-16 10:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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))
|
2010-02-08 14:09:36 -08:00
|
|
|
|
|| (m->type.key.type != OVSDB_TYPE_INTEGER
|
|
|
|
|
&& m->type.key.type != OVSDB_TYPE_REAL)
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|| (m->mutator == OVSDB_M_MOD
|
2010-02-08 14:09:36 -08:00
|
|
|
|
&& m->type.key.type == OVSDB_TYPE_REAL)) {
|
2009-12-16 10:49:31 -08:00
|
|
|
|
return type_mismatch(m, json);
|
|
|
|
|
}
|
2010-02-08 14:09:36 -08:00
|
|
|
|
ovsdb_base_type_clear_constraints(&m->type.key);
|
2009-12-16 10:49:31 -08:00
|
|
|
|
m->type.n_min = m->type.n_max = 1;
|
2010-02-08 14:09:36 -08:00
|
|
|
|
error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
|
|
|
|
|
symtab);
|
|
|
|
|
break;
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
|
|
|
|
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);
|
2019-11-12 12:32:35 +01:00
|
|
|
|
ovsdb_base_type_destroy(&m->type.value);
|
|
|
|
|
m->type.value.enum_ = NULL;
|
2010-02-08 14:09:36 -08:00
|
|
|
|
m->type.value.type = OVSDB_TYPE_VOID;
|
2009-12-16 10:49:31 -08:00
|
|
|
|
error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2],
|
|
|
|
|
symtab);
|
|
|
|
|
}
|
2010-02-08 14:09:36 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-12-16 10:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
exit:
|
|
|
|
|
if (error) {
|
|
|
|
|
ovsdb_type_destroy(&m->type);
|
|
|
|
|
}
|
|
|
|
|
return error;
|
2009-12-16 10:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ovsdb_mutation_free(struct ovsdb_mutation *m)
|
|
|
|
|
{
|
|
|
|
|
ovsdb_datum_destroy(&m->arg, &m->type);
|
2010-02-08 14:09:36 -08:00
|
|
|
|
ovsdb_type_destroy(&m->type);
|
2009-12-16 10:49:31 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ovsdb_error *
|
|
|
|
|
ovsdb_mutation_set_from_json(const struct ovsdb_table_schema *ts,
|
|
|
|
|
const struct json *json,
|
2010-02-08 16:03:21 -08:00
|
|
|
|
struct ovsdb_symbol_table *symtab,
|
2009-12-16 10:49:31 -08:00
|
|
|
|
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);
|
|
|
|
|
}
|
2010-02-08 14:09:36 -08:00
|
|
|
|
|
|
|
|
|
enum ovsdb_mutation_scalar_error {
|
|
|
|
|
ME_OK,
|
|
|
|
|
ME_DOM,
|
2023-10-23 16:22:39 +02:00
|
|
|
|
ME_RANGE,
|
|
|
|
|
ME_NOTSUP
|
2010-02-08 14:09:36 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
2023-10-23 16:22:39 +02:00
|
|
|
|
case ME_NOTSUP:
|
|
|
|
|
return ovsdb_error(NULL, "Operation not supported.");
|
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
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;
|
|
|
|
|
|
2022-07-01 01:34:06 +02:00
|
|
|
|
ovsdb_datum_unshare(dst, dst_type);
|
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
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 {
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2010-02-08 14:09:36 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < dst->n; i++) {
|
|
|
|
|
error = ovsdb_atom_check_constraints(&dst->keys[i], base);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-01 01:34:06 +02:00
|
|
|
|
error = ovsdb_datum_sort(dst, dst_type);
|
2010-02-08 14:09:36 -08:00
|
|
|
|
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(
|
2011-05-31 11:30:16 -07:00
|
|
|
|
"constraint violation",
|
2010-02-08 14:09:36 -08:00
|
|
|
|
"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;
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
case OVSDB_M_INSERT:
|
ovsdb-data: Optimize union of sets.
Current algorithm of ovsdb_datum_union looks like this:
for-each atom in b:
if not bin_search(a, atom):
push(a, clone(atom))
quicksort(a)
So, the complexity looks like this:
Nb * log2(Na) + Nb + (Na + Nb) * log2(Na + Nb)
Comparisons clones Comparisons for quicksort
for search
ovsdb_datum_union() is heavily used in database transactions while
new element is added to a set. For example, if new logical switch
port is added to a logical switch in OVN. This is a very common
use case where CMS adds one new port to an existing switch that
already has, let's say, 100 ports. For this case ovsdb-server will
have to perform:
1 * log2(100) + 1 clone + 101 * log2(101)
Comparisons Comparisons for
for search quicksort.
~7 1 ~707
Roughly 714 comparisons of atoms and 1 clone.
Since binary search can give us position, where new atom should go
(it's the 'low' index after the search completion) for free, the
logic can be re-worked like this:
copied = 0
for-each atom in b:
desired_position = bin_search(a, atom)
push(result, a[ copied : desired_position - 1 ])
copied = desired_position
push(result, clone(atom))
push(result, a[ copied : Na ])
swap(a, result)
Complexity of this schema:
Nb * log2(Na) + Nb + Na
Comparisons clones memory copy on push
for search
'swap' is just a swap of a few pointers. 'push' is not a 'clone',
but a simple memory copy of 'union ovsdb_atom'.
In general, this schema substitutes complexity of a quicksort
with complexity of a memory copy of Na atom structures, where we're
not even copying strings that these atoms are pointing to.
Complexity in the example above goes down from 714 comparisons
to 7 comparisons and memcpy of 100 * sizeof (union ovsdb_atom) bytes.
General complexity of a memory copy should always be lower than
complexity of a quicksort, especially because these copies usually
performed in bulk, so this new schema should work faster for any input.
All in all, this change allows to execute several times more
transactions per second for transactions that adds new entries to sets.
Alternatively, union can be implemented as a linear merge of two
sorted arrays, but this will result in O(Na) comparisons, which
is more than Nb * log2(Na) in common case, since Na is usually
far bigger than Nb. Linear merge will also mean per-atom memory
copies instead of copying in bulk.
'replace' functionality of ovsdb_datum_union() had no users, so it
just removed. But it can easily be added back if needed in the future.
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Acked-by: Han Zhou <hzhou@ovn.org>
Acked-by: Mark D. Gray <mark.d.gray@redhat.com>
2021-09-23 01:47:22 +02:00
|
|
|
|
ovsdb_datum_union(dst, arg, dst_type);
|
2010-02-08 14:09:36 -08:00
|
|
|
|
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;
|
2010-02-10 10:51:11 -08:00
|
|
|
|
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2010-02-08 14:09:36 -08:00
|
|
|
|
}
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-16 10:49:31 -08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 16:22:39 +02:00
|
|
|
|
static int
|
|
|
|
|
mod_double(double *x OVS_UNUSED, double y OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
return ME_NOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
static const struct ovsdb_scalar_mutation add_mutation = {
|
|
|
|
|
add_int, add_double, OVSDB_M_ADD
|
|
|
|
|
};
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
static const struct ovsdb_scalar_mutation sub_mutation = {
|
|
|
|
|
sub_int, sub_double, OVSDB_M_SUB
|
|
|
|
|
};
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
static const struct ovsdb_scalar_mutation mul_mutation = {
|
|
|
|
|
mul_int, mul_double, OVSDB_M_MUL
|
|
|
|
|
};
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
static const struct ovsdb_scalar_mutation div_mutation = {
|
|
|
|
|
div_int, div_double, OVSDB_M_DIV
|
|
|
|
|
};
|
2009-12-16 10:49:31 -08:00
|
|
|
|
|
2010-02-08 14:09:36 -08:00
|
|
|
|
static const struct ovsdb_scalar_mutation mod_mutation = {
|
2023-10-23 16:22:39 +02:00
|
|
|
|
mod_int, mod_double, OVSDB_M_MOD
|
2010-02-08 14:09:36 -08:00
|
|
|
|
};
|