X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=python%2Fovs%2Fdb%2Fschema.py;h=1b5a771f771538a75f3d78b75e7bd8a56b3349c6;hb=e0edde6fee279cdbbf3c179f5f50adaf0c7c7f1e;hp=e0e0daf5ec60b7bd093c4da8247676cbeadf2fec;hpb=c5f341ab193b9126dffef8c77bf8ed35e91290fd;p=sliver-openvswitch.git diff --git a/python/ovs/db/schema.py b/python/ovs/db/schema.py index e0e0daf5e..1b5a771f7 100644 --- a/python/ovs/db/schema.py +++ b/python/ovs/db/schema.py @@ -1,4 +1,4 @@ -# Copyright (c) 2009, 2010, 2011 Nicira Networks +# Copyright (c) 2009, 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,14 @@ from ovs.db import error import ovs.db.parser from ovs.db import types + +def _check_id(name, json): + if name.startswith('_'): + raise error.Error('names beginning with "_" are reserved', json) + elif not ovs.db.parser.is_identifier(name): + raise error.Error("name must be a valid id", json) + + class DbSchema(object): """Schema for an OVSDB database.""" @@ -27,13 +35,6 @@ class DbSchema(object): self.version = version self.tables = tables - # Validate that all ref_tables refer to the names of tables - # that exist. - for table in self.tables.itervalues(): - for column in table.columns.itervalues(): - self.__check_ref_table(column, column.type.key, "key") - self.__check_ref_table(column, column.type.value, "value") - # "isRoot" was not part of the original schema definition. Before it # was added, there was no support for garbage collection. So, for # backward compatibility, if the root set is empty then assume that @@ -42,6 +43,16 @@ class DbSchema(object): for table in self.tables.itervalues(): table.is_root = True + # Find the "ref_table"s referenced by "ref_table_name"s. + # + # Also force certain columns to be persistent, as explained in + # __check_ref_table(). This requires 'is_root' to be known, so this + # must follow the loop updating 'is_root' above. + for table in self.tables.itervalues(): + for column in table.columns.itervalues(): + self.__follow_ref_table(column, column.type.key, "key") + self.__follow_ref_table(column, column.type.value, "value") + def __root_set_size(self): """Returns the number of tables in the schema's root set.""" n_root = 0 @@ -54,23 +65,19 @@ class DbSchema(object): def from_json(json): parser = ovs.db.parser.Parser(json, "database schema") name = parser.get("name", ['id']) - version = parser.get_optional("version", [unicode]) - parser.get_optional("cksum", [unicode]) + version = parser.get_optional("version", [str, unicode]) + parser.get_optional("cksum", [str, unicode]) tablesJson = parser.get("tables", [dict]) parser.finish() if (version is not None and not re.match('[0-9]+\.[0-9]+\.[0-9]+$', version)): - raise error.Error("schema version \"%s\" not in format x.y.z" + raise error.Error('schema version "%s" not in format x.y.z' % version) tables = {} for tableName, tableJson in tablesJson.iteritems(): - if tableName.startswith('_'): - raise error.Error("names beginning with \"_\" are reserved", - json) - elif not ovs.db.parser.is_identifier(tableName): - raise error.Error("name must be a valid id", json) + _check_id(tableName, json) tables[tableName] = TableSchema.from_json(tableJson, tableName) return DbSchema(name, version, tables) @@ -90,13 +97,30 @@ class DbSchema(object): json["version"] = self.version return json - def __check_ref_table(self, column, base, base_name): - if (base and base.type == types.UuidType and base.ref_table and - base.ref_table not in self.tables): + def copy(self): + return DbSchema.from_json(self.to_json()) + + def __follow_ref_table(self, column, base, base_name): + if not base or base.type != types.UuidType or not base.ref_table_name: + return + + base.ref_table = self.tables.get(base.ref_table_name) + if not base.ref_table: raise error.Error("column %s %s refers to undefined table %s" - % (column.name, base_name, base.ref_table), + % (column.name, base_name, base.ref_table_name), tag="syntax error") + if base.is_strong_ref() and not base.ref_table.is_root: + # We cannot allow a strong reference to a non-root table to be + # ephemeral: if it is the only reference to a row, then replaying + # the database log from disk will cause the referenced row to be + # deleted, even though it did exist in memory. If there are + # references to that row later in the log (to modify it, to delete + # it, or just to point to it), then this will yield a transaction + # error. + column.persistent = True + + class IdlSchema(DbSchema): def __init__(self, name, version, tables, idlPrefix, idlHeader): DbSchema.__init__(self, name, version, tables) @@ -106,8 +130,8 @@ class IdlSchema(DbSchema): @staticmethod def from_json(json): parser = ovs.db.parser.Parser(json, "IDL schema") - idlPrefix = parser.get("idlPrefix", [unicode]) - idlHeader = parser.get("idlHeader", [unicode]) + idlPrefix = parser.get("idlPrefix", [str, unicode]) + idlHeader = parser.get("idlHeader", [str, unicode]) subjson = dict(json) del subjson["idlPrefix"] @@ -117,22 +141,44 @@ 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): parser = ovs.db.parser.Parser(json, "table schema for table %s" % name) - columnsJson = parser.get("columns", [dict]) + columns_json = parser.get("columns", [dict]) 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: @@ -140,20 +186,29 @@ class TableSchema(object): elif max_rows <= 0: raise error.Error("maxRows must be at least 1", json) - if not columnsJson: + if not columns_json: raise error.Error("table must have at least one column", json) columns = {} - for columnName, columnJson in columnsJson.iteritems(): - if columnName.startswith('_'): - raise error.Error("names beginning with \"_\" are reserved", - json) - elif not ovs.db.parser.is_identifier(columnName): - raise error.Error("name must be a valid id", json) - columns[columnName] = ColumnSchema.from_json(columnJson, - columnName) - - return TableSchema(name, columns, mutable, max_rows, is_root) + for column_name, column_json in columns_json.iteritems(): + _check_id(column_name, json) + columns[column_name] = ColumnSchema.from_json(column_json, + column_name) + + 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. @@ -181,24 +236,31 @@ 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): - def __init__(self, name, mutable, persistent, type): + def __init__(self, name, mutable, persistent, type_): self.name = name self.mutable = mutable self.persistent = persistent - self.type = type + self.type = type_ + self.unique = False @staticmethod def from_json(json, name): parser = ovs.db.parser.Parser(json, "schema for column %s" % name) mutable = parser.get_optional("mutable", [bool], True) ephemeral = parser.get_optional("ephemeral", [bool], False) - type = types.Type.from_json(parser.get("type", [dict, unicode])) + type_ = types.Type.from_json(parser.get("type", [dict, str, unicode])) parser.finish() - return ColumnSchema(name, mutable, not ephemeral, type) + return ColumnSchema(name, mutable, not ephemeral, type_) def to_json(self): json = {"type": self.type.to_json()} @@ -207,4 +269,3 @@ class ColumnSchema(object): if not self.persistent: json["ephemeral"] = True return json -