[ovs-dev] [PATCH 3/5] ovsdb-idl: Redesign use of indexes.

Ben Pfaff blp at ovn.org
Fri Jun 8 21:59:20 UTC 2018


The design of the compound index feature in the C OVSDB IDL was unusual.
Indexes were generally referenced only by name rather than by pointer, and
could be obtained only from the top-level ovsdb_idl object.  To iterate or
otherwise search an index required explicitly creating a special
ovsdb_idl_cursor object, which at least seemed somewhat heavy-weight given
that it required a string lookup in a table of indexes.

This commit redesigns the compound index interface.  It discards the use of
names for indexes, instead having clients pass in a pointer to the index
object itself.  It simplifies how indexes are created, gets rid of the need
for explicit cursor objects, and updates all of the users to the new
interface.

The underlying reason for this commit is to make it easier in
ovn-controller to keep track of the dependencies for a given function, by
making the indexes explicit arguments to any function that needs to use
them.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 Documentation/topics/idl-compound-indexes.rst | 110 ++++-----
 lib/ovsdb-idl-provider.h                      |  57 ++---
 lib/ovsdb-idl.c                               | 330 +++++++++-----------------
 lib/ovsdb-idl.h                               |  86 +++----
 ovn/controller/bfd.c                          | 124 +++++-----
 ovn/controller/bfd.h                          |   3 +-
 ovn/controller/binding.c                      |  80 ++++---
 ovn/controller/binding.h                      |   4 +
 ovn/controller/lflow.c                        | 124 +++++-----
 ovn/controller/lflow.h                        |   5 +-
 ovn/controller/lport.c                        | 165 ++++---------
 ovn/controller/lport.h                        |  29 +--
 ovn/controller/ovn-controller.c               |  76 +++---
 ovn/controller/physical.c                     |  15 +-
 ovn/controller/physical.h                     |   2 +-
 ovn/controller/pinctrl.c                      | 156 ++++++------
 ovn/controller/pinctrl.h                      |   5 +
 ovsdb/ovsdb-idlc.in                           | 185 ++++++++-------
 tests/test-ovsdb.c                            | 179 ++++++--------
 19 files changed, 793 insertions(+), 942 deletions(-)

diff --git a/Documentation/topics/idl-compound-indexes.rst b/Documentation/topics/idl-compound-indexes.rst
index 79371f9831e8..06ec9a36f71c 100644
--- a/Documentation/topics/idl-compound-indexes.rst
+++ b/Documentation/topics/idl-compound-indexes.rst
@@ -89,21 +89,16 @@ following options:
 -  Use a custom ordering comparator (eg: treat a string column like a IP,
    or sort by the value of the "config" key in a map column).
 
-For querying the index the user needs to create a cursor. That cursor
-points to a position in the index. The user can then use the cursor to
-perform lookups (by key) and/or get the subsequent rows. The user can
-also compare the current value of the cursor to a record.
+Indexes can be searched for matches based on the key.  They can also
+be iterated across a range of keys or in full.
 
 For lookups, the user needs to provide a key to be used for locating the
-specific rows that meet his criteria. This key could be an IP address, a
-MAC address, an ACL rule, etc. When the information is found in the data
-structure the user's cursor is updated to point to the row. If several
-rows match the query then the user can easily get the next row in sequence
-by updating the cursor.
+specific rows that meet his criteria. This key could be an IP address, a MAC
+address, an ACL rule, etc. If several rows match the query then the user can
+easily iterate over all of the matches.
 
 For accessing data in lexicographic order, the user can use the ranged
-iterators. Those iterators need a cursor and "from" and "to" values to
-define a range.
+iterators, which use "from" and "to" values to define a range.
 
 The indexes maintain a pointer to the row in the local replica, avoiding
 the need to make additional copies of the data and thereby minimizing any
@@ -153,10 +148,10 @@ C IDL API
 Index Creation
 ~~~~~~~~~~~~~~
 
-Each index must be created with the function ``ovsdb_idl_create_index()``.
-After an index has been created the user can add one or more columns to it,
-using ``ovsdb_idl_index_add_column``. All indexes must be created with all
-columns added BEFORE the first call to ovsdb\_idl\_run().
+Each index must be created with the function ``ovsdb_idl_index_create()`` or
+one of the simpler convenience functions ``ovsdb_idl_index_create`()`` or
+``ovsdb_idl_index_create2()``.  All indexes must be created before the first
+call to ovsdb\_idl\_run().
 
 Index Creation Example
 ^^^^^^^^^^^^^^^^^^^^^^
@@ -185,19 +180,15 @@ Index Creation Example
         ovsdb_idl_add_column(*idl, &ovsrec_test_col_enumField);
         ovsdb_idl_add_column(*idl, &ovsrec_test_col_boolField);
 
-        /* Create an index.
-         * This index is created using (stringField, numericField) as key.
-         * Also shows the usage of some arguments of add column, although
-         * for a string column it is unnecesary to pass a custom comparator.
-         */
-        struct ovsdb_idl_index *index;
-        index = ovsdb_idl_create_index(*idl, &ovsrec_table_test,
-                                       "by_stringField");
-        ovsdb_idl_index_add_column(index, &ovsrec_test_col_stringField,
-                                   OVSDB_INDEX_ASC, stringField_comparator);
-        ovsdb_idl_index_add_column(index, &ovsrec_test_col_numericField,
-                                   OVSDB_INDEX_DESC, NULL);
-        /* Done. */
+        struct ovsdb_idl_index_column columns[] = {
+            { .column = &ovsrec_test_col_stringField,
+              .comparer = stringField_comparator },
+            { .column = &ovsrec_test_col_numericField, 
+              .order = OVSDB_INDEX_DESC },
+        };
+        struct ovsdb_idl_index *index = ovsdb_idl_create_index(
+            *idl, columns, ARRAY_SIZE(columns));
+        ...
     }
 
 Index Usage
@@ -210,29 +201,20 @@ The recommended way to do queries is using a "ranged foreach", an "equal
 foreach" or a "full foreach" over an index. The mechanism works as
 follows:
 
-1. Create a cursor.
 2. Create index row objects with index columns set to desired search key
    values (one is needed for equality iterators, two for range iterators,
    a search key is not needed for the full index iterator).
-3. Pass the cursor, an iteration variable, and the key values to the iterator.
+3. Pass the index, an iteration variable, and the key values to the iterator.
 4. Use the values within iterator loop.
 
-To create the cursor for the example, we use the following code:
-
-::
-
-    ovsdb_idl_index_cursor my_cursor;
-    ovsdb_idl_initialize_cursor(idl, &ovsrec_table_test, "by_stringField",
-                                &my_cursor);
-
-Now the cursor can be used to perform queries. The library implements three
-different iterators: a range iterator, an equality iterator and a full index
-iterator. The range iterator receives two values and iterates over all
-rows with values that are within that range (inclusive of the two values
-defining the range). The equality iterator iterates over all rows that exactly
-match the value passed. The full index iterator iterates over all rows in the
-index, in an order determined by the comparison function and configured
-direction (ascending or descending).
+The library implements three different iterators: a range iterator, an
+equality iterator and a full index iterator. The range iterator
+receives two values and iterates over all rows with values that are
+within that range (inclusive of the two values defining the range). The
+equality iterator iterates over all rows that exactly match the value
+passed. The full index iterator iterates over all rows in the index, in
+an order determined by the comparison function and configured direction
+(ascending or descending).
 
 Note that indexes are *sorted by the "concatenation" of the values in
 all indexed columns*, so the ranged iterator returns all the values
@@ -248,41 +230,40 @@ these iterators follows:
     /*
      * Equality iterator; iterates over all the records equal to "value".
      */
-    ovsrec_test *value, *record;
-    value = ovsrec_test_index_init_row(idl, &ovsrec_table_test);
-    ovsrec_test_index_set_stringField(value, "hello world");
-    OVSREC_TEST_FOR_EACH_EQUAL (record, &my_cursor, value) {
+    struct ovsrec_test *target = ovsrec_test_index_init_row(index);
+    ovsrec_test_index_set_stringField(target, "hello world");
+    struct ovsrec_test *record;
+    OVSREC_TEST_FOR_EACH_EQUAL (record, target, index) {
         /* Can return zero, one or more records */
         assert(strcmp(record->stringField, "hello world") == 0);
         printf("Found one record with %s", record->stringField);
     }
-    ovsrec_test_index_destroy_row(value);
+    ovsrec_test_index_destroy_row(target);
 
     /*
      * Range iterator; iterates over all records between two values
      * (inclusive).
      */
-    ovsrec_test *value_from, *value_to;
-    value_from = ovsrec_test_index_init_row(idl, &ovsrec_table_test);
-    value_to = ovsrec_test_index_init_row(idl, &ovsrec_table_test);
+    struct ovsrec_test *from = ovsrec_test_index_init_row(index);
+    struct ovsrec_test *to = ovsrec_test_index_init_row(index);
 
-    ovsrec_test_index_set_stringField(value_from, "aaa");
-    ovsrec_test_index_set_stringField(value_to, "mmm");
-    OVSREC_TEST_FOR_EACH_RANGE (record, &my_cursor, value_from, value_to) {
+    ovsrec_test_index_set_stringField(from, "aaa");
+    ovsrec_test_index_set_stringField(to, "mmm");
+    OVSREC_TEST_FOR_EACH_RANGE (record, from, to, index) {
         /* Can return zero, one or more records */
         assert(strcmp("aaa", record->stringField) <= 0);
         assert(strcmp(record->stringField, "mmm") <= 0);
         printf("Found one record with %s", record->stringField);
     }
-    ovsrec_test_index_destroy_row(value_from);
-    ovsrec_test_index_destroy_row(value_to);
+    ovsrec_test_index_destroy_row(from);
+    ovsrec_test_index_destroy_row(to);
 
     /*
      * Index iterator; iterates over all nodes in the index, in order
      * determined by comparison function and configured order (ascending
      * or descending).
      */
-    OVSREC_TEST_FOR_EACH_BYINDEX (record, &my_cursor) {
+    OVSREC_TEST_FOR_EACH_BYINDEX (record, index) {
         /* Can return zero, one or more records */
         printf("Found one record with %s", record->stringField);
     }
@@ -292,11 +273,4 @@ General Index Access
 
 While the currently defined iterators are suitable for many use cases, it is
 also possible to create custom iterators using the more general API on which
-the existing iterators have been built. This API includes the following
-functions, declared in "lib/ovsdb-idl.h":
-
-1. ``ovsrec_<table>_index_compare()``
-2. ``ovsrec_<table>_index_next()``
-3. ``ovsrec_<table>_index_find()``
-4. ``ovsrec_<table>_index_forward_to()``
-5. ``ovsrec_<table>_index_get_data()``
+the existing iterators have been built.  See ``ovsdb-idl.h`` for the details.
diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
index 70bfde11e6a1..2eee4fd01271 100644
--- a/lib/ovsdb-idl-provider.h
+++ b/lib/ovsdb-idl-provider.h
@@ -118,7 +118,7 @@ struct ovsdb_idl_table {
     struct hmap rows;        /* Contains "struct ovsdb_idl_row"s. */
     struct ovsdb_idl_db *db; /* Containing db. */
     unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
-    struct shash indexes;    /* Contains "struct ovsdb_idl_index"s */
+    struct ovs_list indexes;    /* Contains "struct ovsdb_idl_index"s */
     struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */
     struct ovsdb_idl_condition condition;
     bool cond_changed;
@@ -130,33 +130,6 @@ struct ovsdb_idl_class {
     size_t n_tables;
 };
 
-/*
- * Structure containing the per-column configuration of the index.
- */
-struct ovsdb_idl_index_column {
-    const struct ovsdb_idl_column *column; /* Column used for index key. */
-    column_comparator *comparer; /* Column comparison function. */
-    int sorting_order; /* Sorting order (ascending or descending). */
-};
-
-/*
- * Defines a IDL compound index
- */
-struct ovsdb_idl_index {
-    struct skiplist *skiplist;    /* Skiplist with pointers to rows. */
-    struct ovsdb_idl_index_column *columns; /* Columns configuration */
-    size_t n_columns;             /* Number of columns in index. */
-    size_t alloc_columns;         /* Size allocated memory for columns,
-                                     comparers and sorting order. */
-    bool ins_del;                 /* True if a row in the index is being
-                                     inserted or deleted; if true, the
-                                     search key is augmented with the
-                                     UUID and address in order to discriminate
-                                     between entries with identical keys. */
-    const struct ovsdb_idl_table *table; /* Table that owns this index */
-    const char *index_name;       /* The name of this index. */
-};
-
 struct ovsdb_idl_row *ovsdb_idl_get_row_arc(
     struct ovsdb_idl_row *src,
     const struct ovsdb_idl_table_class *dst_table,
@@ -166,6 +139,34 @@ void ovsdb_idl_txn_verify(const struct ovsdb_idl_row *,
                           const struct ovsdb_idl_column *);
 
 struct ovsdb_idl_txn *ovsdb_idl_txn_get(const struct ovsdb_idl_row *);
+
+/* Index internals. */
+
+struct ovsdb_idl_index {
+    struct ovs_list node;                   /* In ->table->indexes. */
+    struct ovsdb_idl_table *table;          /* The indexed table. */
+    struct ovsdb_idl_index_column *columns; /* The indexed columns. */
+    size_t n_columns;
+
+    /* Skiplist with pointers to rows. */
+    struct skiplist *skiplist;
+
+    /* True if a row in the index is being inserted or deleted.  If true, the
+       search key is augmented with the UUID and address to discriminate
+       between entries with identical keys. */
+    bool ins_del;
+};
+
+int ovsdb_idl_index_compare(struct ovsdb_idl_index *,
+                            const struct ovsdb_idl_row *a,
+                            const struct ovsdb_idl_row *b);
+
+void ovsdb_idl_index_write(struct ovsdb_idl_row *,
+                            const struct ovsdb_idl_column *,
+                            struct ovsdb_datum *,
+                            const struct ovsdb_idl_table_class *);
+struct ovsdb_idl_row *ovsdb_idl_index_init_row(struct ovsdb_idl_index *);
+void ovsdb_idl_index_destroy_row(const struct ovsdb_idl_row *);
 
 #ifdef __cplusplus
 }
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index c6ff78e25a04..cd1a12704f29 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -361,11 +361,7 @@ ovsdb_idl_table_from_class(const struct ovsdb_idl *,
 static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table);
 static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
 
-static struct ovsdb_idl_index *ovsdb_idl_create_index_(const struct
-                                                       ovsdb_idl_table *table,
-                                                       size_t allocated_cols);
-static void
- ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table);
+static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *);
 static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *);
 static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *);
 
@@ -405,7 +401,7 @@ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
         memset(table->modes, default_mode, tc->n_columns);
         table->need_table = false;
         shash_init(&table->columns);
-        shash_init(&table->indexes);
+        ovs_list_init(&table->indexes);
         for (size_t j = 0; j < tc->n_columns; j++) {
             const struct ovsdb_idl_column *column = &tc->columns[j];
 
@@ -2461,44 +2457,6 @@ ovsdb_idl_row_unparse(struct ovsdb_idl_row *row)
  * iterate over a subset of rows in a defined order.
  */
 
-static struct ovsdb_idl_index *
-ovsdb_idl_db_create_index(struct ovsdb_idl_db *db,
-                          const struct ovsdb_idl_table_class *tc,
-                          const char *index_name)
-{
-    struct ovsdb_idl_index *index;
-    size_t i;
-
-    for (i = 0; i < db->class_->n_tables; i++) {
-        struct ovsdb_idl_table *table = &db->tables[i];
-
-        if (table->class_ == tc) {
-            index = ovsdb_idl_create_index_(table, 1);
-            if (!shash_add_once(&table->indexes, index_name, index)) {
-                VLOG_ERR("Duplicate index name '%s' in table %s",
-                         index_name, table->class_->name);
-                return NULL;
-            }
-            index->index_name = index_name;
-            return index;
-        }
-    }
-    OVS_NOT_REACHED();
-    return NULL;
-}
-
-/* Creates a new index with the provided name, attached to the given idl and
- * table. Note that all indexes must be created and indexing columns added
- * before the first call to ovsdb_idl_run() is made.
- */
-struct ovsdb_idl_index *
-ovsdb_idl_create_index(struct ovsdb_idl *idl,
-                       const struct ovsdb_idl_table_class *tc,
-                       const char *index_name)
-{
-    return ovsdb_idl_db_create_index(&idl->data, tc, index_name);
-}
-
 /* Generic comparator that can compare each index, using the custom
  * configuration (an struct ovsdb_idl_index) passed to it.
  * Not intended for direct usage.
@@ -2533,7 +2491,7 @@ ovsdb_idl_index_generic_comparer(const void *a,
         }
 
         if (val) {
-            return val * index->columns[i].sorting_order;
+            return index->columns[i].order == OVSDB_INDEX_ASC ? val : -val;
         }
     }
 
@@ -2558,34 +2516,73 @@ ovsdb_idl_index_generic_comparer(const void *a,
 }
 
 static struct ovsdb_idl_index *
-ovsdb_idl_create_index_(const struct ovsdb_idl_table *table,
-                        size_t allocated_cols)
+ovsdb_idl_db_index_create(struct ovsdb_idl_db *db,
+                          const struct ovsdb_idl_index_column *columns,
+                          size_t n)
 {
-    struct ovsdb_idl_index *index;
+    ovs_assert(n > 0);
 
-    index = xmalloc(sizeof (struct ovsdb_idl_index));
-    index->n_columns = 0;
-    index->alloc_columns = allocated_cols;
+    struct ovsdb_idl_index *index = xzalloc(sizeof *index);
+
+    index->table = ovsdb_idl_table_from_column(db, columns[0].column);
+    for (size_t i = 0; i < n; i++) {
+        const struct ovsdb_idl_index_column *c = &columns[i];
+        ovs_assert(ovsdb_idl_table_from_column(db, c->column) == index->table);
+        ovs_assert(*ovsdb_idl_db_get_mode(db, c->column) & OVSDB_IDL_MONITOR);
+    }
+
+    index->columns = xmemdup(columns, n * sizeof *columns);
+    index->n_columns = n;
     index->skiplist = skiplist_create(ovsdb_idl_index_generic_comparer, index);
-    index->columns = xmalloc(allocated_cols *
-                             sizeof (struct ovsdb_idl_index_column));
-    index->ins_del = false;
-    index->table = table;
+
+    ovs_list_push_back(&index->table->indexes, &index->node);
+
     return index;
 }
 
+/* Creates a new index for the given 'idl' and with the 'n' specified
+ * 'columns'.
+ *
+ * All indexes must be created before the first call to ovsdb_idl_run(). */
+struct ovsdb_idl_index *
+ovsdb_idl_index_create(struct ovsdb_idl *idl,
+                       const struct ovsdb_idl_index_column *columns,
+                       size_t n)
+{
+    return ovsdb_idl_db_index_create(&idl->data, columns, n);
+}
+
+struct ovsdb_idl_index *
+ovsdb_idl_index_create1(struct ovsdb_idl *idl,
+                        const struct ovsdb_idl_column *column1)
+{
+    const struct ovsdb_idl_index_column columns[] = {
+        { .column = column1 },
+    };
+    return ovsdb_idl_index_create(idl, columns, ARRAY_SIZE(columns));
+}
+
+struct ovsdb_idl_index *
+ovsdb_idl_index_create2(struct ovsdb_idl *idl,
+                        const struct ovsdb_idl_column *column1,
+                        const struct ovsdb_idl_column *column2)
+{
+    const struct ovsdb_idl_index_column columns[] = {
+        { .column = column1 },
+        { .column = column2 },
+    };
+    return ovsdb_idl_index_create(idl, columns, ARRAY_SIZE(columns));
+}
+
 static void
 ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table)
 {
-    struct ovsdb_idl_index *index;
-    struct shash_node *node;
-
-    SHASH_FOR_EACH (node, &(table->indexes)) {
-        index = node->data;
+    struct ovsdb_idl_index *index, *next;
+    LIST_FOR_EACH_SAFE (index, next, node, &table->indexes) {
         skiplist_destroy(index->skiplist, NULL);
         free(index->columns);
+        free(index);
     }
-    shash_destroy_free_data(&table->indexes);
 }
 
 static void
@@ -2593,10 +2590,7 @@ ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *row)
 {
     struct ovsdb_idl_table *table = row->table;
     struct ovsdb_idl_index *index;
-    struct shash_node *node;
-
-    SHASH_FOR_EACH (node, &(table->indexes)) {
-        index = node->data;
+    LIST_FOR_EACH (index, node, &table->indexes) {
         index->ins_del = true;
         skiplist_insert(index->skiplist, row);
         index->ins_del = false;
@@ -2608,110 +2602,17 @@ ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *row)
 {
     struct ovsdb_idl_table *table = row->table;
     struct ovsdb_idl_index *index;
-    struct shash_node *node;
-
-    SHASH_FOR_EACH (node, &(table->indexes)) {
-        index = node->data;
+    LIST_FOR_EACH (index, node, &table->indexes) {
         index->ins_del = true;
         skiplist_delete(index->skiplist, row);
         index->ins_del = false;
     }
 }
 
-/* Adds a column to an existing index (note that columns can only be added to
- * an index before the first call to ovsdb_idl_run()). The 'order' parameter
- * specifies whether the sort order should be ascending (OVSDB_INDEX_ASC) or
- * descending (OVSDB_INDEX_DESC). The 'custom_comparer' parameter, if non-NULL,
- * contains a pointer to a custom comparison function. A default comparison
- * function is used if a custom comparison function is not provided (the
- * default comparison function can only be used for columns of type string,
- * uuid, integer, real, or boolean).
- */
+/* Writes a datum in an ovsdb_idl_row, and updates the corresponding field in
+ * the table record.  Not intended for direct usage. */
 void
-ovsdb_idl_index_add_column(struct ovsdb_idl_index *index,
-                           const struct ovsdb_idl_column *column,
-                           int order, column_comparator *custom_comparer)
-{
-    /* Check that the column or table is tracked */
-    if (!index->table->need_table &&
-        !((OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT) &
-          *ovsdb_idl_db_get_mode(index->table->db, column))) {
-        VLOG_ERR("Can't add unmonitored column '%s' at index '%s' in "
-                 "table '%s'.",
-                 column->name, index->index_name, index->table->class_->name);
-    }
-    if (!ovsdb_type_is_scalar(&column->type) && !custom_comparer) {
-        VLOG_WARN("Comparing non-scalar values.");
-    }
-
-    /* Allocate more memory for column configuration */
-    if (index->n_columns == index->alloc_columns) {
-        index->alloc_columns++;
-        index->columns = xrealloc(index->columns,
-                                  index->alloc_columns *
-                                  sizeof(struct ovsdb_idl_index_column));
-    }
-
-    /* Append column to index */
-    int i = index->n_columns;
-
-    index->columns[i].column = column;
-    index->columns[i].comparer = custom_comparer ? custom_comparer : NULL;
-    if (order == OVSDB_INDEX_ASC) {
-        index->columns[i].sorting_order = OVSDB_INDEX_ASC;
-    } else {
-        index->columns[i].sorting_order = OVSDB_INDEX_DESC;
-    }
-    index->n_columns++;
-}
-
-static bool
-ovsdb_idl_db_initialize_cursor(struct ovsdb_idl_db *db,
-                               const struct ovsdb_idl_table_class *tc,
-                               const char *index_name,
-                               struct ovsdb_idl_index_cursor *cursor)
-{
-    size_t i;
-
-    for (i = 0; i < db->class_->n_tables; i++) {
-        struct ovsdb_idl_table *table = &db->tables[i];
-
-        if (table->class_ == tc) {
-            struct shash_node *node = shash_find(&table->indexes, index_name);
-
-            if (!node || !node->data) {
-                VLOG_ERR("Cursor initialization failed, "
-                         "index %s at table %s does not exist.",
-                         index_name, tc->name);
-                cursor->index = NULL;
-                cursor->position = NULL;
-                return false;
-            }
-            cursor->index = node->data;
-            cursor->position = skiplist_first(cursor->index->skiplist);
-            return true;
-        }
-    }
-    VLOG_ERR("Cursor initialization failed, "
-             "index %s at table %s does not exist.", index_name, tc->name);
-    return false;
-}
-
-bool
-ovsdb_idl_initialize_cursor(struct ovsdb_idl *idl,
-                            const struct ovsdb_idl_table_class *tc,
-                            const char *index_name,
-                            struct ovsdb_idl_index_cursor *cursor)
-{
-    return ovsdb_idl_db_initialize_cursor(&idl->data, tc, index_name, cursor);
-}
-
-/* ovsdb_idl_index_write_ writes a datum in an ovsdb_idl_row,
- * and updates the corresponding field in the table record.
- * Not intended for direct usage.
- */
-void
-ovsdb_idl_index_write_(struct ovsdb_idl_row *const_row,
+ovsdb_idl_index_write(struct ovsdb_idl_row *const_row,
                        const struct ovsdb_idl_column *column,
                        struct ovsdb_datum *datum,
                        const struct ovsdb_idl_table_class *class)
@@ -2739,7 +2640,7 @@ static const struct uuid index_row_uuid = {
 
 /* Check if a row is an index row */
 static bool
-is_index_row(struct ovsdb_idl_row *row)
+is_index_row(const struct ovsdb_idl_row *row)
 {
     return uuid_equals(&row->uuid, &index_row_uuid);
 }
@@ -2748,15 +2649,15 @@ is_index_row(struct ovsdb_idl_row *row)
  * Not intended for direct usage.
  */
 struct ovsdb_idl_row *
-ovsdb_idl_index_init_row(struct ovsdb_idl * idl,
-                         const struct ovsdb_idl_table_class *class)
+ovsdb_idl_index_init_row(struct ovsdb_idl_index *index)
 {
+    const struct ovsdb_idl_table_class *class = index->table->class_;
     struct ovsdb_idl_row *row = xzalloc(class->allocation_size);
     class->row_init(row);
     row->uuid = index_row_uuid;
     row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum);
     row->written = bitmap_allocate(class->n_columns);
-    row->table = ovsdb_idl_table_from_class(idl, class);
+    row->table = index->table;
     /* arcs are not used for index row, but it doesn't harm to initialize */
     ovs_list_init(&row->src_arcs);
     ovs_list_init(&row->dst_arcs);
@@ -2768,13 +2669,14 @@ ovsdb_idl_index_init_row(struct ovsdb_idl * idl,
  * generated by ovsdb-idlc.
  */
 void
-ovsdb_idl_index_destroy_row__(const struct ovsdb_idl_row *row_)
+ovsdb_idl_index_destroy_row(const struct ovsdb_idl_row *row_)
 {
     struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
     const struct ovsdb_idl_table_class *class = row->table->class_;
     const struct ovsdb_idl_column *c;
     size_t i;
 
+    ovs_assert(is_index_row(row_));
     ovs_assert(ovs_list_is_empty(&row_->src_arcs));
     ovs_assert(ovs_list_is_empty(&row_->dst_arcs));
     BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) {
@@ -2788,68 +2690,59 @@ ovsdb_idl_index_destroy_row__(const struct ovsdb_idl_row *row_)
     free(row);
 }
 
-/* Moves the cursor to the first entry in the index. Returns a pointer to the
- * corresponding ovsdb_idl_row, or NULL if the index list is empy.
- */
 struct ovsdb_idl_row *
-ovsdb_idl_index_first(struct ovsdb_idl_index_cursor *cursor)
+ovsdb_idl_index_find(struct ovsdb_idl_index *index,
+                     const struct ovsdb_idl_row *target)
 {
-    cursor->position = skiplist_first(cursor->index->skiplist);
-    return ovsdb_idl_index_data(cursor);
+    return skiplist_get_data(skiplist_find(index->skiplist, target));
 }
 
-/* Moves the cursor to the next record in the index list.
- */
-struct ovsdb_idl_row *
-ovsdb_idl_index_next(struct ovsdb_idl_index_cursor *cursor)
+struct ovsdb_idl_cursor
+ovsdb_idl_cursor_first(struct ovsdb_idl_index *index)
 {
-    if (!cursor->position) {
-        return NULL;
-    }
-    cursor->position = skiplist_next(cursor->position);
-    return ovsdb_idl_index_data(cursor);
- }
+    return (struct ovsdb_idl_cursor) { skiplist_first(index->skiplist) };
+}
 
-/* Returns the ovsdb_idl_row pointer corresponding to the record at the
- * current cursor location.
- */
-struct ovsdb_idl_row *
-ovsdb_idl_index_data(struct ovsdb_idl_index_cursor *cursor)
+struct ovsdb_idl_cursor
+ovsdb_idl_cursor_first_eq(struct ovsdb_idl_index *index,
+                          const struct ovsdb_idl_row *target)
 {
-    return skiplist_get_data(cursor->position);
+    return (struct ovsdb_idl_cursor) { skiplist_find(index->skiplist,
+                                                     target) };
 }
 
-/* Moves the cursor to the first entry in the index matching the specified
- * value. If 'value' is NULL, the cursor is moved to the last entry in the
- * list. Returns a pointer to the corresponding ovsdb_idl_row or NULL.
- */
-struct ovsdb_idl_row *
-ovsdb_idl_index_find(struct ovsdb_idl_index_cursor *cursor,
-                     struct ovsdb_idl_row *value)
+struct ovsdb_idl_cursor
+ovsdb_idl_cursor_first_ge(struct ovsdb_idl_index *index,
+                          const struct ovsdb_idl_row *target)
 {
-    if (value) {
-        cursor->position = skiplist_find(cursor->index->skiplist, value);
-    } else {
-        cursor->position = skiplist_first(cursor->index->skiplist);
-    }
-    return ovsdb_idl_index_data(cursor);
+    struct skiplist_node *node = (target
+                                  ? skiplist_forward_to(index->skiplist,
+                                                        target)
+                                  : skiplist_first(index->skiplist));
+    return (struct ovsdb_idl_cursor) { node };
+}
+
+void
+ovsdb_idl_cursor_next(struct ovsdb_idl_cursor *cursor)
+{
+    cursor->position = skiplist_next(cursor->position);
+}
+
+void
+ovsdb_idl_cursor_next_eq(struct ovsdb_idl_cursor *cursor,
+                         struct ovsdb_idl_index *index)
+{
+    struct ovsdb_idl_row *data = skiplist_get_data(cursor->position);
+    struct skiplist_node *next_position = skiplist_next(cursor->position);
+    struct ovsdb_idl_row *next_data = skiplist_get_data(next_position);
+    cursor->position = (!ovsdb_idl_index_compare(index, data, next_data)
+                        ? next_position : NULL);
 }
 
-/* Moves the cursor to the first entry in the index with a value greater than
- * or equal to the given value. If 'value' is NULL, the cursor is moved to the
- * first entry in the index.  Returns a pointer to the corresponding
- * ovsdb_idl_row or NULL if such a row does not exist.
- */
 struct ovsdb_idl_row *
-ovsdb_idl_index_forward_to(struct ovsdb_idl_index_cursor *cursor,
-                           struct ovsdb_idl_row *value)
+ovsdb_idl_cursor_data(struct ovsdb_idl_cursor *cursor)
 {
-    if (value) {
-        cursor->position = skiplist_forward_to(cursor->index->skiplist, value);
-    } else {
-        cursor->position = skiplist_first(cursor->index->skiplist);
-    }
-    return ovsdb_idl_index_data(cursor);
+    return skiplist_get_data(cursor->position);
 }
 
 /* Returns the result of comparing two rows using the comparison function
@@ -2862,11 +2755,12 @@ ovsdb_idl_index_forward_to(struct ovsdb_idl_index_cursor *cursor,
  * greater than any other value, and NULL == NULL.
  */
 int
-ovsdb_idl_index_compare(struct ovsdb_idl_index_cursor *cursor,
-                        struct ovsdb_idl_row *a, struct ovsdb_idl_row *b)
+ovsdb_idl_index_compare(struct ovsdb_idl_index *index,
+                        const struct ovsdb_idl_row *a,
+                        const struct ovsdb_idl_row *b)
 {
     if (a && b) {
-        return ovsdb_idl_index_generic_comparer(a, b, cursor->index);
+        return ovsdb_idl_index_generic_comparer(a, b, index);
     } else if (!a && !b) {
         return 0;
     } else if (a) {
diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
index 2f5655227ac1..2c711f4f6d94 100644
--- a/lib/ovsdb-idl.h
+++ b/lib/ovsdb-idl.h
@@ -366,55 +366,57 @@ unsigned int ovsdb_idl_set_condition(struct ovsdb_idl *,
                                      const struct ovsdb_idl_condition *);
 
 unsigned int ovsdb_idl_get_condition_seqno(const struct ovsdb_idl *);
+
+/* Indexes over one or more columns in the IDL, to retrieve rows matching
+ * particular search criteria and to iterate over a subset of rows in a defined
+ * order. */
 
-/* The OVSDB-IDL Compound Indexes feature allows for the creation of custom
- * table indexes over one or more columns in the IDL. These indexes provide
- * the ability to retrieve rows matching a particular search criteria and to
- * iterate over a subset of rows in a defined order.
- */
+enum ovsdb_index_order {
+    OVSDB_INDEX_ASC,            /* 0, 1, 2, ... */
+    OVSDB_INDEX_DESC            /* 2, 1, 0, ... */
+};
 
-#define OVSDB_INDEX_DESC -1
-#define OVSDB_INDEX_ASC 1
+typedef int column_comparator_func(const void *a, const void *b);
 
-/*
- * Skiplist comparison function. Allows to store sorted data.
- */
-typedef int (column_comparator)(const void *a, const void *b);
+struct ovsdb_idl_index_column {
+    const struct ovsdb_idl_column *column;
+    column_comparator_func *comparer;
+    enum ovsdb_index_order order;
+};
 
-struct ovsdb_idl_index_cursor {
-    struct ovsdb_idl_index *index;    /* Index used by this cursor */
+/* Creating an index. */
+struct ovsdb_idl_index *ovsdb_idl_index_create(
+    struct ovsdb_idl *, const struct ovsdb_idl_index_column *, size_t n);
+struct ovsdb_idl_index *ovsdb_idl_index_create1(
+    struct ovsdb_idl *, const struct ovsdb_idl_column *);
+struct ovsdb_idl_index *ovsdb_idl_index_create2(
+    struct ovsdb_idl *, const struct ovsdb_idl_column *,
+    const struct ovsdb_idl_column *);
+
+/* Searching an index. */
+struct ovsdb_idl_row *ovsdb_idl_index_find(struct ovsdb_idl_index *,
+                                           const struct ovsdb_idl_row *);
+
+/* Iteration over an index.
+ *
+ * Usually these would be invoked through table-specific wrappers generated
+ * by the IDL. */
+
+struct ovsdb_idl_cursor {
     struct skiplist_node *position;   /* Current position in the index */
 };
 
-struct ovsdb_idl_index *ovsdb_idl_create_index(struct ovsdb_idl *idl,
-                                        const struct ovsdb_idl_table_class *tc,
-                                        const char *index_name);
-void ovsdb_idl_index_add_column(struct ovsdb_idl_index *,
-                                const struct ovsdb_idl_column *,
-                                int order,
-                                column_comparator *custom_comparer);
-bool ovsdb_idl_initialize_cursor(struct ovsdb_idl *,
-                            const struct ovsdb_idl_table_class *tc,
-                            const char *index_name,
-                            struct ovsdb_idl_index_cursor *cursor);
-void ovsdb_idl_index_write_(struct ovsdb_idl_row *,
-                            const struct ovsdb_idl_column *,
-                            struct ovsdb_datum *,
-                            const struct ovsdb_idl_table_class *);
-struct ovsdb_idl_row *ovsdb_idl_index_init_row(struct ovsdb_idl *,
-                                       const struct ovsdb_idl_table_class *);
-void ovsdb_idl_index_destroy_row__(const struct ovsdb_idl_row *);
-struct ovsdb_idl_row *ovsdb_idl_index_first(struct ovsdb_idl_index_cursor *);
-struct ovsdb_idl_row *ovsdb_idl_index_next(struct ovsdb_idl_index_cursor *);
-struct ovsdb_idl_row *ovsdb_idl_index_data(struct ovsdb_idl_index_cursor *);
-struct ovsdb_idl_row *ovsdb_idl_index_find(struct ovsdb_idl_index_cursor *,
-                                           struct ovsdb_idl_row *);
-struct ovsdb_idl_row *ovsdb_idl_index_forward_to(
-                                               struct ovsdb_idl_index_cursor *,
-                                               struct ovsdb_idl_row *);
-int ovsdb_idl_index_compare(struct ovsdb_idl_index_cursor *,
-                            struct ovsdb_idl_row *a,
-                            struct ovsdb_idl_row *b);
+struct ovsdb_idl_cursor ovsdb_idl_cursor_first(struct ovsdb_idl_index *);
+struct ovsdb_idl_cursor ovsdb_idl_cursor_first_eq(
+    struct ovsdb_idl_index *, const struct ovsdb_idl_row *);
+struct ovsdb_idl_cursor ovsdb_idl_cursor_first_ge(
+    struct ovsdb_idl_index *, const struct ovsdb_idl_row *);
+
+void ovsdb_idl_cursor_next(struct ovsdb_idl_cursor *);
+void ovsdb_idl_cursor_next_eq(struct ovsdb_idl_cursor *,
+                              struct ovsdb_idl_index *);
+
+struct ovsdb_idl_row *ovsdb_idl_cursor_data(struct ovsdb_idl_cursor *);
 
 #ifdef __cplusplus
 }
diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
index c6c6cbaaacb9..1b78b6114658 100644
--- a/ovn/controller/bfd.c
+++ b/ovn/controller/bfd.c
@@ -106,11 +106,11 @@ struct local_datapath_node {
 };
 
 static void
-bfd_travel_gw_related_chassis(const struct local_datapath *dp,
-                              const struct hmap *local_datapaths,
-                              struct ovsdb_idl_index_cursor *cursor,
-                              struct sbrec_port_binding *lpval,
-                              struct sset *bfd_chassis)
+bfd_travel_gw_related_chassis(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    const struct local_datapath *dp,
+    const struct hmap *local_datapaths,
+    struct sset *bfd_chassis)
 {
     struct ovs_list dp_list;
     const struct sbrec_port_binding *pb;
@@ -131,12 +131,12 @@ bfd_travel_gw_related_chassis(const struct local_datapath *dp,
     dp_binding->dp = dp;
     ovs_list_push_back(&dp_list, &dp_binding->node);
 
-    /*
-     * Go through whole graph to figure out all chassis which may deliver
+    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_datapath);
+
+    /* Go through whole graph to figure out all chassis which may deliver
      * packets to gateway. */
-    do {
-        dp_binding = CONTAINER_OF(ovs_list_pop_front(&dp_list),
-                                  struct local_datapath_node, node);
+    LIST_FOR_EACH_POP (dp_binding, node, &dp_list) {
         dp = dp_binding->dp;
         free(dp_binding);
         for (size_t i = 0; i < dp->n_peer_dps; i++) {
@@ -167,8 +167,9 @@ bfd_travel_gw_related_chassis(const struct local_datapath *dp,
                                           hmap_node);
             ovs_list_push_back(&dp_list, &dp_binding->node);
 
-            sbrec_port_binding_index_set_datapath(lpval, pdp);
-            SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, cursor, lpval) {
+            sbrec_port_binding_index_set_datapath(target, pdp);
+            SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                               sbrec_port_binding_by_datapath) {
                 if (pb->chassis) {
                     const char *chassis_name = pb->chassis->name;
                     if (chassis_name) {
@@ -177,17 +178,52 @@ bfd_travel_gw_related_chassis(const struct local_datapath *dp,
                 }
             }
         }
-    } while (!ovs_list_is_empty(&dp_list));
+    }
+    sbrec_port_binding_index_destroy_row(target);
 
     sset_destroy(&visited_dp);
 }
 
+static struct ovs_list *
+bfd_find_ha_gateway_chassis(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    const struct chassis_index *chassis_index,
+    const struct sbrec_datapath_binding *datapath)
+{
+    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_datapath);
+    sbrec_port_binding_index_set_datapath(target, datapath);
+
+    struct ovs_list *ha_gateway_chassis = NULL;
+    const struct sbrec_port_binding *pb;
+    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                       sbrec_port_binding_by_datapath) {
+        if (strcmp(pb->type, "chassisredirect")) {
+            continue;
+        }
+
+        struct ovs_list *gateway_chassis = gateway_chassis_get_ordered(
+            pb, chassis_index);
+        if (!gateway_chassis || ovs_list_is_short(gateway_chassis)) {
+            /* We don't need BFD for non-HA chassisredirect. */
+            gateway_chassis_destroy(gateway_chassis);
+            continue;
+        }
+
+        ha_gateway_chassis = gateway_chassis;
+        break;
+    }
+    sbrec_port_binding_index_destroy_row(target);
+    return ha_gateway_chassis;
+}
+
 static void
-bfd_calculate_chassis(struct controller_ctx *ctx,
-                      const struct sbrec_chassis *our_chassis,
-                      const struct hmap *local_datapaths,
-                      const struct chassis_index *chassis_index,
-                      struct sset *bfd_chassis)
+bfd_calculate_chassis(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    const struct sbrec_chassis *our_chassis,
+    const struct hmap *local_datapaths,
+    const struct chassis_index *chassis_index,
+    struct sset *bfd_chassis)
 {
     /* Identify all chassis nodes to which we need to enable bfd.
      * 1) Any chassis hosting the chassisredirect ports for known
@@ -196,57 +232,36 @@ bfd_calculate_chassis(struct controller_ctx *ctx,
      *    to a router datapath  when our chassis is hosting a router
      *    with a chassis redirect port. */
 
-    const struct sbrec_port_binding *pb;
-    struct ovsdb_idl_index_cursor cursor;
-    struct sbrec_port_binding *lpval;
     const struct local_datapath *dp;
-
-    ovsdb_idl_initialize_cursor(ctx->ovnsb_idl,
-                                &sbrec_table_port_binding,
-                                "lport-by-datapath", &cursor);
-    lpval = sbrec_port_binding_index_init_row(ctx->ovnsb_idl,
-                                              &sbrec_table_port_binding);
-
     HMAP_FOR_EACH (dp, hmap_node, local_datapaths) {
         const char *is_router = smap_get(&dp->datapath->external_ids,
                                          "logical-router");
         bool our_chassis_is_gw_for_dp = false;
         if (is_router) {
-            sbrec_port_binding_index_set_datapath(lpval, dp->datapath);
-            SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) {
-                if (!strcmp(pb->type, "chassisredirect")) {
-                    struct ovs_list *gateway_chassis = NULL;
-                    gateway_chassis =
-                        gateway_chassis_get_ordered(pb, chassis_index);
-                    /* we don't need BFD for non-HA  chassisredirect */
-                    if (!gateway_chassis ||
-                        ovs_list_is_short(gateway_chassis)) {
-                        gateway_chassis_destroy(gateway_chassis);
-                        continue;
-                    }
-                    our_chassis_is_gw_for_dp = gateway_chassis_contains(
-                            gateway_chassis, our_chassis);
-                    struct gateway_chassis *gwc;
-                    LIST_FOR_EACH (gwc, node, gateway_chassis) {
-                        if (gwc->db->chassis) {
-                            sset_add(bfd_chassis, gwc->db->chassis->name);
-                        }
+            struct ovs_list *ha_gateway_chassis
+                = bfd_find_ha_gateway_chassis(sbrec_port_binding_by_datapath,
+                                              chassis_index, dp->datapath);
+            if (ha_gateway_chassis) {
+                our_chassis_is_gw_for_dp = gateway_chassis_contains(
+                    ha_gateway_chassis, our_chassis);
+                struct gateway_chassis *gwc;
+                LIST_FOR_EACH (gwc, node, ha_gateway_chassis) {
+                    if (gwc->db->chassis) {
+                        sset_add(bfd_chassis, gwc->db->chassis->name);
                     }
-                    gateway_chassis_destroy(gateway_chassis);
-                    break;
                 }
+                gateway_chassis_destroy(ha_gateway_chassis);
             }
         }
         if (our_chassis_is_gw_for_dp) {
-            bfd_travel_gw_related_chassis(dp, local_datapaths, &cursor,
-                                          lpval, bfd_chassis);
+            bfd_travel_gw_related_chassis(sbrec_port_binding_by_datapath,
+                                          dp, local_datapaths, bfd_chassis);
         }
     }
-    sbrec_port_binding_index_destroy_row(lpval);
 }
 
 void
-bfd_run(struct controller_ctx *ctx,
+bfd_run(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
         const struct ovsrec_interface_table *interface_table,
         const struct ovsrec_bridge *br_int,
         const struct sbrec_chassis *chassis_rec,
@@ -258,7 +273,8 @@ bfd_run(struct controller_ctx *ctx,
         return;
     }
     struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
-    bfd_calculate_chassis(ctx, chassis_rec, local_datapaths, chassis_index,
+    bfd_calculate_chassis(sbrec_port_binding_by_datapath,
+                          chassis_rec, local_datapaths, chassis_index,
                           &bfd_chassis);
     /* Identify tunnels ports(connected to remote chassis id) to enable bfd */
     struct sset tunnels = SSET_INITIALIZER(&tunnels);
diff --git a/ovn/controller/bfd.h b/ovn/controller/bfd.h
index 240cf2d6bde5..a453976d50d4 100644
--- a/ovn/controller/bfd.h
+++ b/ovn/controller/bfd.h
@@ -20,13 +20,14 @@ struct chassis_index;
 struct controller_ctx;
 struct hmap;
 struct ovsdb_idl;
+struct ovsdb_idl_index;
 struct ovsrec_bridge;
 struct ovsrec_interface_table;
 struct sbrec_chassis;
 struct sset;
 
 void bfd_register_ovs_idl(struct ovsdb_idl *);
-void bfd_run(struct controller_ctx *ctx,
+void bfd_run(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
              const struct ovsrec_interface_table *interface_table,
              const struct ovsrec_bridge *br_int,
              const struct sbrec_chassis *chassis_rec,
diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index a9a50dc68132..b5e8c8ed4db4 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -110,16 +110,14 @@ get_local_iface_ids(const struct ovsrec_bridge *br_int,
 }
 
 static void
-add_local_datapath__(struct controller_ctx *ctx,
+add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                     struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
                      const struct sbrec_datapath_binding *datapath,
                      bool has_local_l3gateway, int depth,
                      struct hmap *local_datapaths)
 {
     uint32_t dp_key = datapath->tunnel_key;
-    const struct sbrec_port_binding *pb;
-    struct ovsdb_idl_index_cursor cursor;
-    struct sbrec_port_binding *lpval;
-
     struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key);
     if (ld) {
         if (has_local_l3gateway) {
@@ -140,44 +138,52 @@ add_local_datapath__(struct controller_ctx *ctx,
         return;
     }
 
-    /* Recursively add logical datapaths to which this one patches. */
-    lpval = sbrec_port_binding_index_init_row(ctx->ovnsb_idl,
-                                              &sbrec_table_port_binding);
-    sbrec_port_binding_index_set_datapath(lpval, datapath);
-    ovsdb_idl_initialize_cursor(ctx->ovnsb_idl, &sbrec_table_port_binding,
-                                "lport-by-datapath", &cursor);
+    struct sbrec_port_binding *target =
+        sbrec_port_binding_index_init_row(sbrec_port_binding_by_datapath);
+    sbrec_port_binding_index_set_datapath(target, datapath);
 
-    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) {
+    const struct sbrec_port_binding *pb;
+    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                       sbrec_port_binding_by_datapath) {
         if (!strcmp(pb->type, "patch")) {
             const char *peer_name = smap_get(&pb->options, "peer");
             if (peer_name) {
                 const struct sbrec_port_binding *peer;
 
-                peer = lport_lookup_by_name( ctx->ovnsb_idl, peer_name);
+                peer = lport_lookup_by_name(sbrec_port_binding_by_name,
+                                            peer_name);
 
                 if (peer && peer->datapath) {
-                    add_local_datapath__(ctx, peer->datapath,
-                                         false, depth + 1, local_datapaths);
+                    add_local_datapath__(sbrec_datapath_binding_by_key,
+                                         sbrec_port_binding_by_datapath,
+                                         sbrec_port_binding_by_name,
+                                         peer->datapath, false,
+                                         depth + 1, local_datapaths);
                     ld->n_peer_dps++;
                     ld->peer_dps = xrealloc(
                             ld->peer_dps,
                             ld->n_peer_dps * sizeof *ld->peer_dps);
                     ld->peer_dps[ld->n_peer_dps - 1] = datapath_lookup_by_key(
-                        ctx->ovnsb_idl, peer->datapath->tunnel_key);
+                        sbrec_datapath_binding_by_key,
+                        peer->datapath->tunnel_key);
                 }
             }
         }
     }
-    sbrec_port_binding_index_destroy_row(lpval);
+    sbrec_port_binding_index_destroy_row(target);
 }
 
 static void
-add_local_datapath(struct controller_ctx *ctx,
+add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                   struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                   struct ovsdb_idl_index *sbrec_port_binding_by_name,
                    const struct sbrec_datapath_binding *datapath,
                    bool has_local_l3gateway, struct hmap *local_datapaths)
 {
-    add_local_datapath__(ctx, datapath, has_local_l3gateway, 0,
-                         local_datapaths);
+    add_local_datapath__(sbrec_datapath_binding_by_key,
+                         sbrec_port_binding_by_datapath,
+                         sbrec_port_binding_by_name,
+                         datapath, has_local_l3gateway, 0, local_datapaths);
 }
 
 static void
@@ -387,6 +393,9 @@ update_local_lport_ids(struct sset *local_lport_ids,
 
 static void
 consider_local_datapath(struct controller_ctx *ctx,
+                        struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                        struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                        struct ovsdb_idl_index *sbrec_port_binding_by_name,
                         const struct chassis_index *chassis_index,
                         const struct sset *active_tunnels,
                         const struct sbrec_chassis *chassis_rec,
@@ -409,8 +418,10 @@ consider_local_datapath(struct controller_ctx *ctx,
             /* Add child logical port to the set of all local ports. */
             sset_add(local_lports, binding_rec->logical_port);
         }
-        add_local_datapath(ctx, binding_rec->datapath,
-                           false, local_datapaths);
+        add_local_datapath(sbrec_datapath_binding_by_key,
+                           sbrec_port_binding_by_datapath,
+                           sbrec_port_binding_by_name,
+                           binding_rec->datapath, false, local_datapaths);
         if (iface_rec && qos_map && ctx->ovs_idl_txn) {
             get_qos_params(binding_rec, qos_map);
         }
@@ -424,8 +435,10 @@ consider_local_datapath(struct controller_ctx *ctx,
         our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
         if (our_chassis) {
             sset_add(local_lports, binding_rec->logical_port);
-            add_local_datapath(ctx, binding_rec->datapath,
-                               false, local_datapaths);
+            add_local_datapath(sbrec_datapath_binding_by_key,
+                               sbrec_port_binding_by_datapath,
+                               sbrec_port_binding_by_name,
+                               binding_rec->datapath, false, local_datapaths);
         }
     } else if (!strcmp(binding_rec->type, "chassisredirect")) {
         gateway_chassis = gateway_chassis_get_ordered(binding_rec,
@@ -436,8 +449,10 @@ consider_local_datapath(struct controller_ctx *ctx,
             our_chassis = gateway_chassis_is_active(
                 gateway_chassis, chassis_rec, active_tunnels);
 
-            add_local_datapath(ctx, binding_rec->datapath,
-                               false, local_datapaths);
+            add_local_datapath(sbrec_datapath_binding_by_key,
+                               sbrec_port_binding_by_datapath,
+                               sbrec_port_binding_by_name,
+                               binding_rec->datapath, false, local_datapaths);
         }
         gateway_chassis_destroy(gateway_chassis);
     } else if (!strcmp(binding_rec->type, "l3gateway")) {
@@ -445,8 +460,10 @@ consider_local_datapath(struct controller_ctx *ctx,
                                           "l3gateway-chassis");
         our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
         if (our_chassis) {
-            add_local_datapath(ctx, binding_rec->datapath,
-                               true, local_datapaths);
+            add_local_datapath(sbrec_datapath_binding_by_key,
+                               sbrec_port_binding_by_datapath,
+                               sbrec_port_binding_by_name,
+                               binding_rec->datapath, true, local_datapaths);
         }
     } else if (!strcmp(binding_rec->type, "localnet")) {
         /* Add all localnet ports to local_lports so that we allocate ct zones
@@ -529,6 +546,9 @@ consider_localnet_port(const struct sbrec_port_binding *binding_rec,
 
 void
 binding_run(struct controller_ctx *ctx,
+            struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+            struct ovsdb_idl_index *sbrec_port_binding_by_name,
             const struct ovsrec_port_table *port_table,
             const struct ovsrec_qos_table *qos_table,
             const struct sbrec_port_binding_table *port_binding_table,
@@ -558,7 +578,9 @@ binding_run(struct controller_ctx *ctx,
      * chassis and update the binding accordingly.  This includes both
      * directly connected logical ports and children of those ports. */
     SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
-        consider_local_datapath(ctx, chassis_index,
+        consider_local_datapath(ctx, sbrec_datapath_binding_by_key,
+                                sbrec_port_binding_by_datapath,
+                                sbrec_port_binding_by_name, chassis_index,
                                 active_tunnels, chassis_rec, binding_rec,
                                 sset_is_empty(&egress_ifaces) ? NULL :
                                 &qos_map, local_datapaths, &lport_to_iface,
diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
index 0f8a3a19bcf9..1759b70de2e3 100644
--- a/ovn/controller/binding.h
+++ b/ovn/controller/binding.h
@@ -23,6 +23,7 @@ struct controller_ctx;
 struct chassis_index;
 struct hmap;
 struct ovsdb_idl;
+struct ovsdb_idl_index;
 struct ovsrec_bridge;
 struct ovsrec_port_table;
 struct ovsrec_qos_table;
@@ -32,6 +33,9 @@ struct sset;
 
 void binding_register_ovs_idl(struct ovsdb_idl *);
 void binding_run(struct controller_ctx *,
+                 struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
                  const struct ovsrec_port_table *,
                  const struct ovsrec_qos_table *,
                  const struct sbrec_port_binding_table *,
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index 86aac2a4731a..434adf3b8428 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -50,33 +50,36 @@ lflow_init(void)
 }
 
 struct lookup_port_aux {
-    struct ovsdb_idl *ovnsb_idl;
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
+    struct ovsdb_idl_index *sbrec_port_binding_by_name;
     const struct sbrec_datapath_binding *dp;
 };
 
 struct condition_aux {
-    struct ovsdb_idl *ovnsb_idl;
+    struct ovsdb_idl_index *sbrec_port_binding_by_name;
     const struct sbrec_chassis *chassis;
     const struct sset *active_tunnels;
     const struct chassis_index *chassis_index;
 };
 
-static void consider_logical_flow(struct controller_ctx *ctx,
-                                  const struct chassis_index *chassis_index,
-                                  const struct sbrec_logical_flow *lflow,
-                                  const struct hmap *local_datapaths,
-                                  const struct sbrec_chassis *chassis,
-                                  struct hmap *dhcp_opts,
-                                  struct hmap *dhcpv6_opts,
-                                  struct hmap *nd_ra_opts,
-                                  uint32_t *conj_id_ofs,
-                                  const struct shash *addr_sets,
-                                  const struct shash *port_groups,
-                                  const struct sset *active_tunnels,
-                                  const struct sset *local_lport_ids,
-                                  struct hmap *flow_table,
-                                  struct ovn_extend_table *group_table,
-                                  struct ovn_extend_table *meter_table);
+static void consider_logical_flow(
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct chassis_index *,
+    const struct sbrec_logical_flow *,
+    const struct hmap *local_datapaths,
+    const struct sbrec_chassis *,
+    struct hmap *dhcp_opts,
+    struct hmap *dhcpv6_opts,
+    struct hmap *nd_ra_opts,
+    uint32_t *conj_id_ofs,
+    const struct shash *addr_sets,
+    const struct shash *port_groups,
+    const struct sset *active_tunnels,
+    const struct sset *local_lport_ids,
+    struct hmap *flow_table,
+    struct ovn_extend_table *group_table,
+    struct ovn_extend_table *meter_table);
 
 static bool
 lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
@@ -84,14 +87,14 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
     const struct lookup_port_aux *aux = aux_;
 
     const struct sbrec_port_binding *pb
-        = lport_lookup_by_name(aux->ovnsb_idl, port_name);
+        = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
     if (pb && pb->datapath == aux->dp) {
         *portp = pb->tunnel_key;
         return true;
     }
 
-    const struct sbrec_multicast_group *mg
-        = mcgroup_lookup_by_dp_name(aux->ovnsb_idl, aux->dp, port_name);
+    const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
+        aux->sbrec_multicast_group_by_name_datapath, aux->dp, port_name);
     if (mg) {
         *portp = mg->tunnel_key;
         return true;
@@ -106,7 +109,7 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
     const struct condition_aux *c_aux = c_aux_;
 
     const struct sbrec_port_binding *pb
-        = lport_lookup_by_name(c_aux->ovnsb_idl, port_name);
+        = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
     if (!pb) {
         return false;
     }
@@ -138,7 +141,8 @@ is_switch(const struct sbrec_datapath_binding *ldp)
 /* Adds the logical flows from the Logical_Flow table to flow tables. */
 static void
 add_logical_flows(
-    struct controller_ctx *ctx,
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const struct sbrec_dhcp_options_table *dhcp_options_table,
     const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
     const struct sbrec_logical_flow_table *logical_flow_table,
@@ -176,7 +180,9 @@ add_logical_flows(
     nd_ra_opts_init(&nd_ra_opts);
 
     SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
-        consider_logical_flow(ctx, chassis_index, lflow, local_datapaths,
+        consider_logical_flow(sbrec_multicast_group_by_name_datapath,
+                              sbrec_port_binding_by_name,
+                              chassis_index, lflow, local_datapaths,
                               chassis, &dhcp_opts, &dhcpv6_opts, &nd_ra_opts,
                               &conj_id_ofs, addr_sets, port_groups,
                               active_tunnels, local_lport_ids,
@@ -189,22 +195,24 @@ add_logical_flows(
 }
 
 static void
-consider_logical_flow(struct controller_ctx *ctx,
-                      const struct chassis_index *chassis_index,
-                      const struct sbrec_logical_flow *lflow,
-                      const struct hmap *local_datapaths,
-                      const struct sbrec_chassis *chassis,
-                      struct hmap *dhcp_opts,
-                      struct hmap *dhcpv6_opts,
-                      struct hmap *nd_ra_opts,
-                      uint32_t *conj_id_ofs,
-                      const struct shash *addr_sets,
-                      const struct shash *port_groups,
-                      const struct sset *active_tunnels,
-                      const struct sset *local_lport_ids,
-                      struct hmap *flow_table,
-                      struct ovn_extend_table *group_table,
-                      struct ovn_extend_table *meter_table)
+consider_logical_flow(
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct chassis_index *chassis_index,
+    const struct sbrec_logical_flow *lflow,
+    const struct hmap *local_datapaths,
+    const struct sbrec_chassis *chassis,
+    struct hmap *dhcp_opts,
+    struct hmap *dhcpv6_opts,
+    struct hmap *nd_ra_opts,
+    uint32_t *conj_id_ofs,
+    const struct shash *addr_sets,
+    const struct shash *port_groups,
+    const struct sset *active_tunnels,
+    const struct sset *local_lport_ids,
+    struct hmap *flow_table,
+    struct ovn_extend_table *group_table,
+    struct ovn_extend_table *meter_table)
 {
     /* Determine translation of logical table IDs to physical table IDs. */
     bool ingress = !strcmp(lflow->pipeline, "ingress");
@@ -280,11 +288,17 @@ consider_logical_flow(struct controller_ctx *ctx,
     }
 
     struct lookup_port_aux aux = {
-        .ovnsb_idl = ctx->ovnsb_idl,
+        .sbrec_multicast_group_by_name_datapath
+            = sbrec_multicast_group_by_name_datapath,
+        .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
         .dp = lflow->logical_datapath
     };
-    struct condition_aux cond_aux = { ctx->ovnsb_idl, chassis, active_tunnels,
-                                      chassis_index};
+    struct condition_aux cond_aux = {
+        .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
+        .chassis = chassis,
+        .active_tunnels = active_tunnels,
+        .chassis_index = chassis_index
+    };
     expr = expr_simplify(expr, is_chassis_resident_cb, &cond_aux);
     expr = expr_normalize(expr);
     uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
@@ -381,12 +395,12 @@ put_load(const uint8_t *data, size_t len,
 }
 
 static void
-consider_neighbor_flow(struct controller_ctx *ctx,
+consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                        const struct sbrec_mac_binding *b,
                        struct hmap *flow_table)
 {
     const struct sbrec_port_binding *pb
-        = lport_lookup_by_name(ctx->ovnsb_idl, b->logical_port);
+        = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port);
     if (!pb) {
         return;
     }
@@ -432,20 +446,21 @@ consider_neighbor_flow(struct controller_ctx *ctx,
 /* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
  * southbound database. */
 static void
-add_neighbor_flows(struct controller_ctx *ctx,
+add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                    const struct sbrec_mac_binding_table *mac_binding_table,
                    struct hmap *flow_table)
 {
     const struct sbrec_mac_binding *b;
     SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
-        consider_neighbor_flow(ctx, b, flow_table);
+        consider_neighbor_flow(sbrec_port_binding_by_name, b, flow_table);
     }
 }
 
 /* Translates logical flows in the Logical_Flow table in the OVN_SB database
  * into OpenFlow flows.  See ovn-architecture(7) for more information. */
 void
-lflow_run(struct controller_ctx *ctx,
+lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+          struct ovsdb_idl_index *sbrec_port_binding_by_name,
           const struct sbrec_dhcp_options_table *dhcp_options_table,
           const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
           const struct sbrec_logical_flow_table *logical_flow_table,
@@ -463,11 +478,14 @@ lflow_run(struct controller_ctx *ctx,
 {
     COVERAGE_INC(lflow_run);
 
-    add_logical_flows(ctx, dhcp_options_table, dhcpv6_options_table,
-                      logical_flow_table, chassis_index, local_datapaths,
-                      chassis, addr_sets, port_groups, active_tunnels,
-                      local_lport_ids, flow_table, group_table, meter_table);
-    add_neighbor_flows(ctx, mac_binding_table, flow_table);
+    add_logical_flows(sbrec_multicast_group_by_name_datapath,
+                      sbrec_port_binding_by_name, dhcp_options_table,
+                      dhcpv6_options_table, logical_flow_table, chassis_index,
+                      local_datapaths, chassis, addr_sets, port_groups,
+                      active_tunnels, local_lport_ids, flow_table, group_table,
+                      meter_table);
+    add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
+                       flow_table);
 }
 
 void
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
index 7e081d72126a..feb59ee09d08 100644
--- a/ovn/controller/lflow.h
+++ b/ovn/controller/lflow.h
@@ -36,8 +36,8 @@
 #include <stdint.h>
 
 struct chassis_index;
-struct controller_ctx;
 struct ovn_extend_table;
+struct ovsdb_idl_index;
 struct hmap;
 struct sbrec_chassis;
 struct sbrec_dhcp_options_table;
@@ -66,7 +66,8 @@ struct uuid;
 #define LOG_PIPELINE_LEN 24
 
 void lflow_init(void);
-void lflow_run(struct controller_ctx *,
+void lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+               struct ovsdb_idl_index *sbrec_port_binding_by_name,
                const struct sbrec_dhcp_options_table *,
                const struct sbrec_dhcpv6_options_table *,
                const struct sbrec_logical_flow_table *,
diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
index e5305d0b0922..cc5c5fbb20e6 100644
--- a/ovn/controller/lport.c
+++ b/ovn/controller/lport.c
@@ -22,146 +22,81 @@
 #include "ovn/lib/ovn-sb-idl.h"
 VLOG_DEFINE_THIS_MODULE(lport);
 
-static struct ovsdb_idl_index_cursor lport_by_name_cursor;
-static struct ovsdb_idl_index_cursor lport_by_key_cursor;
-static struct ovsdb_idl_index_cursor dpath_by_key_cursor;
-static struct ovsdb_idl_index_cursor mc_grp_by_dp_name_cursor;
-
-
-
-/* Finds and returns the port binding record with the given 'name',
- * or NULL if no such port binding exists. */
 const struct sbrec_port_binding *
-lport_lookup_by_name(struct ovsdb_idl *idl, const char *name)
+lport_lookup_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                     const char *name)
 {
-    struct sbrec_port_binding *value;
-    const struct sbrec_port_binding *pb, *retval =  NULL;
-
-    /* Build key for an indexed lookup. */
-    value = sbrec_port_binding_index_init_row(idl, &sbrec_table_port_binding);
-    sbrec_port_binding_index_set_logical_port(value, name);
-
-    /* Find an entry with matching logical port name. Since this column is
-     * declared to be an index in the OVN_Southbound schema, the first match
-     * (if any) will be the only match. */
-    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &lport_by_name_cursor, value) {
-        retval = pb;
-        break;
-    }
-
-    sbrec_port_binding_index_destroy_row(value);
-
-    return retval;
-}
+    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_name);
+    sbrec_port_binding_index_set_logical_port(pb, name);
 
-/* Finds and returns the datapath binding record with tunnel_key equal to the
- * given 'dp_key', or NULL if no such datapath binding exists. */
-const struct sbrec_datapath_binding *
-datapath_lookup_by_key(struct ovsdb_idl *idl, uint64_t dp_key)
-{
-    struct sbrec_datapath_binding *dbval;
-    const struct sbrec_datapath_binding *db, *retval = NULL;
+    const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
+        sbrec_port_binding_by_name, pb);
 
-    /* Build key for an indexed lookup. */
-    dbval = sbrec_datapath_binding_index_init_row(idl,
-                                                &sbrec_table_datapath_binding);
-    sbrec_datapath_binding_index_set_tunnel_key(dbval, dp_key);
-
-    /* Find an entry with matching tunnel_key. Since this column is declared
-     * to be an index in the OVN_Southbound schema, the first match (if any)
-     * will be the only match. */
-    SBREC_DATAPATH_BINDING_FOR_EACH_EQUAL (db, &dpath_by_key_cursor, dbval) {
-        retval = db;
-        break;
-    }
-    sbrec_datapath_binding_index_destroy_row(dbval);
+    sbrec_port_binding_index_destroy_row(pb);
 
     return retval;
 }
 
-/* Finds and returns the port binding record with tunnel_key equal to the
- * given 'port_key' and datapath binding matching 'dp_key', or NULL if no
- * such port binding exists. */
 const struct sbrec_port_binding *
-lport_lookup_by_key(struct ovsdb_idl *idl, uint64_t dp_key, uint64_t port_key)
+lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
+                    uint64_t dp_key, uint64_t port_key)
 {
-    struct sbrec_port_binding *pbval;
-    const struct sbrec_port_binding *pb, *retval = NULL;
-    const struct sbrec_datapath_binding *db;
-
     /* Lookup datapath corresponding to dp_key. */
-    db = datapath_lookup_by_key(idl, dp_key);
+    const struct sbrec_datapath_binding *db = datapath_lookup_by_key(
+        sbrec_datapath_binding_by_key, dp_key);
     if (!db) {
         return NULL;
     }
 
     /* Build key for an indexed lookup. */
-    pbval = sbrec_port_binding_index_init_row(idl, &sbrec_table_port_binding);
-    sbrec_port_binding_index_set_datapath(pbval, db);
-    sbrec_port_binding_index_set_tunnel_key(pbval, port_key);
-
-    /* Find an entry with matching tunnel_key and datapath UUID. Since this
-     * column pair is declared to be an index in the OVN_Southbound schema,
-     * the first match (if any) will be the only match. */
-    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &lport_by_key_cursor, pbval) {
-        retval = pb;
-        break;
-    }
-    sbrec_port_binding_index_destroy_row(pbval);
+    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_key);
+    sbrec_port_binding_index_set_datapath(pb, db);
+    sbrec_port_binding_index_set_tunnel_key(pb, port_key);
+
+    const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
+        sbrec_port_binding_by_key, pb);
+
+    sbrec_port_binding_index_destroy_row(pb);
 
     return retval;
 }
-
-/* Finds and returns the logical multicast group with the given 'name' and
- * datapath binding, or NULL if no such logical multicast group exists. */
-const struct sbrec_multicast_group *
-mcgroup_lookup_by_dp_name(struct ovsdb_idl *idl,
-                           const struct sbrec_datapath_binding *dp,
-                           const char *name)
+
+const struct sbrec_datapath_binding *
+datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                       uint64_t dp_key)
 {
-    struct sbrec_multicast_group *mcval;
-    const struct sbrec_multicast_group *mc, *retval = NULL;
+    struct sbrec_datapath_binding *db = sbrec_datapath_binding_index_init_row(
+        sbrec_datapath_binding_by_key);
+    sbrec_datapath_binding_index_set_tunnel_key(db, dp_key);
 
-    /* Build key for an indexed lookup. */
-    mcval = sbrec_multicast_group_index_init_row(idl,
-                                                 &sbrec_table_multicast_group);
-    sbrec_multicast_group_index_set_name(mcval, name);
-    sbrec_multicast_group_index_set_datapath(mcval, dp);
-
-    /* Find an entry with matching logical multicast group name and datapath.
-     * Since this column pair is declared to be an index in the OVN_Southbound
-     * schema, the first match (if any) will be the only match. */
-    SBREC_MULTICAST_GROUP_FOR_EACH_EQUAL (mc, &mc_grp_by_dp_name_cursor,
-                                          mcval) {
-        retval = mc;
-        break;
-    }
+    const struct sbrec_datapath_binding *retval
+        = sbrec_datapath_binding_index_find(sbrec_datapath_binding_by_key,
+                                            db);
 
-    sbrec_multicast_group_index_destroy_row(mcval);
+    sbrec_datapath_binding_index_destroy_row(db);
 
     return retval;
 }
 
-void
-lport_init(struct ovsdb_idl *idl)
+const struct sbrec_multicast_group *
+mcgroup_lookup_by_dp_name(
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+    const struct sbrec_datapath_binding *db, const char *name)
 {
-    /* Create a cursor for searching multicast group table by datapath
-     * and group name. */
-    ovsdb_idl_initialize_cursor(idl, &sbrec_table_multicast_group,
-                                "multicast-group-by-dp-name",
-                                &mc_grp_by_dp_name_cursor);
-
-    /* Create cursor to search port binding table by logical port name. */
-    ovsdb_idl_initialize_cursor(idl, &sbrec_table_port_binding,
-                                "lport-by-name",
-                                &lport_by_name_cursor);
-
-    /* Create cursor to search port binding table by logical port tunnel key
-     * and datapath uuid. */
-    ovsdb_idl_initialize_cursor(idl, &sbrec_table_port_binding, "lport-by-key",
-                                &lport_by_key_cursor);
-
-    /* Create cursor to search datapath binding table by tunnel key. */
-    ovsdb_idl_initialize_cursor(idl, &sbrec_table_datapath_binding,
-                                "dpath-by-key", &dpath_by_key_cursor);
+    /* Build key for an indexed lookup. */
+    struct sbrec_multicast_group *mc = sbrec_multicast_group_index_init_row(
+        sbrec_multicast_group_by_name_datapath);
+    sbrec_multicast_group_index_set_name(mc, name);
+    sbrec_multicast_group_index_set_datapath(mc, db);
+
+    const struct sbrec_multicast_group *retval
+        = sbrec_multicast_group_index_find(
+            sbrec_multicast_group_by_name_datapath, mc);
+
+    sbrec_multicast_group_index_destroy_row(mc);
+
+    return retval;
 }
diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
index 38c7344dc221..7dcd5bee0aa6 100644
--- a/ovn/controller/lport.h
+++ b/ovn/controller/lport.h
@@ -17,13 +17,11 @@
 #define OVN_LPORT_H 1
 
 #include <stdint.h>
-#include "lib/uuid.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
 
-struct ovsdb_idl;
+struct ovsdb_idl_index;
 struct sbrec_chassis;
 struct sbrec_datapath_binding;
+struct sbrec_multicast_group;
 struct sbrec_port_binding;
 
 
@@ -34,22 +32,21 @@ struct sbrec_port_binding;
  * look up data based on values of its fields.  It's not that smart (yet), so
  * instead we define our own indexes.
  */
-
-
-const struct sbrec_datapath_binding *datapath_lookup_by_key(struct ovsdb_idl *,
-                                                            uint64_t dp_key);
 
 const struct sbrec_port_binding *lport_lookup_by_name(
-    struct ovsdb_idl *, const char *name);
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const char *name);
+
 const struct sbrec_port_binding *lport_lookup_by_key(
-    struct ovsdb_idl *, uint64_t dp_key, uint64_t port_key);
-
+    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+    struct ovsdb_idl_index *sbrec_port_binding_by_key,
+    uint64_t dp_key, uint64_t port_key);
 
-const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
-    struct ovsdb_idl *idl,
-    const struct sbrec_datapath_binding *,
-    const char *name);
+const struct sbrec_datapath_binding *datapath_lookup_by_key(
+    struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t dp_key);
 
-void lport_init(struct ovsdb_idl *idl);
+const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
+    const struct sbrec_datapath_binding *, const char *name);
 
 #endif /* ovn/lport.h */
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 30f6fbb892c2..4ec1de3976ee 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -556,46 +556,6 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
     physical_register_ovs_idl(ovs_idl);
 }
 
-static void
-create_ovnsb_indexes(struct ovsdb_idl *ovnsb_idl)
-{
-    struct ovsdb_idl_index *index;
-
-    /* Index multicast group table by name and datapath. */
-    index = ovsdb_idl_create_index(ovnsb_idl, &sbrec_table_multicast_group,
-                                   "multicast-group-by-dp-name");
-    ovsdb_idl_index_add_column(index, &sbrec_multicast_group_col_name,
-                               OVSDB_INDEX_ASC, NULL);
-    ovsdb_idl_index_add_column(index, &sbrec_multicast_group_col_datapath,
-                               OVSDB_INDEX_ASC, NULL);
-
-    /* Index logical port table by name. */
-    index = ovsdb_idl_create_index(ovnsb_idl, &sbrec_table_port_binding,
-                                   "lport-by-name");
-    ovsdb_idl_index_add_column(index, &sbrec_port_binding_col_logical_port,
-                               OVSDB_INDEX_ASC, NULL);
-
-    /* Index logical port table by tunnel key and datapath. */
-    index = ovsdb_idl_create_index(ovnsb_idl, &sbrec_table_port_binding,
-                                   "lport-by-key");
-    ovsdb_idl_index_add_column(index, &sbrec_port_binding_col_tunnel_key,
-                               OVSDB_INDEX_ASC, NULL);
-    ovsdb_idl_index_add_column(index, &sbrec_port_binding_col_datapath,
-                               OVSDB_INDEX_ASC, NULL);
-
-    /* Index logical port table by datapath. */
-    index = ovsdb_idl_create_index(ovnsb_idl, &sbrec_table_port_binding,
-                                   "lport-by-datapath");
-    ovsdb_idl_index_add_column(index, &sbrec_port_binding_col_datapath,
-                               OVSDB_INDEX_ASC, NULL);
-
-    /* Index datapath binding table by tunnel key. */
-    index = ovsdb_idl_create_index(ovnsb_idl, &sbrec_table_datapath_binding,
-                                   "dpath-by-key");
-    ovsdb_idl_index_add_column(index, &sbrec_datapath_binding_col_tunnel_key,
-                               OVSDB_INDEX_ASC, NULL);
-}
-
 int
 main(int argc, char *argv[])
 {
@@ -643,8 +603,23 @@ main(int argc, char *argv[])
         ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
     ovsdb_idl_set_leader_only(ovnsb_idl_loop.idl, false);
 
-    create_ovnsb_indexes(ovnsb_idl_loop.idl);
-    lport_init(ovnsb_idl_loop.idl);
+    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
+        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
+                                  &sbrec_multicast_group_col_name,
+                                  &sbrec_multicast_group_col_datapath);
+    struct ovsdb_idl_index *sbrec_port_binding_by_name
+        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
+                                  &sbrec_port_binding_col_logical_port);
+    struct ovsdb_idl_index *sbrec_port_binding_by_key
+        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
+                                  &sbrec_port_binding_col_tunnel_key,
+                                  &sbrec_port_binding_col_datapath);
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath
+        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
+                                  &sbrec_port_binding_col_datapath);
+    struct ovsdb_idl_index *sbrec_datapath_binding_by_key
+        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
+                                  &sbrec_datapath_binding_col_tunnel_key);
 
     ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
     update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
@@ -727,6 +702,9 @@ main(int argc, char *argv[])
                        sbrec_chassis_table_get(ctx.ovnsb_idl), chassis_id);
             bfd_calculate_active_tunnels(br_int, &active_tunnels);
             binding_run(&ctx,
+                        sbrec_datapath_binding_by_key,
+                        sbrec_port_binding_by_datapath,
+                        sbrec_port_binding_by_name,
                         ovsrec_port_table_get(ctx.ovs_idl),
                         ovsrec_qos_table_get(ctx.ovs_idl),
                         sbrec_port_binding_table_get(ctx.ovnsb_idl),
@@ -752,7 +730,12 @@ main(int argc, char *argv[])
             enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int,
                                                          &pending_ct_zones);
 
-            pinctrl_run(&ctx, sbrec_dns_table_get(ctx.ovnsb_idl),
+            pinctrl_run(&ctx,
+                        sbrec_datapath_binding_by_key,
+                        sbrec_port_binding_by_datapath,
+                        sbrec_port_binding_by_key,
+                        sbrec_port_binding_by_name,
+                        sbrec_dns_table_get(ctx.ovnsb_idl),
                         sbrec_mac_binding_table_get(ctx.ovnsb_idl),
                         br_int, chassis, &chassis_index,
                         &local_datapaths, &active_tunnels);
@@ -766,7 +749,8 @@ main(int argc, char *argv[])
                     commit_ct_zones(br_int, &pending_ct_zones);
 
                     struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
-                    lflow_run(&ctx,
+                    lflow_run(sbrec_multicast_group_by_name_datapath,
+                              sbrec_port_binding_by_name,
                               sbrec_dhcp_options_table_get(ctx.ovnsb_idl),
                               sbrec_dhcpv6_options_table_get(ctx.ovnsb_idl),
                               sbrec_logical_flow_table_get(ctx.ovnsb_idl),
@@ -777,13 +761,13 @@ main(int argc, char *argv[])
                               &flow_table, &group_table, &meter_table);
 
                     if (chassis_id) {
-                        bfd_run(&ctx,
+                        bfd_run(sbrec_port_binding_by_datapath,
                                 ovsrec_interface_table_get(ctx.ovs_idl),
                                 br_int, chassis, &local_datapaths,
                                 &chassis_index);
                     }
                     physical_run(
-                        &ctx,
+                        sbrec_port_binding_by_name,
                         sbrec_multicast_group_table_get(ctx.ovnsb_idl),
                         sbrec_port_binding_table_get(ctx.ovnsb_idl),
                         mff_ovn_geneve,
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index 2efe73219d89..eb2e35dafa6e 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -291,7 +291,7 @@ load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
 }
 
 static void
-consider_port_binding(struct controller_ctx *ctx,
+consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                       enum mf_field_id mff_ovn_geneve,
                       const struct simap *ct_zones,
                       const struct chassis_index *chassis_index,
@@ -318,7 +318,7 @@ consider_port_binding(struct controller_ctx *ctx,
         }
 
         const struct sbrec_port_binding *peer = lport_lookup_by_name(
-            ctx->ovnsb_idl, peer_name);
+            sbrec_port_binding_by_name, peer_name);
         if (!peer || strcmp(peer->type, binding->type)) {
             return;
         }
@@ -385,7 +385,8 @@ consider_port_binding(struct controller_ctx *ctx,
         const char *distributed_port = smap_get_def(&binding->options,
                                                     "distributed-port", "");
         const struct sbrec_port_binding *distributed_binding
-            = lport_lookup_by_name(ctx->ovnsb_idl, distributed_port);
+            = lport_lookup_by_name(sbrec_port_binding_by_name,
+                                   distributed_port);
 
         if (!distributed_binding) {
             /* Packet will be dropped. */
@@ -867,7 +868,7 @@ update_ofports(struct simap *old, struct simap *new)
 }
 
 void
-physical_run(struct controller_ctx *ctx,
+physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
              const struct sbrec_multicast_group_table *multicast_group_table,
              const struct sbrec_port_binding_table *port_binding_table,
              enum mf_field_id mff_ovn_geneve,
@@ -880,7 +881,6 @@ physical_run(struct controller_ctx *ctx,
              const struct sset *active_tunnels,
              struct hmap *flow_table)
 {
-
     /* This bool tracks physical mapping changes. */
     bool physical_map_changed = false;
 
@@ -999,7 +999,8 @@ physical_run(struct controller_ctx *ctx,
      * 64 for logical-to-physical translation. */
     const struct sbrec_port_binding *binding;
     SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
-        consider_port_binding(ctx, mff_ovn_geneve, ct_zones,
+        consider_port_binding(sbrec_port_binding_by_name,
+                              mff_ovn_geneve, ct_zones,
                               chassis_index, active_tunnels,
                               local_datapaths, binding, chassis,
                               &ofpacts, flow_table);
@@ -1138,7 +1139,7 @@ physical_run(struct controller_ctx *ctx,
          * rule with higher priority for every localport in this
          * datapath. */
         const struct sbrec_port_binding *pb = lport_lookup_by_name(
-            ctx->ovnsb_idl, localport);
+            sbrec_port_binding_by_name, localport);
         if (pb && !strcmp(pb->type, "localport")) {
             match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, pb->tunnel_key);
             match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
index 288cefb2899b..4a070a7d0128 100644
--- a/ovn/controller/physical.h
+++ b/ovn/controller/physical.h
@@ -45,7 +45,7 @@ struct sset;
 #define OVN_GENEVE_LEN 4
 
 void physical_register_ovs_idl(struct ovsdb_idl *);
-void physical_run(struct controller_ctx *,
+void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                   const struct sbrec_multicast_group_table *,
                   const struct sbrec_port_binding_table *,
                   enum mf_field_id mff_ovn_geneve,
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index a6be76889a5b..dcb90a39c6bf 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -66,20 +66,25 @@ static void pinctrl_handle_put_mac_binding(const struct flow *md,
                                            bool is_arp);
 static void init_put_mac_bindings(void);
 static void destroy_put_mac_bindings(void);
-static void run_put_mac_bindings(struct controller_ctx *,
-                                 const struct sbrec_mac_binding_table *);
+static void run_put_mac_bindings(
+    struct controller_ctx *,
+    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+    struct ovsdb_idl_index *sbrec_port_binding_by_key,
+    const struct sbrec_mac_binding_table *);
 static void wait_put_mac_bindings(struct controller_ctx *);
 static void flush_put_mac_bindings(void);
 
 static void init_send_garps(void);
 static void destroy_send_garps(void);
 static void send_garp_wait(void);
-static void send_garp_run(struct controller_ctx *ctx,
-                          const struct ovsrec_bridge *,
-                          const struct sbrec_chassis *,
-                          const struct chassis_index *chassis_index,
-                          const struct hmap *local_datapaths,
-                          const struct sset *active_tunnels);
+static void send_garp_run(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct ovsrec_bridge *,
+    const struct sbrec_chassis *,
+    const struct chassis_index *chassis_index,
+    const struct hmap *local_datapaths,
+    const struct sset *active_tunnels);
 static void pinctrl_handle_nd_na(const struct flow *ip_flow,
                                  const struct match *md,
                                  struct ofpbuf *userdata,
@@ -96,8 +101,11 @@ static void pinctrl_handle_nd_ns(const struct flow *ip_flow,
 static void init_ipv6_ras(void);
 static void destroy_ipv6_ras(void);
 static void ipv6_ra_wait(void);
-static void send_ipv6_ras(const struct controller_ctx *,
-                          const struct hmap *local_datapaths);
+static void send_ipv6_ras(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct hmap *local_datapaths);
+;
 
 COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
 
@@ -1238,6 +1246,10 @@ pinctrl_recv(const struct sbrec_dns_table *dns_table,
 
 void
 pinctrl_run(struct controller_ctx *ctx,
+            struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+            struct ovsdb_idl_index *sbrec_port_binding_by_key,
+            struct ovsdb_idl_index *sbrec_port_binding_by_name,
             const struct sbrec_dns_table *dns_table,
             const struct sbrec_mac_binding_table *mac_binding_table,
             const struct ovsrec_bridge *br_int,
@@ -1280,10 +1292,13 @@ pinctrl_run(struct controller_ctx *ctx,
         ofpbuf_delete(msg);
     }
 
-    run_put_mac_bindings(ctx, mac_binding_table);
-    send_garp_run(ctx, br_int, chassis, chassis_index, local_datapaths,
-                  active_tunnels);
-    send_ipv6_ras(ctx, local_datapaths);
+    run_put_mac_bindings(ctx, sbrec_datapath_binding_by_key,
+                         sbrec_port_binding_by_key, mac_binding_table);
+    send_garp_run(sbrec_port_binding_by_datapath,
+                  sbrec_port_binding_by_name, br_int, chassis, chassis_index,
+                  local_datapaths, active_tunnels);
+    send_ipv6_ras(sbrec_port_binding_by_datapath,
+                  sbrec_port_binding_by_name, local_datapaths);
 }
 
 /* Table of ipv6_ra_state structures, keyed on logical port name */
@@ -1495,7 +1510,8 @@ ipv6_ra_wait(void)
 }
 
 static void
-send_ipv6_ras(const struct controller_ctx *ctx,
+send_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+              struct ovsdb_idl_index *sbrec_port_binding_by_name,
               const struct hmap *local_datapaths)
 {
     struct shash_node *iter, *iter_next;
@@ -1509,16 +1525,13 @@ send_ipv6_ras(const struct controller_ctx *ctx,
 
     const struct local_datapath *ld;
     HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
-        struct sbrec_port_binding *lpval;
-        const struct sbrec_port_binding *pb;
-        struct ovsdb_idl_index_cursor cursor;
-
-        lpval = sbrec_port_binding_index_init_row(ctx->ovnsb_idl,
-                                                  &sbrec_table_port_binding);
-        sbrec_port_binding_index_set_datapath(lpval, ld->datapath);
-        ovsdb_idl_initialize_cursor(ctx->ovnsb_idl, &sbrec_table_port_binding,
-                                    "lport-by-datapath", &cursor);
-        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) {
+        struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+            sbrec_port_binding_by_datapath);
+        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+
+        struct sbrec_port_binding *pb;
+        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                           sbrec_port_binding_by_datapath) {
             if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) {
                 continue;
             }
@@ -1529,7 +1542,7 @@ send_ipv6_ras(const struct controller_ctx *ctx,
             }
 
             const struct sbrec_port_binding *peer
-                = lport_lookup_by_name(ctx->ovnsb_idl, peer_s);
+                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
             if (!peer) {
                 continue;
             }
@@ -1566,7 +1579,7 @@ send_ipv6_ras(const struct controller_ctx *ctx,
                 send_ipv6_ra_time = next_ra;
             }
         }
-        sbrec_port_binding_index_destroy_row(lpval);
+        sbrec_port_binding_index_destroy_row(target);
     }
 
     /* Remove those that are no longer in the SB database */
@@ -1691,6 +1704,8 @@ pinctrl_handle_put_mac_binding(const struct flow *md,
 
 static void
 run_put_mac_binding(struct controller_ctx *ctx,
+                    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
                     const struct sbrec_mac_binding_table *mac_binding_table,
                     const struct put_mac_binding *pmb)
 {
@@ -1699,8 +1714,9 @@ run_put_mac_binding(struct controller_ctx *ctx,
     }
 
     /* Convert logical datapath and logical port key into lport. */
-    const struct sbrec_port_binding *pb
-        = lport_lookup_by_key(ctx->ovnsb_idl, pmb->dp_key, pmb->port_key);
+    const struct sbrec_port_binding *pb = lport_lookup_by_key(
+        sbrec_datapath_binding_by_key, sbrec_port_binding_by_key,
+        pmb->dp_key, pmb->port_key);
     if (!pb) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
@@ -1739,6 +1755,8 @@ run_put_mac_binding(struct controller_ctx *ctx,
 
 static void
 run_put_mac_bindings(struct controller_ctx *ctx,
+                     struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                     struct ovsdb_idl_index *sbrec_port_binding_by_key,
                      const struct sbrec_mac_binding_table *mac_binding_table)
 {
     if (!ctx->ovnsb_idl_txn) {
@@ -1747,7 +1765,8 @@ run_put_mac_bindings(struct controller_ctx *ctx,
 
     const struct put_mac_binding *pmb;
     HMAP_FOR_EACH (pmb, hmap_node, &put_mac_bindings) {
-        run_put_mac_binding(ctx, mac_binding_table, pmb);
+        run_put_mac_binding(ctx, sbrec_datapath_binding_by_key,
+                            sbrec_port_binding_by_key, mac_binding_table, pmb);
     }
     flush_put_mac_bindings();
 }
@@ -1950,13 +1969,15 @@ send_garp(struct garp_data *garp, long long int current_time)
 
 /* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */
 static void
-get_localnet_vifs_l3gwports(struct controller_ctx *ctx,
-                  const struct ovsrec_bridge *br_int,
-                  const struct sbrec_chassis *chassis,
-                  const struct hmap *local_datapaths,
-                  struct sset *localnet_vifs,
-                  struct simap *localnet_ofports,
-                  struct sset *local_l3gw_ports)
+get_localnet_vifs_l3gwports(
+    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+    const struct ovsrec_bridge *br_int,
+    const struct sbrec_chassis *chassis,
+    const struct hmap *local_datapaths,
+    struct sset *localnet_vifs,
+    struct simap *localnet_ofports,
+    struct sset *local_l3gw_ports)
 {
     for (int i = 0; i < br_int->n_ports; i++) {
         const struct ovsrec_port *port_rec = br_int->ports[i];
@@ -1991,7 +2012,7 @@ get_localnet_vifs_l3gwports(struct controller_ctx *ctx,
                 continue;
             }
             const struct sbrec_port_binding *pb
-                = lport_lookup_by_name(ctx->ovnsb_idl, iface_id);
+                = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
             if (!pb) {
                 continue;
             }
@@ -2004,13 +2025,10 @@ get_localnet_vifs_l3gwports(struct controller_ctx *ctx,
         }
     }
 
+    struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+        sbrec_port_binding_by_datapath);
+
     const struct local_datapath *ld;
-    struct ovsdb_idl_index_cursor cursor;
-    struct sbrec_port_binding *lpval;
-    lpval = sbrec_port_binding_index_init_row(ctx->ovnsb_idl,
-                                              &sbrec_table_port_binding);
-    ovsdb_idl_initialize_cursor(ctx->ovnsb_idl, &sbrec_table_port_binding,
-                                "lport-by-datapath", &cursor);
     HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
         const struct sbrec_port_binding *pb;
 
@@ -2023,27 +2041,27 @@ get_localnet_vifs_l3gwports(struct controller_ctx *ctx,
          * bindings of type "patch" since they might connect to
          * distributed gateway ports with NAT addresses. */
 
-        sbrec_port_binding_index_set_datapath(lpval, ld->datapath);
-
-        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) {
+        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                           sbrec_port_binding_by_datapath) {
             if ((ld->has_local_l3gateway && !strcmp(pb->type, "l3gateway"))
                 || !strcmp(pb->type, "patch")) {
                 sset_add(local_l3gw_ports, pb->logical_port);
             }
         }
     }
-    sbrec_port_binding_index_destroy_row(lpval);
+    sbrec_port_binding_index_destroy_row(target);
 }
 
 static bool
-pinctrl_is_chassis_resident(struct controller_ctx *ctx,
+pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                             const struct sbrec_chassis *chassis,
                             const struct chassis_index *chassis_index,
                             const struct sset *active_tunnels,
                             const char *port_name)
 {
     const struct sbrec_port_binding *pb
-        = lport_lookup_by_name(ctx->ovnsb_idl, port_name);
+        = lport_lookup_by_name(sbrec_port_binding_by_name, port_name);
     if (!pb || !pb->chassis) {
         return false;
     }
@@ -2127,7 +2145,7 @@ extract_addresses_with_port(const char *addresses,
 }
 
 static void
-consider_nat_address(struct controller_ctx *ctx,
+consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                      const char *nat_address,
                      const struct sbrec_port_binding *pb,
                      struct sset *nat_address_keys,
@@ -2141,7 +2159,8 @@ consider_nat_address(struct controller_ctx *ctx,
     if (!extract_addresses_with_port(nat_address, laddrs, &lport)
         || (!lport && !strcmp(pb->type, "patch"))
         || (lport && !pinctrl_is_chassis_resident(
-            ctx, chassis, chassis_index, active_tunnels, lport))) {
+                sbrec_port_binding_by_name, chassis, chassis_index,
+                active_tunnels, lport))) {
         destroy_lport_addresses(laddrs);
         free(laddrs);
         free(lport);
@@ -2160,7 +2179,7 @@ consider_nat_address(struct controller_ctx *ctx,
 }
 
 static void
-get_nat_addresses_and_keys(struct controller_ctx *ctx,
+get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_port_binding_by_name,
                            struct sset *nat_address_keys,
                            struct sset *local_l3gw_ports,
                            const struct sbrec_chassis *chassis,
@@ -2172,14 +2191,15 @@ get_nat_addresses_and_keys(struct controller_ctx *ctx,
     SSET_FOR_EACH(gw_port, local_l3gw_ports) {
         const struct sbrec_port_binding *pb;
 
-        pb = lport_lookup_by_name(ctx->ovnsb_idl, gw_port);
+        pb = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
         if (!pb) {
             continue;
         }
 
         if (pb->n_nat_addresses) {
             for (int i = 0; i < pb->n_nat_addresses; i++) {
-                consider_nat_address(ctx, pb->nat_addresses[i], pb,
+                consider_nat_address(sbrec_port_binding_by_name,
+                                     pb->nat_addresses[i], pb,
                                      nat_address_keys, chassis,
                                      chassis_index, active_tunnels,
                                      nat_addresses);
@@ -2190,7 +2210,8 @@ get_nat_addresses_and_keys(struct controller_ctx *ctx,
             const char *nat_addresses_options = smap_get(&pb->options,
                                                          "nat-addresses");
             if (nat_addresses_options) {
-                consider_nat_address(ctx, nat_addresses_options, pb,
+                consider_nat_address(sbrec_port_binding_by_name,
+                                     nat_addresses_options, pb,
                                      nat_address_keys, chassis,
                                      chassis_index, active_tunnels,
                                      nat_addresses);
@@ -2206,7 +2227,8 @@ send_garp_wait(void)
 }
 
 static void
-send_garp_run(struct controller_ctx *ctx,
+send_garp_run(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+              struct ovsdb_idl_index *sbrec_port_binding_by_name,
               const struct ovsrec_bridge *br_int,
               const struct sbrec_chassis *chassis,
               const struct chassis_index *chassis_index,
@@ -2221,10 +2243,14 @@ send_garp_run(struct controller_ctx *ctx,
 
     shash_init(&nat_addresses);
 
-    get_localnet_vifs_l3gwports(ctx, br_int, chassis, local_datapaths,
-                      &localnet_vifs, &localnet_ofports, &local_l3gw_ports);
+    get_localnet_vifs_l3gwports(sbrec_port_binding_by_datapath,
+                                sbrec_port_binding_by_name,
+                                br_int, chassis, local_datapaths,
+                                &localnet_vifs, &localnet_ofports,
+                                &local_l3gw_ports);
 
-    get_nat_addresses_and_keys(ctx, &nat_ip_keys, &local_l3gw_ports,
+    get_nat_addresses_and_keys(sbrec_port_binding_by_name,
+                               &nat_ip_keys, &local_l3gw_ports,
                                chassis, chassis_index, active_tunnels,
                                &nat_addresses);
     /* For deleted ports and deleted nat ips, remove from send_garp_data. */
@@ -2239,9 +2265,8 @@ send_garp_run(struct controller_ctx *ctx,
     /* Update send_garp_data. */
     const char *iface_id;
     SSET_FOR_EACH (iface_id, &localnet_vifs) {
-        const struct sbrec_port_binding *pb;
-
-        pb = lport_lookup_by_name(ctx->ovnsb_idl, iface_id);
+        const struct sbrec_port_binding *pb = lport_lookup_by_name(
+            sbrec_port_binding_by_name, iface_id);
         if (pb) {
             send_garp_update(pb, &localnet_ofports, local_datapaths,
                              &nat_addresses);
@@ -2251,9 +2276,8 @@ send_garp_run(struct controller_ctx *ctx,
     /* Update send_garp_data for nat-addresses. */
     const char *gw_port;
     SSET_FOR_EACH (gw_port, &local_l3gw_ports) {
-        const struct sbrec_port_binding *pb;
-
-        pb = lport_lookup_by_name(ctx->ovnsb_idl, gw_port);
+        const struct sbrec_port_binding *pb
+            = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
         if (pb) {
             send_garp_update(pb, &localnet_ofports, local_datapaths,
                              &nat_addresses);
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
index a2518ebcdaec..27f51d7de65c 100644
--- a/ovn/controller/pinctrl.h
+++ b/ovn/controller/pinctrl.h
@@ -26,6 +26,7 @@ struct chassis_index;
 struct controller_ctx;
 struct hmap;
 struct lport_index;
+struct ovsdb_idl_index;
 struct ovsrec_bridge;
 struct sbrec_chassis;
 struct sbrec_dns_table;
@@ -33,6 +34,10 @@ struct sbrec_mac_binding_table;
 
 void pinctrl_init(void);
 void pinctrl_run(struct controller_ctx *,
+                 struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                 struct ovsdb_idl_index *sbrec_port_binding_by_key,
+                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
                  const struct sbrec_dns_table *,
                  const struct sbrec_mac_binding_table *,
                  const struct ovsrec_bridge *, const struct sbrec_chassis *,
diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
index 5a675ebba24b..14d0b0ed486b 100755
--- a/ovsdb/ovsdb-idlc.in
+++ b/ovsdb/ovsdb-idlc.in
@@ -284,24 +284,43 @@ static inline bool %(s)s_is_deleted(const struct %(s)s *row)
 }
 
 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))
+
+struct %(s)s *%(s)s_index_find(struct ovsdb_idl_index *, const struct %(s)s *);
+
+int %(s)s_index_compare(
+    struct ovsdb_idl_index *, 
+    const struct %(s)s *, 
+    const struct %(s)s *);
+struct ovsdb_idl_cursor %(s)s_cursor_first(struct ovsdb_idl_index *);
+struct ovsdb_idl_cursor %(s)s_cursor_first_eq(
+    struct ovsdb_idl_index *, const struct %(s)s *);
+struct ovsdb_idl_cursor %(s)s_cursor_first_ge(
+    struct ovsdb_idl_index *, const struct %(s)s *);
+
+struct %(s)s *%(s)s_cursor_data(struct ovsdb_idl_cursor *);
+
+#define %(S)s_FOR_EACH_RANGE(ROW, FROM, TO, INDEX) \\
+        for (struct ovsdb_idl_cursor cursor__ = %(s)s_cursor_first_ge(INDEX, FROM); \\
+             (cursor__.position \\
+              && ((ROW) = %(s)s_cursor_data(&cursor__), \\
+                  !(TO) || %(s)s_index_compare(INDEX, ROW, TO) <= 0)); \\
+             ovsdb_idl_cursor_next(&cursor__))
+#define %(S)s_FOR_EACH_EQUAL(ROW, KEY, INDEX) \\
+        for (struct ovsdb_idl_cursor cursor__ = %(s)s_cursor_first_eq(INDEX, KEY); \\
+             (cursor__.position \\
+              ? ((ROW) = %(s)s_cursor_data(&cursor__), \\
+                 ovsdb_idl_cursor_next_eq(&cursor__, INDEX), \\
+                 true) \\
+              : false); \\
+            )
+#define %(S)s_FOR_EACH_BYINDEX(ROW, INDEX) \\
+        for (struct ovsdb_idl_cursor cursor__ = %(s)s_cursor_first(INDEX); \\
+             (cursor__.position \\
+              ? ((ROW) = %(s)s_cursor_data(&cursor__), \\
+                 ovsdb_idl_cursor_next(&cursor__), \\
+                 true) \\
+              : false); \\
+            )
 
 void %(s)s_init(struct %(s)s *);
 void %(s)s_delete(const struct %(s)s *);
@@ -367,7 +386,7 @@ 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("struct %(s)s *%(s)s_index_init_row(struct ovsdb_idl_index *);" % {'s': structName})
         print
         for columnName, column in sorted(table.columns.items()):
             print('void %(s)s_index_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName})
@@ -1164,80 +1183,68 @@ void
 void
 %(s)s_index_destroy_row(const struct %(s)s *row)
 {
-    ovsdb_idl_index_destroy_row__(&row->header_);
+    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.items()):
-        #    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("}")
+%(s)s_index_init_row(struct ovsdb_idl_index *index)
+{
+    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+    return (struct %(s)s *) ovsdb_idl_index_init_row(index);
+}
 
-        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 *
+%(s)s_index_find(struct ovsdb_idl_index *index, const struct %(s)s *target)
 {
-    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)
+    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+    return %(s)s_cast(ovsdb_idl_index_find(index, &target->header_));
+}
+
+/* Compares 'a' to 'b' and returns a strcmp()-type result. */
+int
+%(s)s_index_compare(
+    struct ovsdb_idl_index *index, 
+    const struct %(s)s *a, 
+    const struct %(s)s *b)
 {
-    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 ovsdb_idl_index_compare(index, &a->header_, &b->header_);
+}
+
+struct ovsdb_idl_cursor
+%(s)s_cursor_first(struct ovsdb_idl_index *index)
 {
-    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)
+    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+    return ovsdb_idl_cursor_first(index);
+}
+
+struct ovsdb_idl_cursor
+%(s)s_cursor_first_eq(
+    struct ovsdb_idl_index *index, const struct %(s)s *target)
 {
-    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)
+    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+    return ovsdb_idl_cursor_first_eq(index, &target->header_);
+}
+
+struct ovsdb_idl_cursor
+%(s)s_cursor_first_ge(
+    struct ovsdb_idl_index *index, const struct %(s)s *target)
 {
-    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)
+    ovs_assert(index->table->class_ == &%(p)stable_%(tl)s);
+    return ovsdb_idl_cursor_first_ge(index, &target->header_);
+}
+
+struct %(s)s *
+%(s)s_cursor_data(struct ovsdb_idl_cursor *cursor)
 {
-    return %(s)s_cast(ovsdb_idl_index_data(CONST_CAST(struct ovsdb_idl_index_cursor *, cursor)));
-}""" % { 's' : structName })
+    return %(s)s_cast(ovsdb_idl_cursor_data(cursor));
+}
+""" % {'s': structName,
+        'c': columnName,
+        't': tableName,
+        'tl': tableName.lower(),
+        'p': prefix})
         # Indexes Set functions
         for columnName, column in sorted(table.columns.items()):
             type = column.type
@@ -1270,10 +1277,10 @@ struct %(s)s *
     } 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]);
+    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,
@@ -1317,7 +1324,7 @@ struct %(s)s *
                     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_"
+                txn_write_func = "ovsdb_idl_index_write"
             elif type.is_optional_pointer():
                 print("    union ovsdb_atom *key = xmalloc(sizeof (union ovsdb_atom));")
                 print()
@@ -1330,7 +1337,7 @@ struct %(s)s *
                 print("        datum.keys = NULL;")
                 print("    }")
                 print("    datum.values = NULL;")
-                txn_write_func = "ovsdb_idl_index_write_"
+                txn_write_func = "ovsdb_idl_index_write"
             elif type.n_max == 1:
                 print("    union ovsdb_atom *key = xmalloc(sizeof(union ovsdb_atom));")
                 print()
@@ -1343,7 +1350,7 @@ struct %(s)s *
                 print("        datum.keys = NULL;")
                 print("    }")
                 print("    datum.values = NULL;")
-                txn_write_func = "ovsdb_idl_index_write_"
+                txn_write_func = "ovsdb_idl_index_write"
             else:
                 print("    size_t i;")
                 print()
@@ -1364,7 +1371,7 @@ struct %(s)s *
                     valueType = "OVSDB_TYPE_VOID"
                 print("    ovsdb_datum_sort_unique(&datum, %s, %s);" % (
                     type.key.toAtomicType(), valueType))
-                txn_write_func = "ovsdb_idl_index_write_"
+                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,
diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
index c557c7c64520..ff5f92035603 100644
--- a/tests/test-ovsdb.c
+++ b/tests/test-ovsdb.c
@@ -2723,7 +2723,6 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
     struct idltest_simple4 *myRow2;
     const struct ovsdb_datum *uset OVS_UNUSED;
     const struct ovsdb_datum *uref OVS_UNUSED;
-    struct ovsdb_idl_index_cursor cursor;
     int step = 0;
 
     idl = ovsdb_idl_create(ctx->argv[1], &idltest_idl_class, false, true);
@@ -2734,16 +2733,11 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
     ovsdb_idl_add_table(idl, &idltest_table_simple4);
     ovsdb_idl_add_column(idl, &idltest_simple4_col_name);
 
-    struct ovsdb_idl_index *index;
-    index = ovsdb_idl_create_index(idl, &idltest_table_simple3, "uref");
-    ovsdb_idl_index_add_column(index, &idltest_simple3_col_uref,
-                               OVSDB_INDEX_ASC, NULL);
+    struct ovsdb_idl_index *index = ovsdb_idl_index_create1(
+        idl, &idltest_simple3_col_uref);
 
     ovsdb_idl_get_initial_snapshot(idl);
 
-    ovsdb_idl_initialize_cursor(idl, &idltest_table_simple3, "uref",
-                                &cursor);
-
     setvbuf(stdout, NULL, _IONBF, 0);
     ovsdb_idl_run(idl);
 
@@ -2765,12 +2759,11 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
 
     /* Use index to query the row with reference */
 
-    struct idltest_simple3 *equal;
-    equal = idltest_simple3_index_init_row(idl, &idltest_table_simple3);
+    struct idltest_simple3 *equal = idltest_simple3_index_init_row(index);
     myRow2 = (struct idltest_simple4 *) idltest_simple4_first(idl);
     idltest_simple3_index_set_uref(equal, &myRow2, 1);
     printf("%03d: Query using index with reference\n", step++);
-    IDLTEST_SIMPLE3_FOR_EACH_EQUAL (myRow, &cursor, equal) {
+    IDLTEST_SIMPLE3_FOR_EACH_EQUAL (myRow, equal, index) {
         print_idl_row_simple3(myRow, step++);
     }
     idltest_simple3_index_destroy_row(equal);
@@ -2797,65 +2790,64 @@ do_idl_compound_index_with_ref(struct ovs_cmdl_context *ctx)
 
 static int
 test_idl_compound_index_single_column(struct ovsdb_idl *idl,
-        struct ovsdb_idl_index_cursor *sCursor,
-        struct ovsdb_idl_index_cursor *iCursor)
+                                      struct ovsdb_idl_index *s_index,
+                                      struct ovsdb_idl_index *i_index)
 {
     const struct idltest_simple *myRow;
     struct ovsdb_idl_txn *txn;
     int step = 0;
 
-     /* Display records by string index -> sCursor */
+     /* Display records by string index. */
      ++step;
-     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sCursor) {
+     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, s_index) {
          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 */
+     /* Display records by integer index. */
      ++step;
-     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, iCursor) {
+     IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, i_index) {
          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
+     /* Display records by string index -> s_index with filtering
       * where s=\"List001\
       */
      ++step;
-     struct idltest_simple *equal;
-     equal = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     struct idltest_simple *equal = idltest_simple_index_init_row(s_index);
      idltest_simple_index_set_s(equal, "List001");
      ovs_assert(strcmp(equal->s, "List001") == 0);
-     IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, sCursor, equal) {
+     IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, s_index) {
          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 */
+     /* Display records by integer index -> i_index 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) {
+     IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, i_index) {
          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] */
+     /* Display records by integer index -> i_index in range i=[3,7] */
      ++step;
      struct idltest_simple *from, *to;
-     from = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     from = idltest_simple_index_init_row(i_index);
      idltest_simple_index_set_i(from, 3);
      ovs_assert(from->i == 3);
-     to = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     to = idltest_simple_index_init_row(i_index);
      idltest_simple_index_set_i(to, 7);
      ovs_assert(to->i == 7);
-     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, iCursor, from, to) {
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, i_index) {
          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 */
+     /* Delete record i=4 and insert i=54 by integer index -> i_index */
      ++step;
      struct idltest_simple *toDelete, *toInsert;
-     toDelete = idltest_simple_index_init_row(idl, &idltest_table_simple);
+     toDelete = idltest_simple_index_init_row(i_index);
      idltest_simple_index_set_i(toDelete, 4);
      ovs_assert(toDelete->i == 4);
-     myRow = idltest_simple_index_find(iCursor, toDelete);
+     myRow = idltest_simple_index_find(i_index, toDelete);
      ovs_assert(myRow);
      ovs_assert(myRow->i == 4);
      txn = ovsdb_idl_txn_create(idl);
@@ -2868,7 +2860,7 @@ test_idl_compound_index_single_column(struct ovsdb_idl *idl,
      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) {
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, i_index) {
          printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
                 myRow->s, myRow->b?"True":"False", myRow->r);
      }
@@ -2876,7 +2868,7 @@ test_idl_compound_index_single_column(struct ovsdb_idl *idl,
      /* 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) {
+     IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, NULL, NULL, i_index) {
          printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step,  myRow->i,
                 myRow->s, myRow->b?"True":"False", myRow->r);
      }
@@ -2885,52 +2877,50 @@ test_idl_compound_index_single_column(struct ovsdb_idl *idl,
      idltest_simple_index_destroy_row(from);
      idltest_simple_index_destroy_row(to);
      idltest_simple_index_destroy_row(equal);
+     idltest_simple_index_destroy_row(toDelete);
      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)
+test_idl_compound_index_double_column(struct ovsdb_idl_index *si_index,
+                                      struct ovsdb_idl_index *sid_index,
+                                      struct ovsdb_idl_index *is_index,
+                                      struct ovsdb_idl_index *ids_index)
 {
     const struct idltest_simple *myRow;
     int step = 0;
 
-    /* Display records by string-integer index -> siCursor */
+    /* Display records by string-integer index -> si_index */
     step++;
-    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, siCursor) {
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, si_index) {
         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 */
+    /* Display records by string-integer(down order) index -> sid_index */
     step++;
-    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sidCursor) {
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, sid_index) {
         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
+    /* Display records by string-integer index -> si_index with filtering
      * where s="List000" and i=10
      */
     step++;
-    struct idltest_simple *equal;
-    equal = idltest_simple_index_init_row(idl, &idltest_table_simple);
+    struct idltest_simple *equal = idltest_simple_index_init_row(si_index);
     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) {
+    IDLTEST_SIMPLE_FOR_EACH_EQUAL (myRow, equal, si_index) {
         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]
+    /* Display records by string-integer index -> si_index 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);
+    struct idltest_simple *from = idltest_simple_index_init_row(si_index);
+    struct idltest_simple *to = idltest_simple_index_init_row(si_index);
     idltest_simple_index_set_i(from, 0);
     ovs_assert(from->i == 0);
     idltest_simple_index_set_s(from, "List001");
@@ -2939,19 +2929,19 @@ test_idl_compound_index_double_column(struct ovsdb_idl *idl,
     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) {
+    IDLTEST_SIMPLE_FOR_EACH_RANGE (myRow, from, to, si_index) {
         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) {
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, is_index) {
         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) {
+    IDLTEST_SIMPLE_FOR_EACH_BYINDEX (myRow, ids_index) {
         printf("%03d: i=%"PRId64" s=%s b=%s r=%f\n", step, myRow->i, myRow->s,
                myRow->b?"True":"False", myRow->r);
     }
@@ -2966,8 +2956,6 @@ 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
     };
@@ -2983,60 +2971,37 @@ do_idl_compound_index(struct ovs_cmdl_context *ctx)
     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);
+    struct ovsdb_idl_index *s_index
+        = ovsdb_idl_index_create1(idl, &idltest_simple_col_s);
+
+    struct ovsdb_idl_index *i_index
+        = ovsdb_idl_index_create1(idl, &idltest_simple_col_i);
+
+    struct ovsdb_idl_index *si_index
+        = ovsdb_idl_index_create2(idl, &idltest_simple_col_s,
+                                  &idltest_simple_col_i);
+
+    const struct ovsdb_idl_index_column sid_columns[] = {
+        { .column = &idltest_simple_col_s },
+        { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC },
+    };
+    struct ovsdb_idl_index *sid_index
+        = ovsdb_idl_index_create(idl, sid_columns, ARRAY_SIZE(sid_columns));
+
+    struct ovsdb_idl_index *is_index
+        = ovsdb_idl_index_create2(idl, &idltest_simple_col_i,
+                                  &idltest_simple_col_s);
+
+    const struct ovsdb_idl_index_column ids_columns[] = {
+        { .column = &idltest_simple_col_i, .order = OVSDB_INDEX_DESC },
+        { .column = &idltest_simple_col_s },
+    };
+    struct ovsdb_idl_index *ids_index
+        = ovsdb_idl_index_create(idl, ids_columns, ARRAY_SIZE(sid_columns));
 
     /* 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++) {
@@ -3050,11 +3015,11 @@ do_idl_compound_index(struct ovs_cmdl_context *ctx)
 
         switch (test_to_run) {
             case IDL_COMPOUND_INDEX_WITH_SINGLE_COLUMN:
-                test_idl_compound_index_single_column(idl, &sCursor, &iCursor);
+                test_idl_compound_index_single_column(idl, s_index, i_index);
                 break;
             case IDL_COMPOUND_INDEX_WITH_DOUBLE_COLUMN:
-                test_idl_compound_index_double_column(idl, &siCursor,
-                        &sidCursor, &isCursor, &idsCursor);
+                test_idl_compound_index_double_column(si_index, sid_index,
+                                                      is_index, ids_index);
                 break;
             default:
                 printf("%03d: Test %s not implemented.\n", step++, arg);
-- 
2.16.1



More information about the dev mailing list