[ovs-dev] [PATCH v7 4/7] ovsdb-idl: Autogenerated functions for compound indexes

Lance Richardson lrichard at redhat.com
Fri Jul 14 19:15:46 UTC 2017


Generates and fills in the default comparators for columns with
type int, real, string. Also creates the macros that allow
iteration over the contents of the index, and perform
queries.

Signed-off-by: Arnoldo Lutz Guevara <arnoldo.lutz.guevara at hpe.com>
Signed-off-by: Esteban Rodriguez Betancourt <estebarb at hpe.com>
Co-authored-by: Arnoldo Lutz Guevara <arnoldo.lutz.guevara at hpe.com>
Co-authored-by: Esteban Rodriguez Betancourt <estebarb at hpe.com>
Signed-off-by: Lance Richardson <lrichard at redhat.com>
---
  v7: - Rebased and made needed changes in ovsdb-idlc.in for Python 3
        compatibility.
      - Added a test case for *_FOR_EACH_RANGE (row, cursor, NULL, NULL)
        (from=NULL is interpreted as -infinity, to=NULL is interpreted
         as +infinity).
      - Replaced assert() with ovs_assert() in test-ovsdb.c (this included
        a few that were unrelated to this patch, mea culpa).
  v6: - No longer need to dynamically allocate strings passed to
        ovsdb_idl_index_write().
  v5: - Coding style fixes, including in generated idl code.
      - Fixed memory leak of xmalloc()-ed struct ovsdb_datum in generated code.
      - Changed tests to dynamically allocate string values when building a row
        to be matched; otherwise the ovsdb_datum_destroy() call added in this
        version of the patch series attempts to free a string literal.

 ovsdb/ovsdb-idlc.in | 258 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/ovsdb-idl.at  | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-ovsdb.c  | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 821 insertions(+), 5 deletions(-)

diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
index 7cbcbf5ab..f065ef1c6 100755
--- a/ovsdb/ovsdb-idlc.in
+++ b/ovsdb/ovsdb-idlc.in
@@ -9,6 +9,7 @@ import sys
 import ovs.json
 import ovs.db.error
 import ovs.db.schema
+from ovs.db.types import StringType, IntegerType, RealType
 
 argv0 = sys.argv[0]
 
@@ -202,6 +203,26 @@ static inline bool %(s)s_is_deleted(const struct %(s)s *row)
     return %(s)s_row_get_seqno(row, OVSDB_IDL_CHANGE_DELETE) > 0;
 }
 
+void %(s)s_index_destroy_row(const struct %(s)s *);
+int %(s)s_index_compare(struct ovsdb_idl_index_cursor *, const struct %(s)s *, const struct %(s)s *);
+const struct %(s)s *%(s)s_index_first(struct ovsdb_idl_index_cursor *);
+const struct %(s)s *%(s)s_index_next(struct ovsdb_idl_index_cursor *);
+const struct %(s)s *%(s)s_index_find(struct ovsdb_idl_index_cursor *, const struct %(s)s *);
+const struct %(s)s *%(s)s_index_forward_to(struct ovsdb_idl_index_cursor *, const struct %(s)s *);
+const struct %(s)s *%(s)s_index_get_data(const struct ovsdb_idl_index_cursor *);
+#define %(S)s_FOR_EACH_RANGE(ROW, CURSOR, FROM, TO) \\
+        for ((ROW) = %(s)s_index_forward_to(CURSOR, FROM); \\
+             ((ROW) && %(s)s_index_compare(CURSOR, ROW, TO) <= 0); \\
+             (ROW) = %(s)s_index_next(CURSOR))
+#define %(S)s_FOR_EACH_EQUAL(ROW, CURSOR, KEY) \\
+        for ((ROW) = %(s)s_index_find(CURSOR, KEY); \\
+             ((ROW) && %(s)s_index_compare(CURSOR, ROW, KEY) == 0); \\
+             (ROW) = %(s)s_index_next(CURSOR))
+#define %(S)s_FOR_EACH_BYINDEX(ROW, CURSOR) \\
+        for ((ROW) = %(s)s_index_first(CURSOR); \\
+             (ROW); \\
+             (ROW) = %(s)s_index_next(CURSOR))
+
 void %(s)s_init(struct %(s)s *);
 void %(s)s_delete(const struct %(s)s *);
 struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
@@ -258,6 +279,19 @@ bool %(s)s_is_updated(const struct %(s)s *, enum %(s)s_column_id);
         print("")
 
     # Table indexes.
+        print("struct %(s)s * %(s)s_index_init_row(struct ovsdb_idl *, const struct ovsdb_idl_table_class *);" % {'s': structName})
+        print
+        for columnName, column in sorted(table.columns.iteritems()):
+            print('void %(s)s_index_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName})
+            if column.type.is_smap():
+                args = ['const struct smap *']
+            else:
+                comment, members = cMembers(prefix, tableName, columnName,
+                                            column, True)
+                args = ['%(type)s%(name)s' % member for member in members]
+            print('%s);' % ', '.join(args))
+
+        print
     printEnum("%stable_id" % prefix.lower(), ["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
     print("")
     for tableName in schema.tables:
@@ -980,6 +1014,230 @@ void
                 print("    free(%s);" % var)
             print("}")
 
+# Index table related functions
+        print("""
+/* Destroy 'row' of kind "%(t)s". The row must have been
+ * created with ovsdb_idl_index_init_row.
+ */
+void
+%(s)s_index_destroy_row(const struct %(s)s *row)
+{
+    ovsdb_idl_index_destroy_row__(&row->header_);
+}
+        """ % { 's' : structName, 't': tableName })
+        print("""
+/* Creates a new row of kind "%(t)s". */
+struct %(s)s *
+%(s)s_index_init_row(struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *class)
+{""" % {'s': structName, 't': tableName})
+        #for columnName, column in sorted(table.columns.iteritems()):
+        #    if column.type.is_smap():
+        #        print "    smap_init(&row->%s);" % columnName
+        print("    return (struct %(s)s *) ovsdb_idl_index_init_row(idl, class);" % {'s': structName, 't': tableName})
+        print("}")
+
+        print("""
+/*  This function is used to compare "%(s)s" records on table in iteration loops for compound-index operations.
+    After been called, cursor point to current position in the index
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+                const struct "%(s)s" *const_data1,  const struct "%(s)s" *const_data2. Data to be compared.
+    Return value: 0 if both data values are equal, -1 if first parameter is less than second and 1 otherwise. */""" % {'s' : structName})
+        print('int')
+        print("""%(s)s_index_compare(struct ovsdb_idl_index_cursor *cursor, const struct %(s)s *const_data1,  const struct %(s)s *const_data2)
+{
+    struct %(s)s *data1 = CONST_CAST(struct %(s)s *, const_data1);
+    struct %(s)s *data2 = CONST_CAST(struct %(s)s *, const_data2);
+    return ovsdb_idl_index_compare(cursor, &data1->header_, &data2->header_);
+}""" % { 's' : structName })
+        print("""
+/*  This function is called to position the cursor at the first row in "%(s)s" table on the associated compound-index.
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+    Return value: The first row in the corresponding index. */""" %  {'s' : structName })
+        print("""const struct %(s)s *\n%(s)s_index_first(struct ovsdb_idl_index_cursor *cursor)
+{
+    return %(s)s_cast(ovsdb_idl_index_first(cursor));
+}""" % { 's' : structName })
+        print("""
+/*  This function is called to position the cursor at the next row in "%(s)s" table on the associated compound-index.
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+    Return value: The next row in the corresponding index. */""" %  {'s' : structName, 'c' : columnName })
+        print("""const struct %(s)s *\n%(s)s_index_next(struct ovsdb_idl_index_cursor *cursor)
+{
+    return %(s)s_cast(ovsdb_idl_index_next(cursor));
+}""" % { 's' : structName })
+        print("""
+/*  This function is used to find the data of the row in "%(s)s" table that meet criteria with the requested data
+    associated in the compound-index.
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+                const struct %(s)s *const_data. Data to be searched.
+    Return value: The row in the corresponding index if found or NULL otherwise. */""" %  {'s' : structName })
+        print("""const struct %(s)s *\n%(s)s_index_find(struct ovsdb_idl_index_cursor *cursor, const struct %(s)s *const_data)
+{
+    struct %(s)s *data = CONST_CAST(struct %(s)s *, const_data);
+    return %(s)s_cast(ovsdb_idl_index_find(cursor, &data->header_));
+}""" % { 's' : structName })
+        print("""
+/*  This function is used to set the cursor pointing to the row in "%(s)s" table that meet criteria of the requested data
+    associated in the compound-index.
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+                const struct %(s)s *const_data. Data to be searched.
+    Return value: The row in the corresponding index closest to the criteria. */""" %  {'s' : structName })
+        print("""const struct %(s)s *\n%(s)s_index_forward_to(struct ovsdb_idl_index_cursor *cursor, const struct %(s)s *const_data)
+{
+    struct %(s)s *data = CONST_CAST(struct %(s)s *, const_data);
+    return %(s)s_cast(ovsdb_idl_index_forward_to(cursor, &data->header_));
+}""" % { 's' : structName })
+        print("""
+/*  This function is used to get the data of the row in the current position pointed by the cursor in
+    "%(s)s" table.
+    Parameters: struct ovsdb_idl_index_cursor *cursor. Cursor used to iterate over the indexed data on this table.
+    Return value: The row in the corresponding index if found or NULL otherwise. */""" %  {'s' : structName, 'c' : columnName })
+        print("""const struct %(s)s *\n%(s)s_index_get_data(const struct ovsdb_idl_index_cursor *cursor)
+{
+    return %(s)s_cast(ovsdb_idl_index_data(CONST_CAST(struct ovsdb_idl_index_cursor *, cursor)));
+}""" % { 's' : structName })
+        # Indexes Set functions
+        for columnName, column in sorted(table.columns.iteritems()):
+            type = column.type
+
+            comment, members = cMembers(prefix, tableName, columnName,
+                                        column, True)
+
+            if type.is_smap():
+                print(comment)
+                print("""void
+%(s)s_index_set_%(c)s(const struct %(s)s *row, const struct smap *%(c)s)
+{
+    struct ovsdb_datum *datum = xmalloc(sizeof(struct ovsdb_datum));
+
+    if (%(c)s) {
+        struct smap_node *node;
+        size_t i;
+
+        datum->n = smap_count(%(c)s);
+        datum->keys = xmalloc(datum->n * sizeof *datum->keys);
+        datum->values = xmalloc(datum->n * sizeof *datum->values);
+
+        i = 0;
+        SMAP_FOR_EACH (node, %(c)s) {
+            datum->keys[i].string = node->key;
+            datum->values[i].string = node->value;
+            i++;
+        }
+        ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
+    } else {
+        ovsdb_datum_init_empty(datum);
+    }
+    ovsdb_idl_index_write_(CONST_CAST(struct ovsdb_idl_row *, &row->header_),
+                           &%(s)s_columns[%(S)s_COL_%(C)s],
+                           datum,
+                           &%(p)stable_classes[%(P)sTABLE_%(T)s]);
+}
+""" % {'t': tableName,
+       'p': prefix,
+       'P': prefix.upper(),
+       's': structName,
+       'S': structName.upper(),
+       'c': columnName,
+       'C': columnName.upper(),
+       't': tableName,
+       'T': tableName.upper()})
+                continue
+
+            keyVar = members[0]['name']
+            nVar = None
+            valueVar = None
+            if type.value:
+                valueVar = members[1]['name']
+                if len(members) > 2:
+                    nVar = members[2]['name']
+            else:
+                if len(members) > 1:
+                    nVar = members[1]['name']
+
+            print(comment)
+            print('void')
+            print('%(s)s_index_set_%(c)s(const struct %(s)s *row, %(args)s)' % \
+                {'s': structName, 'c': columnName,
+                 'args': ', '.join(['%(type)s%(name)s' % m for m in members])})
+            print("{")
+            print("    struct ovsdb_datum datum;")
+            if type.n_min == 1 and type.n_max == 1:
+                print("    union ovsdb_atom *key = xmalloc(sizeof(union ovsdb_atom));")
+                if type.value:
+                    print("    union ovsdb_atom *value = xmalloc(sizeof(union ovsdb_atom));")
+                print()
+                print("    datum.n = 1;")
+                print("    datum.keys = key;")
+                print("    " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar))
+                if type.value:
+                    print("    datum.values = value;")
+                    print("    "+ type.value.assign_c_value_casting_away_const("value->%s" % type.value.type.to_string(), valueVar))
+                else:
+                    print("    datum.values = NULL;")
+                txn_write_func = "ovsdb_idl_index_write_"
+            elif type.is_optional_pointer():
+                print("    union ovsdb_atom *key = xmalloc(sizeof (union ovsdb_atom));")
+                print()
+                print("    if (%s) {" % keyVar)
+                print("        datum.n = 1;")
+                print("        datum.keys = key;")
+                print("        " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), keyVar))
+                print("    } else {")
+                print("        datum.n = 0;")
+                print("        datum.keys = NULL;")
+                print("    }")
+                print("    datum.values = NULL;")
+                txn_write_func = "ovsdb_idl_index_write_"
+            elif type.n_max == 1:
+                print("    union ovsdb_atom *key = xmalloc(sizeof(union ovsdb_atom));")
+                print()
+                print("    if (%s) {" % nVar)
+                print("        datum.n = 1;")
+                print("        datum.keys = key;")
+                print("        " + type.key.assign_c_value_casting_away_const("key->%s" % type.key.type.to_string(), "*" + keyVar))
+                print("    } else {")
+                print("        datum.n = 0;")
+                print("        datum.keys = NULL;")
+                print("    }")
+                print("    datum.values = NULL;")
+                txn_write_func = "ovsdb_idl_index_write_"
+            else:
+                print("    size_t i;")
+                print()
+                print("    datum.n = %s;" % nVar)
+                print("    datum.keys = %s ? xmalloc(%s * sizeof *datum.keys) : NULL;" % (nVar, nVar))
+                if type.value:
+                    print("    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar)
+                else:
+                    print("    datum.values = NULL;")
+                print("    for (i = 0; i < %s; i++) {" % nVar)
+                print("        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar))
+                if type.value:
+                    print("        " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar))
+                print("    }")
+                if type.value:
+                    valueType = type.value.toAtomicType()
+                else:
+                    valueType = "OVSDB_TYPE_VOID"
+                print("    ovsdb_datum_sort_unique(&datum, %s, %s);" % (
+                    type.key.toAtomicType(), valueType))
+                txn_write_func = "ovsdb_idl_index_write_"
+            print("    %(f)s(CONST_CAST(struct ovsdb_idl_row *, &row->header_), &%(s)s_columns[ %(S)s_COL_%(C)s ], &datum, &%(p)stable_classes[%(P)sTABLE_%(T)s]);" \
+                % {'f': txn_write_func,
+                   's': structName,
+                   'S': structName.upper(),
+                   'C': columnName.upper(),
+                   'p': prefix,
+                   'P': prefix.upper(),
+                   't': tableName,
+                   'T': tableName.upper()})
+            print("}")
+# End Index table related functions
+
+        # Table columns.
+        print("\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
+            structName, structName.upper()))
         print("""
 void
 %(s)s_set_condition(struct ovsdb_idl *idl, struct ovsdb_idl_condition *condition)
diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at
index 4eaf87f86..920feb471 100644
--- a/tests/ovsdb-idl.at
+++ b/tests/ovsdb-idl.at
@@ -1301,3 +1301,285 @@ OVSDB_CHECK_IDL_NOTIFY([simple idl verify notify],
 014: i=1 r=123.5 b=false s=mystring u=<3> ia=[1 2 3] ra=[-0.5] ba=[true] sa=[abc def] ua=[<4> <5>] uuid=<0>
 015: done
 ]])
+
+# Tests to verify the functionality of the one column compound index.
+# It tests index for one column string and integer indexes.
+# The run of test-ovsdb generates the output of the display of data using the different indexes defined in
+# the program.
+# Then, some at_checks are used to verify the correctness of the corresponding index as well as the existence
+# of all the rows involved in the test.
+m4_define([OVSDB_CHECK_IDL_COMPOUND_INDEX_SINGLE_COLUMN_C],
+  [AT_SETUP([$1 - C])
+   AT_KEYWORDS([ovsdb server idl compound_index_single_column compound_index positive $5])
+   AT_CHECK([ovsdb_start_idltest])
+   m4_if([$2], [], [],
+     [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
+# Generate the data to be tested.
+   AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-compound-index unix:socket $3],
+            [0], [stdout], [ignore], [kill `cat pid`])
+# Filter the rows of data that corresponds to the string index eliminating the extra columns of data.
+# This is done to verifiy that the output data is in the correct and expected order.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sed -e 's/ i=.*//g']],
+            [0], [$4], [], [kill `cat pid`])
+# Here, the data is filtered and sorted in order to have all the rows in the index and be
+# able to determined that all the involved rows are present.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sort -k 1,1n -k 2,2 -k 3,3]],
+            [0], [$5], [], [kill `cat pid`])
+# Filter the rows of data that corresponds to the integer index eliminating the extra columns of data.
+# This is done to verifiy that the output data is in the correct and expected order.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sed -e 's/ s=.*//g']],
+            [0], [$6], [], [kill `cat pid`])
+# Here again, the data is filtered and sorted in order to have all the rows in the index and be
+# able to determined that all the involved rows are present.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sort -k 1,1n -k 2,2 -k 3,3]],
+            [0], [$7], [], [kill `cat pid`])
+   OVSDB_SERVER_SHUTDOWN
+   AT_CLEANUP])
+
+OVSDB_CHECK_IDL_COMPOUND_INDEX_SINGLE_COLUMN_C([Compound_index, single column test ],
+    [['["idltest",
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 1, "b":true, "r":101.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 2, "b":false, "r":102.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 10, "b":true, "r":110.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 1, "b":false, "r":110.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":120.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":122.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 4, "b":true, "r":130.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List005", "i": 5, "b":true, "r":130.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 20, "b":true, "r":220.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 19, "b":true, "r":219.0}}
+      ]']],
+    [idl_compound_index_single_column],
+    [001: s=List000
+001: s=List000
+001: s=List000
+001: s=List001
+001: s=List001
+001: s=List001
+001: s=List001
+001: s=List005
+001: s=List020
+001: s=List020
+003: s=List001
+003: s=List001
+003: s=List001
+003: s=List001
+],
+[001: s=List000 i=1 b=True r=101.000000
+001: s=List000 i=10 b=True r=110.000000
+001: s=List000 i=2 b=False r=102.000000
+001: s=List001 i=1 b=False r=110.000000
+001: s=List001 i=2 b=True r=120.000000
+001: s=List001 i=2 b=True r=122.000000
+001: s=List001 i=4 b=True r=130.000000
+001: s=List005 i=5 b=True r=130.000000
+001: s=List020 i=19 b=True r=219.000000
+001: s=List020 i=20 b=True r=220.000000
+003: s=List001 i=1 b=False r=110.000000
+003: s=List001 i=2 b=True r=120.000000
+003: s=List001 i=2 b=True r=122.000000
+003: s=List001 i=4 b=True r=130.000000
+],
+[002: i=1
+002: i=1
+002: i=2
+002: i=2
+002: i=2
+002: i=4
+002: i=5
+002: i=10
+002: i=19
+002: i=20
+004: i=5
+005: i=4
+005: i=5
+006: i=5
+006: i=10
+006: i=19
+006: i=20
+006: i=54
+007: i=1
+007: i=1
+007: i=2
+007: i=2
+007: i=2
+007: i=5
+007: i=10
+007: i=19
+007: i=20
+007: i=54
+],
+[002: i=1 s=List000 b=True r=101.000000
+002: i=1 s=List001 b=False r=110.000000
+002: i=10 s=List000 b=True r=110.000000
+002: i=19 s=List020 b=True r=219.000000
+002: i=2 s=List000 b=False r=102.000000
+002: i=2 s=List001 b=True r=120.000000
+002: i=2 s=List001 b=True r=122.000000
+002: i=20 s=List020 b=True r=220.000000
+002: i=4 s=List001 b=True r=130.000000
+002: i=5 s=List005 b=True r=130.000000
+004: i=5 s=List005 b=True r=130.000000
+005: i=4 s=List001 b=True r=130.000000
+005: i=5 s=List005 b=True r=130.000000
+006: i=10 s=List000 b=True r=110.000000
+006: i=19 s=List020 b=True r=219.000000
+006: i=20 s=List020 b=True r=220.000000
+006: i=5 s=List005 b=True r=130.000000
+006: i=54 s=Lista054 b=False r=0.000000
+007: i=1 s=List000 b=True r=101.000000
+007: i=1 s=List001 b=False r=110.000000
+007: i=10 s=List000 b=True r=110.000000
+007: i=19 s=List020 b=True r=219.000000
+007: i=2 s=List000 b=False r=102.000000
+007: i=2 s=List001 b=True r=120.000000
+007: i=2 s=List001 b=True r=122.000000
+007: i=20 s=List020 b=True r=220.000000
+007: i=5 s=List005 b=True r=130.000000
+007: i=54 s=Lista054 b=False r=0.000000
+])
+
+# Tests to verify the functionality of two column compound index.
+# It tests index for two columns using string and integer fields.
+# The run of test-ovsdb generates the output of the display of data using the different indexes defined in
+# the program.
+# Then, some at_checks are used to verify the correctness of the corresponding index as well as the existence
+# of all the rows involved in the test.
+m4_define([OVSDB_CHECK_IDL_COMPOUND_INDEX_DOUBLE_COLUMN_C],
+  [AT_SETUP([$1 - C])
+   AT_KEYWORDS([ovsdb server idl compound_index_double_column compound_index positive $5])
+   AT_CHECK([ovsdb_start_idltest])
+   m4_if([$2], [], [],
+     [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
+# Generate the data to be tested.
+   AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 -c idl-compound-index unix:socket $3],
+            [0], [stdout], [ignore], [kill `cat pid`])
+# Filter the rows of data that corresponds to the string-integer index eliminating the extra columns of data.
+# This is done to verifiy that the output data is in the correct and expected order.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sed -e 's/ b=.*//g']],
+            [0], [$4], [], [kill `cat pid`])
+# Here, the data is filtered and sorted in order to have all the rows in the index and be
+# able to determined that all the involved rows are present.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: s=.*' | sort -k 1,1n -k 2,2 -k 3,3]],
+            [0], [$5], [], [kill `cat pid`])
+# Filter the rows of data that corresponds to the integer index eliminating the extra columns of data.
+# This is done to verifiy that the output data is in the correct and expected order.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sed -e 's/ b=.*//g']],
+            [0], [$6], [], [kill `cat pid`])
+# Here again, the data is filtered and sorted in order to have all the rows in the index and be
+# able to determined that all the involved rows are present.
+   AT_CHECK([[cat stdout | grep -oh '[0-9]\{3\}: i=.*' | sort -k 1,1n -k 2,2 -k 3,3]],
+            [0], [$7], [], [kill `cat pid`])
+   OVSDB_SERVER_SHUTDOWN
+   AT_CLEANUP])
+
+OVSDB_CHECK_IDL_COMPOUND_INDEX_DOUBLE_COLUMN_C([Compound_index, double column test ],
+    [['["idltest",
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 1, "b":true, "r":101.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 2, "b":false, "r":102.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List000", "i": 10, "b":true, "r":110.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 1, "b":false, "r":110.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":120.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 2, "b":true, "r":122.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List001", "i": 4, "b":true, "r":130.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List005", "i": 5, "b":true, "r":130.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 20, "b":true, "r":220.0}},
+      {"op": "insert", "table": "simple", "row": {"s":"List020", "i": 19, "b":true, "r":219.0}}
+      ]']],
+    [idl_compound_index_double_column],
+    [001: s=List000 i=1
+001: s=List000 i=2
+001: s=List000 i=10
+001: s=List001 i=1
+001: s=List001 i=2
+001: s=List001 i=2
+001: s=List001 i=4
+001: s=List005 i=5
+001: s=List020 i=19
+001: s=List020 i=20
+002: s=List000 i=10
+002: s=List000 i=2
+002: s=List000 i=1
+002: s=List001 i=4
+002: s=List001 i=2
+002: s=List001 i=2
+002: s=List001 i=1
+002: s=List005 i=5
+002: s=List020 i=20
+002: s=List020 i=19
+003: s=List000 i=10
+004: s=List001 i=1
+004: s=List001 i=2
+004: s=List001 i=2
+004: s=List001 i=4
+004: s=List005 i=5
+],
+    [001: s=List000 i=1 b=True r=101.000000
+001: s=List000 i=10 b=True r=110.000000
+001: s=List000 i=2 b=False r=102.000000
+001: s=List001 i=1 b=False r=110.000000
+001: s=List001 i=2 b=True r=120.000000
+001: s=List001 i=2 b=True r=122.000000
+001: s=List001 i=4 b=True r=130.000000
+001: s=List005 i=5 b=True r=130.000000
+001: s=List020 i=19 b=True r=219.000000
+001: s=List020 i=20 b=True r=220.000000
+002: s=List000 i=1 b=True r=101.000000
+002: s=List000 i=10 b=True r=110.000000
+002: s=List000 i=2 b=False r=102.000000
+002: s=List001 i=1 b=False r=110.000000
+002: s=List001 i=2 b=True r=120.000000
+002: s=List001 i=2 b=True r=122.000000
+002: s=List001 i=4 b=True r=130.000000
+002: s=List005 i=5 b=True r=130.000000
+002: s=List020 i=19 b=True r=219.000000
+002: s=List020 i=20 b=True r=220.000000
+003: s=List000 i=10 b=True r=110.000000
+004: s=List001 i=1 b=False r=110.000000
+004: s=List001 i=2 b=True r=120.000000
+004: s=List001 i=2 b=True r=122.000000
+004: s=List001 i=4 b=True r=130.000000
+004: s=List005 i=5 b=True r=130.000000
+],
+    [005: i=1 s=List000
+005: i=1 s=List001
+005: i=2 s=List000
+005: i=2 s=List001
+005: i=2 s=List001
+005: i=4 s=List001
+005: i=5 s=List005
+005: i=10 s=List000
+005: i=19 s=List020
+005: i=20 s=List020
+006: i=20 s=List020
+006: i=19 s=List020
+006: i=10 s=List000
+006: i=5 s=List005
+006: i=4 s=List001
+006: i=2 s=List000
+006: i=2 s=List001
+006: i=2 s=List001
+006: i=1 s=List000
+006: i=1 s=List001
+],
+    [005: i=1 s=List000 b=True r=101.000000
+005: i=1 s=List001 b=False r=110.000000
+005: i=10 s=List000 b=True r=110.000000
+005: i=19 s=List020 b=True r=219.000000
+005: i=2 s=List000 b=False r=102.000000
+005: i=2 s=List001 b=True r=120.000000
+005: i=2 s=List001 b=True r=122.000000
+005: i=20 s=List020 b=True r=220.000000
+005: i=4 s=List001 b=True r=130.000000
+005: i=5 s=List005 b=True r=130.000000
+006: i=1 s=List000 b=True r=101.000000
+006: i=1 s=List001 b=False r=110.000000
+006: i=10 s=List000 b=True r=110.000000
+006: i=19 s=List020 b=True r=219.000000
+006: i=2 s=List000 b=False r=102.000000
+006: i=2 s=List001 b=True r=120.000000
+006: i=2 s=List001 b=True r=122.000000
+006: i=20 s=List020 b=True r=220.000000
+006: i=4 s=List001 b=True r=130.000000
+006: i=5 s=List005 b=True r=130.000000
+])
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index 9c51e323c..1760268b6 100644
--- a/tests/test-ovsdb.c
+++ b/tests/test-ovsdb.c
@@ -16,7 +16,6 @@
 
 #include <config.h>
 
-#include <assert.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <inttypes.h>
@@ -215,7 +214,14 @@ usage(void)
            "  idl-partial-update-set-column SERVER \n"
            "    connect to SERVER and executes different operations to\n"
            "    test the capacity of updating elements inside a set column\n"
-           "    displaying the table information after each operation.\n",
+           "    displaying the table information after each operation.\n"
+           "  idl-compound-index TEST_TO_EXECUTE\n"
+           "    Execute the tests to verify compound-index feature.\n"
+           "    The TEST_TO_EXECUTE are:\n"
+           "        idl_compound_index_single_column:\n"
+           "          test for indexes using one column.\n"
+           "        idl_compound_index_double_column:\n"
+           "            test for indexes using two columns.\n",
            program_name, program_name);
     vlog_usage();
     printf("\nOther options:\n"
@@ -394,7 +400,7 @@ do_default_data(struct ovs_cmdl_context *ctx OVS_UNUSED)
                 ovsdb_base_type_init(&type.value, value);
                 type.n_min = n_min;
                 type.n_max = 1;
-                assert(ovsdb_type_is_valid(&type));
+                ovs_assert(ovsdb_type_is_valid(&type));
 
                 printf("key %s, value %s, n_min %u: ",
                        ovsdb_atomic_type_to_string(key),
@@ -1681,7 +1687,7 @@ do_transact_print(struct ovs_cmdl_context *ctx OVS_UNUSED)
     HMAP_FOR_EACH (row, hmap_node, &do_transact_table->rows) {
         rows[i++] = row;
     }
-    assert(i == n_rows);
+    ovs_assert(i == n_rows);
 
     qsort(rows, n_rows, sizeof *rows, compare_rows_by_uuid);
 
@@ -1723,7 +1729,7 @@ do_transact(struct ovs_cmdl_context *ctx)
     json_destroy(json);
     do_transact_db = ovsdb_create(schema);
     do_transact_table = ovsdb_get_table(do_transact_db, "mytable");
-    assert(do_transact_table != NULL);
+    ovs_assert(do_transact_table != NULL);
 
     for (i = 1; i < ctx->argc; i++) {
         struct json *command;
@@ -2655,6 +2661,275 @@ do_idl_partial_update_set_column(struct ovs_cmdl_context *ctx)
     return;
 }
 
+static int
+test_idl_compound_index_single_column(struct ovsdb_idl *idl,
+        struct ovsdb_idl_index_cursor *sCursor,
+        struct ovsdb_idl_index_cursor *iCursor)
+{
+    const struct idltest_simple *myRow;
+    struct ovsdb_idl_txn *txn;
+    int step = 0;
+
+     /* Display records by string index -> sCursor */
+     ++step;
+     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sCursor) {
+         printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s,
+                myRow->i, myRow->b?"True":"False", myRow->r);
+     }
+     /* Display records by integer index -> iCursor */
+     ++step;
+     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, iCursor) {
+         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
+                myRow->s, myRow->b?"True":"False", myRow->r);
+     }
+     /* Display records by string index -> sCursor with filtering
+      * where s=\"List001\
+      */
+     ++step;
+     struct idltest_simple *equal;
+     equal = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     idltest_simple_index_set_s(equal, "List001");
+     ovs_assert(strcmp(equal->s, "List001") == 0);
+     IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, sCursor, equal) {
+         printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s,
+                myRow->i, myRow->b?"True":"False", myRow->r);
+     }
+     /* Display records by integer index -> iCursor with filtering where i=5 */
+     ++step;
+     idltest_simple_index_set_i(equal, 5);
+     ovs_assert(equal->i == 5);
+     IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, iCursor, equal) {
+         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
+                myRow->s, myRow->b?"True":"False", myRow->r);
+     }
+     /* Display records by integer index -> iCursor in range i=[3,7] */
+     ++step;
+     struct idltest_simple *from, *to;
+     from = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     idltest_simple_index_set_i(from, 3);
+     ovs_assert(from->i == 3);
+     to = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     idltest_simple_index_set_i(to, 7);
+     ovs_assert(to->i == 7);
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, iCursor, from, to) {
+         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
+                myRow->s, myRow->b?"True":"False", myRow->r);
+     }
+     /* Delete record i=4 and insert i=54 by integer index -> iCursor */
+     ++step;
+     struct idltest_simple *toDelete, *toInsert;
+     toDelete = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     idltest_simple_index_set_i(toDelete, 4);
+     ovs_assert(toDelete->i == 4);
+     myRow = idltest_simple_index_find(iCursor, toDelete);
+     ovs_assert(myRow);
+     ovs_assert(myRow->i == 4);
+     txn = ovsdb_idl_txn_create(idl);
+     idltest_simple_delete(myRow);
+     toInsert = idltest_simple_insert(txn);
+     idltest_simple_set_i(toInsert, 54);
+     idltest_simple_set_s(toInsert, "Lista054");
+     ovsdb_idl_txn_commit_block(txn);
+     ovsdb_idl_txn_destroy(txn);
+     idltest_simple_index_set_i(to, 60);
+     printf("Expected 60, stored %"PRId64"\n", to->i);
+     ovs_assert(to->i == 60);
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, iCursor, from, to) {
+         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
+                myRow->s, myRow->b?"True":"False", myRow->r);
+     }
+
+     /* Test special-case range, "from" and "to" are both NULL,
+      * which is interpreted as the range from -infinity to +infinity. */
+     ++step;
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, iCursor, NULL, NULL) {
+         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
+                myRow->s, myRow->b?"True":"False", myRow->r);
+     }
+
+     /* Free the temporal rows */
+     idltest_simple_index_destroy_row(from);
+     idltest_simple_index_destroy_row(to);
+     idltest_simple_index_destroy_row(equal);
+     return step;
+}
+
+static int
+test_idl_compound_index_double_column(struct ovsdb_idl *idl,
+        struct ovsdb_idl_index_cursor *siCursor,
+        struct ovsdb_idl_index_cursor *sidCursor,
+        struct ovsdb_idl_index_cursor *isCursor,
+        struct ovsdb_idl_index_cursor *idsCursor)
+{
+    const struct idltest_simple *myRow;
+    int step = 0;
+
+    /* Display records by string-integer index -> siCursor */
+    step++;
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, siCursor) {
+        printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i,
+               myRow->b?"True":"False", myRow->r);
+    }
+    /* Display records by string-integer(down order) index -> sidCursor */
+    step++;
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sidCursor) {
+        printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i,
+               myRow->b?"True":"False", myRow->r);
+    }
+    /* Display records by string-integer index -> siCursor with filtering
+     * where s="List000" and i=10
+     */
+    step++;
+    struct idltest_simple *equal;
+    equal = idltest_simple_index_init_row(idl, &idltest_table_simple);
+    idltest_simple_index_set_s(equal, "List000");
+    ovs_assert(strcmp(equal->s, "List000") == 0);
+    idltest_simple_index_set_i(equal, 10);
+    ovs_assert(equal->i == 10);
+    IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, siCursor, equal) {
+        printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i,
+               myRow->b?"True":"False", myRow->r);
+    }
+    /* Display records by string-integer index -> siCursor in range i=[0,100]
+     * and s=[\"List002\",\"List003\"]
+     */
+    step++;
+    struct idltest_simple *from, *to;
+    from =  idltest_simple_index_init_row(idl, &idltest_table_simple);
+    to = idltest_simple_index_init_row(idl, &idltest_table_simple);
+    idltest_simple_index_set_i(from, 0);
+    ovs_assert(from->i == 0);
+    idltest_simple_index_set_s(from, "List001");
+    ovs_assert(strcmp(from->s, "List001") == 0);
+    idltest_simple_index_set_i(to, 100);
+    ovs_assert(to->i == 100);
+    idltest_simple_index_set_s(to, "List005");
+    ovs_assert(strcmp(to->s, "List005")==0);
+    IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, siCursor, from, to) {
+        printf("%03d: s=%s i=%"PRId64" b=%s r=%f\n", step, myRow->s, myRow->i,
+               myRow->b?"True":"False", myRow->r);
+    }
+    /* Display records using integer-string index. */
+    step++;
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, isCursor) {
+        printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s,
+               myRow->b?"True":"False", myRow->r);
+    }
+    /* Display records using integer(descend)-string index. */
+    step++;
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, idsCursor) {
+        printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s,
+               myRow->b?"True":"False", myRow->r);
+    }
+
+    idltest_simple_index_destroy_row(to);
+    idltest_simple_index_destroy_row(from);
+    idltest_simple_index_destroy_row(equal);
+    return step;
+}
+
+static void
+do_idl_compound_index(struct ovs_cmdl_context *ctx)
+{
+    struct ovsdb_idl *idl;
+    struct ovsdb_idl_index_cursor sCursor, iCursor, siCursor, sidCursor,
+                    isCursor, idsCursor;
+    enum TESTS { IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN,
+            IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN
+    };
+    int step = 0;
+    int i;
+
+    idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true);
+
+    /* Add tables/columns and initialize index data needed for tests */
+    ovsdb_idl_add_table(idl, &idltest_table_simple);
+    ovsdb_idl_add_column(idl, &idltest_simple_col_s);
+    ovsdb_idl_add_column(idl, &idltest_simple_col_i);
+    ovsdb_idl_add_column(idl, &idltest_simple_col_r);
+    ovsdb_idl_add_column(idl, &idltest_simple_col_b);
+
+    struct ovsdb_idl_index *index;
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple, "string");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_s, OVSDB_INDEX_ASC,
+                               NULL);
+
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple, "integer");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_i, OVSDB_INDEX_ASC,
+                               NULL);
+
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple,
+                                   "string-integer");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_s, OVSDB_INDEX_ASC,
+                               NULL);
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_i, OVSDB_INDEX_ASC,
+                               NULL);
+
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple,
+                                   "string-integerd");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_s, OVSDB_INDEX_ASC,
+                               NULL);
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_i, OVSDB_INDEX_DESC,
+                               NULL);
+
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple,
+                                   "integer-string");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_i, OVSDB_INDEX_ASC,
+                               NULL);
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_s, OVSDB_INDEX_ASC,
+                               NULL);
+
+    index = ovsdb_idl_create_index(idl, &idltest_table_simple,
+                                   "integerd-string");
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_i, OVSDB_INDEX_DESC,
+                               NULL);
+    ovsdb_idl_index_add_column(index, &idltest_simple_col_s, OVSDB_INDEX_ASC,
+                               NULL);
+
+    /* wait for replica to be updated */
+    ovsdb_idl_get_initial_snapshot(idl);
+
+    /* Initialize cursors to be used by indexes */
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "string",
+                                &sCursor);
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "integer",
+                                &iCursor);
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "string-integer",
+                                &siCursor);
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "string-integerd",
+                                &sidCursor);
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "integer-string",
+                                &isCursor);
+    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple, "integerd-string",
+                                &idsCursor);
+
+    setvbuf(stdout, NULL, _IONBF, 0);
+    int test_to_run = -1;
+    for (i = 2; i < ctx->argc; i++) {
+        char *arg = ctx->argv[i];
+
+        if (strcmp(arg,"idl_compound_index_single_column") == 0) {
+            test_to_run = IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN;
+        } else if (strcmp(arg, "idl_compound_index_double_column") == 0) {
+            test_to_run = IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN;
+        }
+
+        switch (test_to_run) {
+            case IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN:
+                test_idl_compound_index_single_column(idl, &sCursor, &iCursor);
+                break;
+            case IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN:
+                test_idl_compound_index_double_column(idl, &siCursor,
+                        &sidCursor, &isCursor, &idsCursor);
+                break;
+            default:
+                printf("%03d: Test %s not implemented.\n", step++, arg);
+        }
+    }
+    ovsdb_idl_destroy(idl);
+    printf("%03d: done\n", step);
+}
+
 static struct ovs_cmdl_command all_commands[] = {
     { "log-io", NULL, 2, INT_MAX, do_log_io, OVS_RO },
     { "default-atoms", NULL, 0, 0, do_default_atoms, OVS_RO },
@@ -2686,6 +2961,7 @@ static struct ovs_cmdl_command all_commands[] = {
     { "execute-readonly", NULL, 2, INT_MAX, do_execute_ro, OVS_RO },
     { "trigger", NULL, 2, INT_MAX, do_trigger, OVS_RO },
     { "idl", NULL, 1, INT_MAX, do_idl, OVS_RO },
+    { "idl-compound-index", NULL, 2, 2, do_idl_compound_index, OVS_RW },
     { "idl-partial-update-map-column", NULL, 1, INT_MAX,
         do_idl_partial_update_map_column, OVS_RO },
     { "idl-partial-update-set-column", NULL, 1, INT_MAX,
-- 
2.13.0



More information about the dev mailing list