ovsdb: Implement table uniqueness constraints ("indexes").
[sliver-openvswitch.git] / python / ovs / db / schema.py
index 9883adc..9c453df 100644 (file)
@@ -134,14 +134,33 @@ class IdlSchema(DbSchema):
         return IdlSchema(schema.name, schema.version, schema.tables,
                          idlPrefix, idlHeader)
 
+def column_set_from_json(json, columns):
+    if json is None:
+        return tuple(columns)
+    elif type(json) != list:
+        raise error.Error("array of distinct column names expected", json)
+    else:
+        for column_name in json:
+            if type(column_name) not in [str, unicode]:
+                raise error.Error("array of distinct column names expected",
+                                  json)
+            elif column_name not in columns:
+                raise error.Error("%s is not a valid column name"
+                                  % column_name, json)
+        if len(set(json)) != len(json):
+            # Duplicate.
+            raise error.Error("array of distinct column names expected", json)
+        return tuple([columns[column_name] for column_name in json])
+
 class TableSchema(object):
     def __init__(self, name, columns, mutable=True, max_rows=sys.maxint,
-                 is_root=True):
+                 is_root=True, indexes=[]):
         self.name = name
         self.columns = columns
         self.mutable = mutable
         self.max_rows = max_rows
         self.is_root = is_root
+        self.indexes = indexes
 
     @staticmethod
     def from_json(json, name):
@@ -150,6 +169,7 @@ class TableSchema(object):
         mutable = parser.get_optional("mutable", [bool], True)
         max_rows = parser.get_optional("maxRows", [int])
         is_root = parser.get_optional("isRoot", [bool], False)
+        indexes_json = parser.get_optional("indexes", [list], [])
         parser.finish()
 
         if max_rows == None:
@@ -170,7 +190,20 @@ class TableSchema(object):
             columns[columnName] = ColumnSchema.from_json(columnJson,
                                                          columnName)
 
-        return TableSchema(name, columns, mutable, max_rows, is_root)
+        indexes = []
+        for index_json in indexes_json:
+            index = column_set_from_json(index_json, columns)
+            if not index:
+                raise error.Error("index must have at least one column", json)
+            elif len(index) == 1:
+                index[0].unique = True
+            for column in index:
+                if not column.persistent:
+                    raise error.Error("ephemeral columns (such as %s) may "
+                                      "not be indexed" % column.name, json)
+            indexes.append(index)
+
+        return TableSchema(name, columns, mutable, max_rows, is_root, indexes)
 
     def to_json(self, default_is_root=False):
         """Returns this table schema serialized into JSON.
@@ -198,6 +231,11 @@ class TableSchema(object):
         if self.max_rows != sys.maxint:
             json["maxRows"] = self.max_rows
 
+        if self.indexes:
+            json["indexes"] = []
+            for index in self.indexes:
+                json["indexes"].append([column.name for column in index])
+
         return json
 
 class ColumnSchema(object):
@@ -206,6 +244,7 @@ class ColumnSchema(object):
         self.mutable = mutable
         self.persistent = persistent
         self.type = type
+        self.unique = False
 
     @staticmethod
     def from_json(json, name):