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

Mark Michelson mmichels at redhat.com
Mon Jun 11 20:16:06 UTC 2018


So far all I've reviewed is the documentation. See my comments below.

On 06/08/2018 05:59 PM, Ben Pfaff wrote:
> 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().

I think there's a typo in the first sentence. I think it's supposed to say:

"or one of the simpler convenience functions ovsdb_idl_index_create1() 
or ovsdb_idl_index_create2()."

>   
>   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 },
> +        };

I don't like the change to using struct initializers over function calls 
for initialization. It's possible to create an ovsdb_idl_index_column 
without specifying an ordering or comparator, which can lead to 
hard-to-debug issues. I suggest hiding ovsdb_idl_index_column 
initialization behind functions. This forces the user to specify an 
ordering or comparator when applicable.

> +        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.

This list now starts with item 2.

>   
> -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);
> 



More information about the dev mailing list