[ovs-dev] [PATCH 7/8] ovsdb: Add support for weak references.
Ben Pfaff
blp at nicira.com
Mon Mar 15 22:42:55 UTC 2010
---
lib/ovsdb-types.c | 23 ++++++
lib/ovsdb-types.h | 33 +++++++++-
ovsdb/OVSDB.py | 13 +++-
ovsdb/SPECS | 33 +++++++--
ovsdb/row.c | 17 +++++
ovsdb/row.h | 34 ++++++++--
ovsdb/transaction.c | 145 +++++++++++++++++++++++++++++++++++++++-
tests/ovsdb-execution.at | 162 ++++++++++++++++++++++++++++++++++++++++++++
tests/uuidfilt.pl | 12 +++
vswitchd/vswitch.ovsschema | 11 ++-
10 files changed, 462 insertions(+), 21 deletions(-)
diff --git a/lib/ovsdb-types.c b/lib/ovsdb-types.c
index df18ee5..b3452dd 100644
--- a/lib/ovsdb-types.c
+++ b/lib/ovsdb-types.c
@@ -412,10 +412,30 @@ ovsdb_base_type_from_json(struct ovsdb_base_type *base,
refTable = ovsdb_parser_member(&parser, "refTable",
OP_ID | OP_OPTIONAL);
if (refTable) {
+ const struct json *refType;
+
base->u.uuid.refTableName = xstrdup(refTable->u.string);
+
/* We can't set base->u.uuid.refTable here because we don't have
* enough context (we might not even be running in ovsdb-server).
* ovsdb_create() will set refTable later. */
+
+ refType = ovsdb_parser_member(&parser, "refType",
+ OP_ID | OP_OPTIONAL);
+ if (refType) {
+ const char *refType_s = json_string(refType);
+ if (!strcmp(refType_s, "strong")) {
+ base->u.uuid.refType = OVSDB_REF_STRONG;
+ } else if (!strcmp(refType_s, "weak")) {
+ base->u.uuid.refType = OVSDB_REF_WEAK;
+ } else {
+ error = ovsdb_syntax_error(json, NULL, "refType must be "
+ "\"strong\" or \"weak\" (not "
+ "\"%s\")", refType_s);
+ }
+ } else {
+ base->u.uuid.refType = OVSDB_REF_STRONG;
+ }
}
}
@@ -495,6 +515,9 @@ ovsdb_base_type_to_json(const struct ovsdb_base_type *base)
if (base->u.uuid.refTableName) {
json_object_put_string(json, "refTable",
base->u.uuid.refTableName);
+ if (base->u.uuid.refType == OVSDB_REF_WEAK) {
+ json_object_put_string(json, "refType", "weak");
+ }
}
break;
diff --git a/lib/ovsdb-types.h b/lib/ovsdb-types.h
index 6f1727e..6903aa8 100644
--- a/lib/ovsdb-types.h
+++ b/lib/ovsdb-types.h
@@ -44,6 +44,11 @@ struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type);
/* An atomic type plus optional constraints. */
+enum ovsdb_ref_type {
+ OVSDB_REF_STRONG, /* Target must exist. */
+ OVSDB_REF_WEAK /* Delete reference if target disappears. */
+};
+
struct ovsdb_base_type {
enum ovsdb_atomic_type type;
@@ -72,6 +77,7 @@ struct ovsdb_base_type {
struct ovsdb_uuid_constraints {
char *refTableName; /* Name of referenced table, or NULL. */
struct ovsdb_table *refTable; /* Referenced table, if available. */
+ enum ovsdb_ref_type refType; /* Reference type. */
} uuid;
} u;
};
@@ -85,7 +91,7 @@ struct ovsdb_base_type {
#define OVSDB_BASE_STRING_INIT { .type = OVSDB_TYPE_STRING, \
.u.string = { 0, UINT_MAX } }
#define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID, \
- .u.uuid = { NULL, NULL } }
+ .u.uuid = { NULL, NULL, 0 } }
void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type);
void ovsdb_base_type_clone(struct ovsdb_base_type *,
@@ -101,6 +107,11 @@ struct ovsdb_error *ovsdb_base_type_from_json(struct ovsdb_base_type *,
const struct json *)
WARN_UNUSED_RESULT;
struct json *ovsdb_base_type_to_json(const struct ovsdb_base_type *);
+
+static inline bool ovsdb_base_type_is_ref(const struct ovsdb_base_type *);
+static inline bool ovsdb_base_type_is_strong_ref(
+ const struct ovsdb_base_type *);
+static inline bool ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *);
/* An OVSDB type.
*
@@ -160,6 +171,26 @@ ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type)
return atomic_type >= 0 && atomic_type < OVSDB_N_TYPES;
}
+static inline bool
+ovsdb_base_type_is_ref(const struct ovsdb_base_type *base)
+{
+ return base->type == OVSDB_TYPE_UUID && base->u.uuid.refTable;
+}
+
+static inline bool
+ovsdb_base_type_is_strong_ref(const struct ovsdb_base_type *base)
+{
+ return (ovsdb_base_type_is_ref(base)
+ && base->u.uuid.refType == OVSDB_REF_STRONG);
+}
+
+static inline bool
+ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *base)
+{
+ return (ovsdb_base_type_is_ref(base)
+ && base->u.uuid.refType == OVSDB_REF_WEAK);
+}
+
static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type)
{
return (type->value.type == OVSDB_TYPE_VOID
diff --git a/ovsdb/OVSDB.py b/ovsdb/OVSDB.py
index 5297229..f01c45b 100644
--- a/ovsdb/OVSDB.py
+++ b/ovsdb/OVSDB.py
@@ -196,13 +196,14 @@ class Atom:
class BaseType:
def __init__(self, type,
enum=None,
- refTable=None,
+ refTable=None, refType="strong",
minInteger=None, maxInteger=None,
minReal=None, maxReal=None,
minLength=None, maxLength=None):
self.type = type
self.enum = enum
self.refTable = refTable
+ self.refType = refType
self.minInteger = minInteger
self.maxInteger = maxInteger
self.minReal = minReal
@@ -221,17 +222,23 @@ class BaseType:
enumType = Type(atomicType, None, 0, 'unlimited')
enum = Datum.fromJson(enumType, enum)
refTable = getMember(json, 'refTable', [unicode], description)
+ refType = getMember(json, 'refType', [unicode], description)
+ if refType == None:
+ refType = "strong"
minInteger = getMember(json, 'minInteger', [int, long], description)
maxInteger = getMember(json, 'maxInteger', [int, long], description)
minReal = getMember(json, 'minReal', [int, long, float], description)
maxReal = getMember(json, 'maxReal', [int, long, float], description)
minLength = getMember(json, 'minLength', [int], description)
maxLength = getMember(json, 'minLength', [int], description)
- return BaseType(atomicType, enum, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
+ return BaseType(atomicType, enum, refTable, refType, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
def toEnglish(self, escapeLiteral=returnUnchanged):
if self.type == 'uuid' and self.refTable:
- return escapeLiteral(self.refTable)
+ s = escapeLiteral(self.refTable)
+ if self.refType == 'weak':
+ s = "weak reference to " + s
+ return s
else:
return self.type
diff --git a/ovsdb/SPECS b/ovsdb/SPECS
index db50417..f5d748c 100644
--- a/ovsdb/SPECS
+++ b/ovsdb/SPECS
@@ -175,6 +175,7 @@ is represented by <database-schema>, as described below.
"minLength": <integer> optional, strings only
"maxLength": <integer> optional, strings only
"refTable": <id> optional, uuids only
+ "refType": "strong" or "weak" optional, only with "refTable"
An <atomic-type> by itself is equivalent to a JSON object with a
single member "type" whose value is the <atomic-type>.
@@ -203,8 +204,17 @@ is represented by <database-schema>, as described below.
bytes or UTF-16 code units).
If "type" is "uuid", then "refTable", if present, must be the name
- of a table within this database. If "refTable" is set, the
- allowed UUIDs are limited to UUIDs for rows in the named table.
+ of a table within this database. If "refTable" is specified, then
+ "refType" may also be specified. If "refTable" is set, the effect
+ depends on "refType":
+
+ - If "refType" is "strong" or if "refType" is omitted, the
+ allowed UUIDs are limited to UUIDs for rows in the named
+ table.
+
+ - If "refType" is "weak", then any UUIDs are allowed, but
+ UUIDs that do not correspond to rows in the named table will
+ be automatically deleted.
"refTable" constraints are "deferred" constraints: they are
enforced only at transaction commit time (see the "transact"
@@ -337,11 +347,20 @@ include at least the following:
When the commit was attempted, a column's value referenced the
UUID for a row that did not exist in the table named by the
- column's <base-type> key or value "refTable". (This can be
- caused by inserting a row that references a nonexistent row,
- by deleting a row that is still referenced by another row, by
- specifying the UUID for a row in the wrong table, and other
- ways.)
+ column's <base-type> key or value "refTable" that has a
+ "refType" of "strong". (This can be caused by inserting a row
+ that references a nonexistent row, by deleting a row that is
+ still referenced by another row, by specifying the UUID for a
+ row in the wrong table, and other ways.)
+
+ "error": "constraint violation"
+
+ A column with a <base-type> key or value "refTable" whose
+ "refType" is "weak" became empty due to deletion(s) caused
+ because the rows that it referenced were deleted (or never
+ existed, if the column's row was inserted within the
+ transaction), and this column is not allowed to be empty
+ because its <type> has a "min" of 1.
If "params" contains one or more "wait" operations, then the
transaction may take an arbitrary amount of time to complete. The
diff --git a/ovsdb/row.c b/ovsdb/row.c
index d088ff9..5043cbc 100644
--- a/ovsdb/row.c
+++ b/ovsdb/row.c
@@ -35,6 +35,8 @@ allocate_row(const struct ovsdb_table *table)
struct ovsdb_row *row = xmalloc(row_size);
row->table = (struct ovsdb_table *) table;
row->txn_row = NULL;
+ list_init(&row->src_refs);
+ list_init(&row->dst_refs);
row->n_refs = 0;
return row;
}
@@ -77,8 +79,23 @@ ovsdb_row_destroy(struct ovsdb_row *row)
{
if (row) {
const struct ovsdb_table *table = row->table;
+ struct ovsdb_weak_ref *weak, *next;
const struct shash_node *node;
+ LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, dst_node,
+ &row->dst_refs) {
+ list_remove(&weak->src_node);
+ list_remove(&weak->dst_node);
+ free(weak);
+ }
+
+ LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, src_node,
+ &row->src_refs) {
+ list_remove(&weak->src_node);
+ list_remove(&weak->dst_node);
+ free(weak);
+ }
+
SHASH_FOR_EACH (node, &table->schema->columns) {
const struct ovsdb_column *column = node->data;
ovsdb_datum_destroy(&row->fields[column->index], &column->type);
diff --git a/ovsdb/row.h b/ovsdb/row.h
index 302f61a..6c249a1 100644
--- a/ovsdb/row.h
+++ b/ovsdb/row.h
@@ -20,20 +20,42 @@
#include <stdint.h>
#include "column.h"
#include "hmap.h"
+#include "list.h"
#include "ovsdb-data.h"
struct ovsdb_column_set;
+/* A weak reference.
+ *
+ * When a column in row A contains a weak reference to UUID of a row B this
+ * constitutes a weak reference from A (the source) to B (the destination).
+ *
+ * Rows A and B may be in the same table or different tables.
+ *
+ * Weak references from a row to itself are allowed, but no "struct
+ * ovsdb_weak_ref" structures are created for them.
+ */
+struct ovsdb_weak_ref {
+ struct list src_node; /* In src->src_refs list. */
+ struct list dst_node; /* In destination row's dst_refs list. */
+ struct ovsdb_row *src; /* Source row. */
+};
+
/* A row in a database table. */
struct ovsdb_row {
- struct ovsdb_table *table; /* Table to which this belongs. */
- struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
+ struct ovsdb_table *table; /* Table to which this belongs. */
+ struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */
struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */
- /* Number of refs to this row from other rows, in this table or other
- * tables, through 'uuid' columns that have a 'refTable' constraint
- * pointing to this table. A row with nonzero 'n_refs' cannot be deleted.
- * Updated and checked only at transaction commit. */
+ /* Weak references. */
+ struct list src_refs; /* Weak references from this row. */
+ struct list dst_refs; /* Weak references to this row. */
+
+ /* Number of strong refs to this row from other rows, in this table or
+ * other tables, through 'uuid' columns that have a 'refTable' constraint
+ * pointing to this table and a 'refType' of "strong". A row with nonzero
+ * 'n_refs' cannot be deleted. Updated and checked only at transaction
+ * commit. */
size_t n_refs;
struct ovsdb_datum fields[];
diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
index d2654a7..b01f5e1 100644
--- a/ovsdb/transaction.c
+++ b/ovsdb/transaction.c
@@ -160,7 +160,7 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn,
const struct ovsdb_table *table;
unsigned int i;
- if (base->type != OVSDB_TYPE_UUID || !base->u.uuid.refTable) {
+ if (!ovsdb_base_type_is_strong_ref(base)) {
return NULL;
}
@@ -270,6 +270,141 @@ ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED,
return NULL;
}
+static void
+add_weak_ref(struct ovsdb_txn *txn,
+ const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
+{
+ struct ovsdb_row *src = (struct ovsdb_row *) src_;
+ struct ovsdb_row *dst = (struct ovsdb_row *) dst_;
+ struct ovsdb_weak_ref *weak;
+
+ if (src == dst) {
+ return;
+ }
+
+ dst = ovsdb_txn_row_modify(txn, dst);
+
+ if (!list_is_empty(&dst->dst_refs)) {
+ /* Omit duplicates. */
+ weak = CONTAINER_OF(list_back(&dst->dst_refs),
+ struct ovsdb_weak_ref, dst_node);
+ if (weak->src == src) {
+ return;
+ }
+ }
+
+ weak = xmalloc(sizeof *weak);
+ weak->src = src;
+ list_push_back(&dst->dst_refs, &weak->dst_node);
+ list_push_back(&src->src_refs, &weak->src_node);
+}
+
+static struct ovsdb_error * WARN_UNUSED_RESULT
+assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
+{
+ struct ovsdb_table *table;
+ struct shash_node *node;
+
+ if (txn_row->old) {
+ /* Mark rows that have weak references to 'txn_row' as modified, so
+ * that their weak references will get reassessed. */
+ struct ovsdb_weak_ref *weak, *next;
+
+ LIST_FOR_EACH_SAFE (weak, next, struct ovsdb_weak_ref, dst_node,
+ &txn_row->old->dst_refs) {
+ if (!weak->src->txn_row) {
+ ovsdb_txn_row_modify(txn, weak->src);
+ }
+ }
+ }
+
+ if (!txn_row->new) {
+ /* We don't have to do anything about references that originate at
+ * 'txn_row', because ovsdb_row_destroy() will remove those weak
+ * references. */
+ return NULL;
+ }
+
+ table = txn_row->new->table;
+ SHASH_FOR_EACH (node, &table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+ struct ovsdb_datum *datum = &txn_row->new->fields[column->index];
+ unsigned int orig_n, i;
+ bool zero = false;
+
+ orig_n = datum->n;
+
+ if (ovsdb_base_type_is_weak_ref(&column->type.key)) {
+ for (i = 0; i < datum->n; ) {
+ const struct ovsdb_row *row;
+
+ row = ovsdb_table_get_row(column->type.key.u.uuid.refTable,
+ &datum->keys[i].uuid);
+ if (row) {
+ add_weak_ref(txn, txn_row->new, row);
+ i++;
+ } else {
+ if (uuid_is_zero(&datum->keys[i].uuid)) {
+ zero = true;
+ }
+ ovsdb_datum_remove_unsafe(datum, i, &column->type);
+ }
+ }
+ }
+
+ if (ovsdb_base_type_is_weak_ref(&column->type.value)) {
+ for (i = 0; i < datum->n; ) {
+ const struct ovsdb_row *row;
+
+ row = ovsdb_table_get_row(column->type.value.u.uuid.refTable,
+ &datum->values[i].uuid);
+ if (row) {
+ add_weak_ref(txn, txn_row->new, row);
+ i++;
+ } else {
+ if (uuid_is_zero(&datum->values[i].uuid)) {
+ zero = true;
+ }
+ ovsdb_datum_remove_unsafe(datum, i, &column->type);
+ }
+ }
+ }
+
+ if (datum->n != orig_n) {
+ ovsdb_datum_sort_assert(datum, column->type.key.type);
+ if (datum->n < column->type.n_min) {
+ const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new);
+ if (zero && !txn_row->old) {
+ return ovsdb_error(
+ "constraint violation",
+ "Weak reference column \"%s\" in \"%s\" row "UUID_FMT
+ " (inserted within this transaction) contained "
+ "all-zeros UUID (probably as the default value for "
+ "this column) but deleting this value caused a "
+ "constraint volation because this column is not "
+ "allowed to be empty.", column->name,
+ table->schema->name, UUID_ARGS(row_uuid));
+ } else {
+ return ovsdb_error(
+ "constraint violation",
+ "Deletion of %u weak reference(s) to deleted (or "
+ "never-existing) rows from column \"%s\" in \"%s\" "
+ "row "UUID_FMT" %scaused this column to become empty, "
+ "but constraints on this column disallow an "
+ "empty column.",
+ orig_n - datum->n, column->name, table->schema->name,
+ UUID_ARGS(row_uuid),
+ (txn_row->old
+ ? ""
+ : "(inserted within this transaction) "));
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
static struct ovsdb_error * WARN_UNUSED_RESULT
determine_changes(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
{
@@ -330,6 +465,14 @@ ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
return error;
}
+ /* Check reference counts and remove bad reference for "weak" referential
+ * integrity. */
+ error = for_each_txn_row(txn, assess_weak_refs);
+ if (error) {
+ ovsdb_txn_abort(txn);
+ return error;
+ }
+
/* Send the commit to each replica. */
LIST_FOR_EACH (replica, struct ovsdb_replica, node, &txn->db->replicas) {
error = (replica->class->commit)(replica, txn, durable);
diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at
index ed28b2a..dc4f3e8 100644
--- a/tests/ovsdb-execution.at
+++ b/tests/ovsdb-execution.at
@@ -30,6 +30,30 @@ m4_define([CONSTRAINT_SCHEMA],
"positive": {"type": {"key": {"type": "integer",
"minInteger": 1}}}}}}}]])
+m4_define([WEAK_SCHEMA],
+ [[{"name": "weak",
+ "tables": {
+ "a": {
+ "columns": {
+ "a": {"type": "integer"},
+ "a2a": {"type": {"key": {"type": "uuid",
+ "refTable": "a",
+ "refType": "weak"},
+ "min": 0, "max": "unlimited"}},
+ "a2a1": {"type": {"key": {"type": "uuid",
+ "refTable": "a",
+ "refType": "weak"}}},
+ "a2b": {"type": {"key": {"type": "uuid",
+ "refTable": "b",
+ "refType": "weak"}}}}},
+ "b": {
+ "columns": {
+ "b": {"type": "integer"},
+ "b2a": {"type": {"key": {"type": "uuid",
+ "refTable": "a",
+ "refType": "weak"},
+ "min": 0, "max": "unlimited"}}}}}}]])
+
# OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
#
# Runs "test-ovsdb execute" with the given SCHEMA and each of the
@@ -551,6 +575,144 @@ OVSDB_CHECK_EXECUTION([referential integrity -- mutual references],
[{"count":1},{"details":"cannot delete a row <0> because of 1 remaining reference(s)","error":"referential integrity violation"}]
[{"count":1},{"details":"cannot delete b row <1> because of 1 remaining reference(s)","error":"referential integrity violation"}]
[{"count":1},{"count":1}]
+]])
+
+OVSDB_CHECK_EXECUTION([weak references],
+ [WEAK_SCHEMA],
+ [[[["weak",
+ {"op": "insert",
+ "table": "a",
+ "row": {"a": 0,
+ "a2a": ["set", [["named-uuid", "row1"],
+ ["named-uuid", "row2"],
+ ["uuid", "0e767b36-6822-4044-8307-d58467e04669"]]],
+ "a2a1": ["named-uuid", "row1"],
+ "a2b": ["named-uuid", "row3"]},
+ "uuid-name": "row1"},
+ {"op": "insert",
+ "table": "a",
+ "row": {"a": 1,
+ "a2a": ["set", [["named-uuid", "row1"],
+ ["named-uuid", "row2"]]],
+ "a2a1": ["named-uuid", "row2"],
+ "a2b": ["named-uuid", "row3"]},
+ "uuid-name": "row2"},
+ {"op": "insert",
+ "table": "a",
+ "row": {"a": 2,
+ "a2a": ["set", [["named-uuid", "row1"],
+ ["named-uuid", "row2"]]],
+ "a2a1": ["named-uuid", "row2"],
+ "a2b": ["named-uuid", "row4"]}},
+ {"op": "insert",
+ "table": "b",
+ "row": {"b": 2,
+ "b2a": ["named-uuid", "row1"]},
+ "uuid-name": "row3"},
+ {"op": "insert",
+ "table": "b",
+ "row": {"b": 3,
+ "b2a": ["named-uuid", "row2"]},
+ "uuid-name": "row4"}]]],
+ dnl Check that the nonexistent row UUID we added to row a0 was deleted,
+ dnl and that other rows were inserted as requested.
+ [[["weak",
+ {"op": "select",
+ "table": "a",
+ "where": [],
+ "columns": ["_uuid", "a2a", "a2a1", "a2b"],
+ "sort": ["a"]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "b",
+ "where": [],
+ "columns": ["_uuid", "b", "b2a"],
+ "sort": ["b"]}]]],
+ dnl Try to insert invalid all-zeros weak reference (the default) into
+ dnl "a2b", which requires exactly one value.
+ [[["weak",
+ {"op": "insert",
+ "table": "a",
+ "row": {}}]]],
+ dnl Try to delete row from "b" that is referred to by weak references
+ dnl from "a" table "a2b" column that requires exactly one value.
+ [[["weak",
+ {"op": "delete",
+ "table": "b",
+ "where": [["b", "==", 3]]}]]],
+ dnl Try to delete row from "a" that is referred to by weak references
+ dnl from "a" table "a2a1" column that requires exactly one value.
+ [[["weak",
+ {"op": "delete",
+ "table": "a",
+ "where": [["a", "==", 1]]}]]],
+ dnl Delete the row that had the reference that caused the previous
+ dnl deletion to fail, then check that other rows are unchanged.
+ [[["weak",
+ {"op": "delete",
+ "table": "a",
+ "where": [["a", "==", 2]]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "a",
+ "where": [],
+ "columns": ["_uuid", "a2a", "a2a1", "a2b"],
+ "sort": ["a"]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "b",
+ "where": [],
+ "columns": ["_uuid", "b", "b2a"],
+ "sort": ["b"]}]]],
+ dnl Delete row a0 then check that references to it were removed.
+ [[["weak",
+ {"op": "delete",
+ "table": "a",
+ "where": [["a", "==", 0]]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "a",
+ "where": [],
+ "columns": ["_uuid", "a2a", "a2a1", "a2b"],
+ "sort": ["a"]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "b",
+ "where": [],
+ "columns": ["_uuid", "b", "b2a"],
+ "sort": ["b"]}]]],
+ dnl Delete row a1 then check that references to it were removed.
+ [[["weak",
+ {"op": "delete",
+ "table": "a",
+ "where": [["a", "==", 1]]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "a",
+ "where": [],
+ "columns": ["_uuid", "a2a", "a2a1", "a2b"],
+ "sort": ["a"]}]]],
+ [[["weak",
+ {"op": "select",
+ "table": "b",
+ "where": [],
+ "columns": ["_uuid", "b", "b2a"],
+ "sort": ["b"]}]]]],
+ [[[{"uuid":["uuid","<0>"]},{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}]
+[{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<2>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<4>"]}]}]
+[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
+[{"uuid":["uuid","<5>"]},{"details":"Weak reference column \"a2b\" in \"a\" row <5> (inserted within this transaction) contained all-zeros UUID (probably as the default value for this column) but deleting this value caused a constraint volation because this column is not allowed to be empty.","error":"constraint violation"}]
+[{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2b\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}]
+[{"count":1},{"details":"Deletion of 1 weak reference(s) to deleted (or never-existing) rows from column \"a2a1\" in \"a\" row <2> caused this column to become empty, but constraints on this column disallow an empty column.","error":"constraint violation"}]
+[{"count":1}]
+[{"rows":[{"_uuid":["uuid","<0>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<0>"],"a2b":["uuid","<3>"]},{"_uuid":["uuid","<1>"],"a2a":["set",[["uuid","<0>"],["uuid","<1>"]]],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}]
+[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["uuid","<0>"]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
+[{"count":1}]
+[{"rows":[{"_uuid":["uuid","<1>"],"a2a":["uuid","<1>"],"a2a1":["uuid","<1>"],"a2b":["uuid","<3>"]}]}]
+[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["uuid","<1>"]}]}]
+[{"count":1}]
+[{"rows":[]}]
+[{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}]
]])])
EXECUTION_EXAMPLES
diff --git a/tests/uuidfilt.pl b/tests/uuidfilt.pl
index 6f003a5..835f13b 100755
--- a/tests/uuidfilt.pl
+++ b/tests/uuidfilt.pl
@@ -13,9 +13,21 @@ sub lookup_uuid {
return "<$uuids{$uuid}>";
}
+sub sort_set {
+ my ($s) = @_;
+ my (@uuids) = sort { $a <=> $b } (grep(/\d+/, split(/(\d+)/, $s)));
+ return '["set",[' . join(',', map('["uuid","<' . $_ . '>"]', @uuids)) . ']]';
+}
+
my $u = '[0-9a-fA-F]';
my $uuid_re = "${u}{8}-${u}{4}-${u}{4}-${u}{4}-${u}{12}";
while (<>) {
s/($uuid_re)/lookup_uuid($1)/eg;
+
+ # Sort sets like this:
+ # [["uuid","<1>"],["uuid","<0>"]]
+ # to look like this:
+ # [["uuid","<0>"],["uuid","<1>"]]
+ s/(\["set",\[(,?\["uuid","<\d+>"\])+\]\])/sort_set($1)/ge;
print $_;
}
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index f0217cd..c148b6e 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -122,11 +122,14 @@
"type": "string"},
"select_src_port": {
"type": {"key": {"type": "uuid",
- "refTable": "Port"},
+ "refTable": "Port",
+ "refType": "weak"},
"min": 0, "max": "unlimited"}},
"select_dst_port": {
"type": {"key": {"type": "uuid",
- "refTable": "Port"}, "min": 0, "max": "unlimited"}},
+ "refTable": "Port",
+ "refType": "weak"},
+ "min": 0, "max": "unlimited"}},
"select_vlan": {
"type": {"key": {"type": "integer",
"minInteger": 0,
@@ -134,7 +137,9 @@
"min": 0, "max": 4096}},
"output_port": {
"type": {"key": {"type": "uuid",
- "refTable": "Port"}, "min": 0, "max": 1}},
+ "refTable": "Port",
+ "refType": "weak"},
+ "min": 0, "max": 1}},
"output_vlan": {
"type": {"key": {"type": "integer",
"minInteger": 1,
--
1.6.6.1
More information about the dev
mailing list