From 87ab878cadeb8cf5f8f89a7d00bfc64ee6b4edbf Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 16 Mar 2010 17:08:06 -0700 Subject: [PATCH] ovsdb: Allow constraining the number of rows in a table. --- ovsdb/SPECS | 13 +++++++++++++ ovsdb/table.c | 28 ++++++++++++++++++++++++---- ovsdb/table.h | 4 +++- ovsdb/transaction.c | 28 ++++++++++++++++++++++++++++ tests/ovsdb-execution.at | 15 +++++++++++++-- tests/ovsdb-table.at | 10 ++++++++++ 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/ovsdb/SPECS b/ovsdb/SPECS index f5d748c0..c926e212 100644 --- a/ovsdb/SPECS +++ b/ovsdb/SPECS @@ -101,6 +101,7 @@ is represented by , as described below. A JSON object with the following members: "columns": {: , ...} required + "maxRows": optional The value of "columns" is a JSON object whose names are column names and whose values are s. @@ -122,6 +123,13 @@ is represented by , as described below. the database process is stopped and then started again, each "_version" also changes to a new random value. + If "maxRows" is specified, as a positive integer, it limits the + maximum number of rows that may be present in the table. This is + a "deferred" constraint, enforced only at transaction commit time + (see the "transact" request below). If "maxRows" is not + specified, the size of the table is limited only by the resources + available to the database server. + A JSON object with the following members: @@ -362,6 +370,11 @@ include at least the following: transaction), and this column is not allowed to be empty because its has a "min" of 1. + "error": "constraint violation" + + The number of rows in a table exceeds the maximum number + permitted by the table's "maxRows" value (see ). + If "params" contains one or more "wait" operations, then the transaction may take an arbitrary amount of time to complete. The database implementation must be capable of accepting, executing, and diff --git a/ovsdb/table.c b/ovsdb/table.c index 3f35b86e..6a4e7ae2 100644 --- a/ovsdb/table.c +++ b/ovsdb/table.c @@ -18,6 +18,7 @@ #include "table.h" #include +#include #include "json.h" #include "column.h" @@ -35,7 +36,8 @@ add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column) } struct ovsdb_table_schema * -ovsdb_table_schema_create(const char *name, bool mutable) +ovsdb_table_schema_create(const char *name, bool mutable, + unsigned int max_rows) { struct ovsdb_column *uuid, *version; struct ovsdb_table_schema *ts; @@ -44,6 +46,7 @@ ovsdb_table_schema_create(const char *name, bool mutable) ts->name = xstrdup(name); ts->mutable = mutable; shash_init(&ts->columns); + ts->max_rows = max_rows; uuid = ovsdb_column_create("_uuid", false, true, &ovsdb_type_uuid); add_column(ts, uuid); @@ -62,7 +65,7 @@ ovsdb_table_schema_clone(const struct ovsdb_table_schema *old) struct ovsdb_table_schema *new; struct shash_node *node; - new = ovsdb_table_schema_create(old->name, old->mutable); + new = ovsdb_table_schema_create(old->name, old->mutable, old->max_rows); SHASH_FOR_EACH (node, &old->columns) { const struct ovsdb_column *column = node->data; @@ -94,10 +97,11 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, struct ovsdb_table_schema **tsp) { struct ovsdb_table_schema *ts; - const struct json *columns, *mutable; + const struct json *columns, *mutable, *max_rows; struct shash_node *node; struct ovsdb_parser parser; struct ovsdb_error *error; + long long int n_max_rows; *tsp = NULL; @@ -105,18 +109,31 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name, columns = ovsdb_parser_member(&parser, "columns", OP_OBJECT); mutable = ovsdb_parser_member(&parser, "mutable", OP_TRUE | OP_FALSE | OP_OPTIONAL); + max_rows = ovsdb_parser_member(&parser, "maxRows", + OP_INTEGER | OP_OPTIONAL); error = ovsdb_parser_finish(&parser); if (error) { return error; } + if (max_rows) { + if (json_integer(max_rows) <= 0) { + return ovsdb_syntax_error(json, NULL, + "maxRows must be at least 1"); + } + n_max_rows = max_rows->u.integer; + } else { + n_max_rows = UINT_MAX; + } + if (shash_is_empty(json_object(columns))) { return ovsdb_syntax_error(json, NULL, "table must have at least one column"); } ts = ovsdb_table_schema_create(name, - mutable ? json_boolean(mutable) : true); + mutable ? json_boolean(mutable) : true, + MIN(n_max_rows, UINT_MAX)); SHASH_FOR_EACH (node, json_object(columns)) { struct ovsdb_column *column; @@ -160,6 +177,9 @@ ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts) } } json_object_put(json, "columns", columns); + if (ts->max_rows != UINT_MAX) { + json_object_put(json, "maxRows", json_integer_create(ts->max_rows)); + } return json; } diff --git a/ovsdb/table.h b/ovsdb/table.h index ff99cf17..4d3b9ee7 100644 --- a/ovsdb/table.h +++ b/ovsdb/table.h @@ -29,10 +29,12 @@ struct ovsdb_table_schema { char *name; bool mutable; struct shash columns; /* Contains "struct ovsdb_column *"s. */ + unsigned int max_rows; /* Maximum number of rows. */ }; struct ovsdb_table_schema *ovsdb_table_schema_create(const char *name, - bool mutable); + bool mutable, + unsigned int max_rows); struct ovsdb_table_schema *ovsdb_table_schema_clone( const struct ovsdb_table_schema *); void ovsdb_table_schema_destroy(struct ovsdb_table_schema *); diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c index 2e4c73a8..218fbce3 100644 --- a/ovsdb/transaction.c +++ b/ovsdb/transaction.c @@ -441,6 +441,27 @@ determine_changes(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) return NULL; } +static struct ovsdb_error * WARN_UNUSED_RESULT +check_max_rows(struct ovsdb_txn *txn) +{ + struct ovsdb_txn_table *t; + + LIST_FOR_EACH (t, struct ovsdb_txn_table, node, &txn->txn_tables) { + size_t n_rows = hmap_count(&t->table->rows); + unsigned int max_rows = t->table->schema->max_rows; + + if (n_rows > max_rows) { + return ovsdb_error("constraint violation", + "transaction causes \"%s\" table to contain " + "%zu rows, greater than the schema-defined " + "limit of %u row(s)", + t->table->schema->name, n_rows, max_rows); + } + } + + return NULL; +} + struct ovsdb_error * ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable) { @@ -459,6 +480,13 @@ ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable) return NULL; } + /* Check maximum rows table constraints. */ + error = check_max_rows(txn); + if (error) { + ovsdb_txn_abort(txn); + return error; + } + /* Update reference counts and check referential integrity. */ error = update_ref_counts(txn); if (error) { diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at index dc4f3e88..bbdcbb50 100644 --- a/tests/ovsdb-execution.at +++ b/tests/ovsdb-execution.at @@ -28,7 +28,8 @@ m4_define([CONSTRAINT_SCHEMA], "constrained": { "columns": { "positive": {"type": {"key": {"type": "integer", - "minInteger": 1}}}}}}}]]) + "minInteger": 1}}}}, + "maxRows": 1}}}]]) m4_define([WEAK_SCHEMA], [[{"name": "weak", @@ -462,10 +463,20 @@ OVSDB_CHECK_EXECUTION([insert and update constraints], {"op": "update", "table": "constrained", "where": [], - "row": {"positive": -2}}]]]], + "row": {"positive": -2}}]]], + [[["constraints", + {"op": "insert", + "table": "constrained", + "row": {"positive": 1}}]]], + [[["constraints", + {"op": "insert", + "table": "constrained", + "row": {"positive": 2}}]]]], [[[{"details":"0 is less than minimum allowed value 1","error":"constraint violation"}] [{"details":"-1 is less than minimum allowed value 1","error":"constraint violation"}] [{"details":"-2 is less than minimum allowed value 1","error":"constraint violation"}] +[{"uuid":["uuid","<0>"]}] +[{"uuid":["uuid","<1>"]},{"details":"transaction causes \"constrained\" table to contain 2 rows, greater than the schema-defined limit of 1 row(s)","error":"constraint violation"}] ]]) OVSDB_CHECK_EXECUTION([referential integrity -- simple], diff --git a/tests/ovsdb-table.at b/tests/ovsdb-table.at index 25f5dddf..623dd6dd 100644 --- a/tests/ovsdb-table.at +++ b/tests/ovsdb-table.at @@ -10,6 +10,11 @@ OVSDB_CHECK_POSITIVE([immutable table with one column], "mutable": false}']], [[{"columns":{"name":{"type":"string"}},"mutable":false}]]) +OVSDB_CHECK_POSITIVE([table with maxRows of 2], + [[parse-table mytable '{"columns": {"name": {"type": "string"}}, + "maxRows": 2}']], + [[{"columns":{"name":{"type":"string"}},"maxRows":2}]]) + OVSDB_CHECK_NEGATIVE([column names may not begin with _], [[parse-table mytable \ '{"columns": {"_column": {"type": "integer"}}}']], @@ -23,3 +28,8 @@ OVSDB_CHECK_NEGATIVE([table must have at least one column (1)], OVSDB_CHECK_NEGATIVE([table must have at least one column (2)], [[parse-table mytable '{"columns": {}}']], [[table must have at least one column]]) + +OVSDB_CHECK_NEGATIVE([table maxRows must be positive], + [[parse-table mytable '{"columns": {"name": {"type": "string"}}, + "maxRows": 0}']], + [[syntax "{"columns":{"name":{"type":"string"}},"maxRows":0}": syntax error: maxRows must be at least 1]]) -- 2.30.2