A synthetic column is one that is not present in the actual database but
instead calculated by code in the client based on columns in the row.  This
can be useful to avoid repeatedly calculating the same function of a row.

Signed-off-by: Ben Pfaff <b...@ovn.org>
---
 lib/ovsdb-idl-provider.h |  1 +
 lib/ovsdb-idl.c          | 14 ++++++--
 ovsdb/ovsdb-idlc.1       | 31 ++++++++++++++++-
 ovsdb/ovsdb-idlc.in      | 89 +++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 127 insertions(+), 8 deletions(-)

diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
index b0ebed44f83a..4268dc54c659 100644
--- a/lib/ovsdb-idl-provider.h
+++ b/lib/ovsdb-idl-provider.h
@@ -94,6 +94,7 @@ struct ovsdb_idl_column {
     char *name;
     struct ovsdb_type type;
     bool is_mutable;
+    bool is_synthetic;
     void (*parse)(struct ovsdb_idl_row *, const struct ovsdb_datum *);
     void (*unparse)(struct ovsdb_idl_row *);
 };
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index 29f893116aee..b027f8cad35d 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -1560,9 +1560,16 @@ ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl,
         columns = table->need_table ? json_array_create_empty() : NULL;
         for (j = 0; j < tc->n_columns; j++) {
             const struct ovsdb_idl_column *column = &tc->columns[j];
-            if (table->modes[j] & OVSDB_IDL_MONITOR) {
-                if (table_schema
-                    && !sset_contains(table_schema, column->name)) {
+            bool db_has_column = (table_schema &&
+                                  sset_contains(table_schema, column->name));
+            if (column->is_synthetic) {
+                if (db_has_column) {
+                    VLOG_WARN("%s table in %s database has synthetic "
+                              "column %s", table->class_->name,
+                              idl->class_->database, column->name);
+                }
+            } else if (table->modes[j] & OVSDB_IDL_MONITOR) {
+                if (table_schema && !db_has_column) {
                     VLOG_WARN("%s table in %s database lacks %s column "
                               "(database needs upgrade?)",
                               table->class_->name, idl->class_->database,
@@ -3815,6 +3822,7 @@ ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_,
     size_t column_idx;
     bool write_only;
 
+    ovs_assert(!column->is_synthetic);
     if (ovsdb_idl_row_is_synthetic(row)) {
         goto discard_datum;
     }
diff --git a/ovsdb/ovsdb-idlc.1 b/ovsdb/ovsdb-idlc.1
index f1659b4a63b7..cc78b66c40e0 100644
--- a/ovsdb/ovsdb-idlc.1
+++ b/ovsdb/ovsdb-idlc.1
@@ -55,8 +55,37 @@ after the \fB#include\fR directives in those files.
 This member is optional.  If specified, it is an object whose contents
 describes extensions to the OVSDB schema language, for the purpose of
 specifying interpretation by the IDL.
-No extensions are supported yet.
 .
+.IP "\fB""\fBsynthetic\fR"" member of <column-schema> ""\fBextensions\fR"" 
object"
+If this optional member is set to \fBtrue\fR, then it indicates that
+the column is not expected to be found in the actual database.
+Instead, code supplied by the IDL's client fills in the desired
+structure members based on the value of one or more other database
+columns.  This can be used to cache the result of a calculation, for
+example.
+.
+.IP "\fB""\fBparse\fR"" member of <column-schema> ""\fBextensions\fR"" object"
+This member should be present if and only if the column is synthetic.
+It should be a string that contains C code to set the value of the
+column's member in an object named \fBrow\fR, e.g. "\fBrow->column =
+1;" if the column's name is \fBcolumn\fR and has integer type.  The
+code may rely on the columns named in \fBdependencies\fR to be
+initialized.  The function can get called for rows that do not satisfy
+the constraints in the schema, e.g. that a pointer to another is
+nonnull, so it must not rely on those constraints.
+.
+.IP "\fB""\fBunparse\fR"" member of <column-schema> ""\fBextensions\fR"" 
object"
+This member is honored only if the column is synthetic.  It should be
+a string that contains C code to free the data in the column's member
+in an object named \fBrow\fR, e.g. "\fBfree(row->column);" if the
+column's name is \fBcolumn\fR and points to data that was allocated by
+the \fBparse\fR function and needs to be freed.
+.
+.IP "\fB""\fBdependencies\fR"" member of <column-schema> ""\fBextensions\fR"" 
object"
+This member should be a list of the names of columns whose values are
+used by the code in \fBparse\fR and \fBunparse\fR.  The IDL ensures
+that dependencies are parsed before the columns that depends on them,
+and vice versa for unparsing.
 .SS "Commands"
 .IP "\fBannotate\fI schema annotations\fR"
 Reads \fIschema\fR, which should be a file in JSON format (ordinarily
diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
index 95bb9f4ca9e0..1ea2115c0e74 100755
--- a/ovsdb/ovsdb-idlc.in
+++ b/ovsdb/ovsdb-idlc.in
@@ -119,8 +119,42 @@ def cMembers(prefix, tableName, columnName, column, const, 
refTable=True):
 
     return (comment, members)
 
+# This is equivalent to sorted(table.columns.items()), except that the
+# sorting includes a topological component: if column B has a
+# dependency on column A, then A will be sorted before B.
 def sorted_columns(table):
-    return sorted(table.columns.items())
+    input = []
+    for name, column in table.columns.items():
+        dependencies = column.extensions.get('dependencies', [])
+        for d in dependencies:
+            if d not in table.columns:
+                sys.stderr.write("Table %s column %s depends on column %s "
+                                 "but there is no such column\n"
+                                 % (table.name, name, d))
+                sys.exit(1)
+        input += [(name, column, set(dependencies))]
+
+    output = []
+    satisfied_dependencies = set()
+    while input:
+        done = []
+        next = []
+        for name, column, dependencies in input:
+            if dependencies <= satisfied_dependencies:
+                done += [(name, column)]
+            else:
+                next += [(name, column, dependencies)]
+
+        if not done:
+            sys.stderr.write("Table %s columns have circular dependencies\n"
+                             % table.name)
+            sys.exit(1)
+
+        for name, column in done:
+            satisfied_dependencies.add(name)
+        output += sorted(done)
+        input = next
+    return output
 
 # If a column name in the schema is a C or C++ keyword, append an underscore
 # to the column name.
@@ -179,6 +213,9 @@ extern "C" {
         print("\tstruct ovsdb_idl_row header_;")
         for columnName, column in sorted_columns(table):
             print("\n\t/* %s column. */" % columnName)
+            if column.extensions.get("members"):
+                print("\t%s" % column.extensions["members"])
+                continue
             comment, members = cMembers(prefix, tableName,
                                         columnName, column, False)
             for member in members:
@@ -261,10 +298,14 @@ bool %(s)s_is_updated(const struct %(s)s *, enum 
%(s)s_column_id);
 ''' % {'s': structName, 'S': structName.upper()})
 
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             print('void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': 
structName, 'c': columnName})
 
         print("")
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             if column.type.value:
                 valueParam = ', enum ovsdb_atomic_type value_type'
             else:
@@ -274,6 +315,8 @@ bool %(s)s_is_updated(const struct %(s)s *, enum 
%(s)s_column_id);
 
         print("")
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             print('void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': 
structName, 'c': columnName}, end=' ')
             if column.type.is_smap():
                 args = ['const struct smap *']
@@ -285,6 +328,8 @@ bool %(s)s_is_updated(const struct %(s)s *, enum 
%(s)s_column_id);
 
         print("")
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             if column.type.is_map():
                 print('void %(s)s_update_%(c)s_setkey(const struct %(s)s *, ' 
% {'s': structName, 'c': columnName}, end=' ')
                 print('%(coltype)s, %(valtype)s);' % 
{'coltype':column.type.key.to_const_c_type(prefix), 
'valtype':column.type.value.to_const_c_type(prefix)})
@@ -391,6 +436,21 @@ static struct %(s)s *
 
         # Parse functions.
         for columnName, column in sorted_columns(table):
+            if 'parse' in column.extensions:
+                print('''
+static void
+%(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum 
OVS_UNUSED)
+{
+    struct %(s)s *row = %(s)s_cast(row_);\
+''' % {'s': structName, 'c': columnName})
+                print(column.extensions["parse"])
+                print("}")
+                continue
+            if column.extensions.get('synthetic'):
+                # Synthetic columns aren't parsed from a datum.
+                unused = " OVS_UNUSED"
+            else:
+                unused = ""
             print('''
 static void
 %(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
@@ -398,6 +458,10 @@ static void
     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
                                                 'c': columnName})
             type = column.type
+            if 'parse' in column.extensions:
+                print(column.extensions["parse"])
+                print("}")
+                continue
             if type.value:
                 keyVar = "row->key_%s" % columnName
                 valueVar = "row->value_%s" % columnName
@@ -479,15 +543,16 @@ static void
         # Unparse functions.
         for columnName, column in sorted_columns(table):
             type = column.type
-            if type.is_smap() or (type.n_min != 1 or type.n_max != 1) and not 
type.is_optional_pointer():
+            if (type.is_smap() or (type.n_min != 1 or type.n_max != 1) and not 
type.is_optional_pointer()) or 'unparse' in column.extensions:
                 print('''
 static void
 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
 {
     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
                                                 'c': columnName})
-
-                if type.is_smap():
+                if 'unparse' in column.extensions:
+                    print(column.extensions["unparse"])
+                elif type.is_smap():
                     print("    smap_destroy(&row->%s);" % columnName)
                 else:
                     if type.value:
@@ -628,6 +693,8 @@ bool
 
         # Verify functions.
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             print('''
 /* Causes the original contents of column "%(c)s" in 'row' to be
  * verified as a prerequisite to completing the transaction.  That is, if
@@ -663,6 +730,8 @@ void
 
         # Get functions.
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             if column.type.value:
                 valueParam = ',\n\tenum ovsdb_atomic_type value_type 
OVS_UNUSED'
                 valueType = '\n    ovs_assert(value_type == %s);' % 
column.type.value.toAtomicType()
@@ -703,6 +772,8 @@ const struct ovsdb_datum *
 
         # Set functions.
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             type = column.type
 
             comment, members = cMembers(prefix, tableName, columnName,
@@ -817,6 +888,8 @@ void
             print("}")
         # Update/Delete of partial map column functions
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             type = column.type
             if type.is_map():
                 print('''
@@ -926,6 +999,8 @@ void
 
         # Add clause functions.
         for columnName, column in sorted_columns(table):
+            if column.extensions.get('synthetic'):
+                continue
             type = column.type
 
             comment, members = cMembers(prefix, tableName, columnName,
@@ -1299,6 +1374,10 @@ void
                 mutable = "true"
             else:
                 mutable = "false"
+            if column.extensions.get("synthetic"):
+                synthetic = "true"
+            else:
+                synthetic = "false"
             type_init = '\n'.join("            " + x
                                   for x in column.type.cInitType("%s_col_%s" % 
(tableName, columnName), prereqs))
             print("""\
@@ -1308,6 +1387,7 @@ void
 %(type)s
          },
          .is_mutable = %(mutable)s,
+         .is_synthetic = %(synthetic)s,
          .parse = %(s)s_parse_%(c)s,
          .unparse = %(s)s_unparse_%(c)s,
     },\n""" % {'P': prefix.upper(),
@@ -1316,6 +1396,7 @@ void
                'C': columnName.upper(),
                's': structName,
                'mutable': mutable,
+               'synthetic': synthetic,
                'type': type_init,
                'column_name_in_schema': column.name})
         print("};")
-- 
2.16.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to