Currently all OVSDB database queries except for UUID lookups all result in linear lookups over the entire table, even if an index is present.
This patch modifies ovsdb_query() to attempt an index lookup first, if possible. If no matching indexes are present then a linear index is still conducted. Reported-at: https://issues.redhat.com/browse/FDP-590 Signed-off-by: Mike Pattrick <[email protected]> --- NEWS | 3 ++ ovsdb/query.c | 102 +++++++++++++++++++++++++++++++++++---- ovsdb/row.h | 28 +++++++++++ ovsdb/transaction.c | 27 ----------- tests/ovsdb-execution.at | 34 ++++++++++++- tests/ovsdb-server.at | 2 +- tests/ovsdb-tool.at | 2 +- 7 files changed, 159 insertions(+), 39 deletions(-) diff --git a/NEWS b/NEWS index b92cec532..89f64d6f6 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ Post-v3.3.0 - The primary development branch has been renamed from 'master' to 'main'. The OVS tree remains hosted on GitHub. https://github.com/openvswitch/ovs.git + - OVSDB: + * Added support to lookup rows based on arbitrary indexes, instead of + only UUID indexes. v3.3.0 - 16 Feb 2024 diff --git a/ovsdb/query.c b/ovsdb/query.c index eebe56412..e3e50a034 100644 --- a/ovsdb/query.c +++ b/ovsdb/query.c @@ -21,32 +21,116 @@ #include "condition.h" #include "row.h" #include "table.h" +#include "transaction.h" + +static bool +ovsdb_query_index(struct ovsdb_table *table, + const struct ovsdb_condition *cnd, + const struct ovsdb_row **out) +{ + for (size_t idx = 0; idx < table->schema->n_indexes; idx++) { + const struct ovsdb_column_set *index = &table->schema->indexes[idx]; + struct hmap_node *node; + size_t matches = 0; + uint32_t hash = 0; + + if (index->n_columns != cnd->n_clauses) { + continue; + } + + /* The conditions may not be in the same order as the index. */ + for (size_t c = 0; c < cnd->n_clauses; c++) { + const struct ovsdb_clause *cnd_cls = &cnd->clauses[c]; + + if (cnd_cls->function != OVSDB_F_EQ) { + return false; + } + + for (size_t i = 0; i < index->n_columns; i++) { + const struct ovsdb_column *idx_col = index->columns[i]; + + if (cnd_cls->index == idx_col->index) { + hash = ovsdb_datum_hash(&cnd_cls->arg, &idx_col->type, + hash); + matches++; + break; + } + } + + /* If none of the indexed columns match, continue to the next + * index. */ + if (matches == c) { + break; + } + } + + if (matches != cnd->n_clauses) { + continue; + } + + for (node = hmap_first_with_hash(&table->indexes[idx], hash); node; + node = hmap_next_with_hash(node)) { + struct ovsdb_row *irow = ovsdb_row_from_index_node(node, table, + idx); + + for (size_t c = 0; c < cnd->n_clauses; c++) { + const struct ovsdb_clause *cnd_cls = &cnd->clauses[c]; + + if (!ovsdb_datum_equals(&cnd_cls->arg, + &irow->fields[cnd_cls->index], + &cnd_cls->column->type)) { + irow = NULL; + break; + } + } + + if (irow) { + *out = irow; + return true; + } + } + + /* In the case that there was a matching index but no matching row, the + * index check is still considered to be a success. */ + return true; + } + return false; +} void ovsdb_query(struct ovsdb_table *table, const struct ovsdb_condition *cnd, bool (*output_row)(const struct ovsdb_row *, void *aux), void *aux) { + const struct ovsdb_row *row = NULL; + if (cnd->n_clauses > 0 && cnd->clauses[0].column->index == OVSDB_COL_UUID && cnd->clauses[0].function == OVSDB_F_EQ) { /* Optimize the case where the query has a clause of the form "uuid == * <some-uuid>", since we have an index on UUID. */ - const struct ovsdb_row *row; row = ovsdb_table_get_row(table, &cnd->clauses[0].arg.keys[0].uuid); if (row && row->table == table && ovsdb_condition_match_every_clause(row, cnd)) { output_row(row, aux); } - } else { - /* Linear scan. */ - const struct ovsdb_row *row; + return; + } - HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) { - if (ovsdb_condition_match_every_clause(row, cnd) && - !output_row(row, aux)) { - break; - } + /* Index check. */ + if (ovsdb_query_index(table, cnd, &row)) { + if (row) { + output_row(row, aux); + return; + } + return; + } + + /* Linear scan. */ + HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) { + if (ovsdb_condition_match_every_clause(row, cnd) && + !output_row(row, aux)) { + break; } } } diff --git a/ovsdb/row.h b/ovsdb/row.h index 6f5e58acb..f585b9fdc 100644 --- a/ovsdb/row.h +++ b/ovsdb/row.h @@ -22,6 +22,7 @@ #include "openvswitch/hmap.h" #include "openvswitch/list.h" #include "ovsdb-data.h" +#include "table.h" struct ovsdb_column_set; @@ -152,6 +153,33 @@ ovsdb_row_hash(const struct ovsdb_row *row) { return uuid_hash(ovsdb_row_get_uuid(row)); } + +/* Returns the offset in bytes from the start of an ovsdb_row for 'table' to + * the hmap_node for the index numbered 'i'. */ +static inline size_t +ovsdb_row_index_offset__(const struct ovsdb_table *table, size_t i) +{ + size_t n_fields = shash_count(&table->schema->columns); + return (offsetof(struct ovsdb_row, fields) + + n_fields * sizeof(struct ovsdb_datum) + + i * sizeof(struct hmap_node)); +} + +/* Returns the hmap_node in 'row' for the index numbered 'i'. */ +static inline struct hmap_node * +ovsdb_row_get_index_node(struct ovsdb_row *row, size_t i) +{ + return (void *) ((char *) row + ovsdb_row_index_offset__(row->table, i)); +} + +/* Returns the ovsdb_row given 'index_node', which is a pointer to that row's + * hmap_node for the index numbered 'i' within 'table'. */ +static inline struct ovsdb_row * +ovsdb_row_from_index_node(struct hmap_node *index_node, + const struct ovsdb_table *table, size_t i) +{ + return (void *) ((char *) index_node - ovsdb_row_index_offset__(table, i)); +} /* An unordered collection of rows. */ struct ovsdb_row_set { diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c index 484a88e1c..989d889cd 100644 --- a/ovsdb/transaction.c +++ b/ovsdb/transaction.c @@ -194,33 +194,6 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED, return NULL; } -/* Returns the offset in bytes from the start of an ovsdb_row for 'table' to - * the hmap_node for the index numbered 'i'. */ -static size_t -ovsdb_row_index_offset__(const struct ovsdb_table *table, size_t i) -{ - size_t n_fields = shash_count(&table->schema->columns); - return (offsetof(struct ovsdb_row, fields) - + n_fields * sizeof(struct ovsdb_datum) - + i * sizeof(struct hmap_node)); -} - -/* Returns the hmap_node in 'row' for the index numbered 'i'. */ -static struct hmap_node * -ovsdb_row_get_index_node(struct ovsdb_row *row, size_t i) -{ - return (void *) ((char *) row + ovsdb_row_index_offset__(row->table, i)); -} - -/* Returns the ovsdb_row given 'index_node', which is a pointer to that row's - * hmap_node for the index numbered 'i' within 'table'. */ -static struct ovsdb_row * -ovsdb_row_from_index_node(struct hmap_node *index_node, - const struct ovsdb_table *table, size_t i) -{ - return (void *) ((char *) index_node - ovsdb_row_index_offset__(table, i)); -} - void ovsdb_txn_abort(struct ovsdb_txn *txn) { diff --git a/tests/ovsdb-execution.at b/tests/ovsdb-execution.at index 1ffa2b738..587604268 100644 --- a/tests/ovsdb-execution.at +++ b/tests/ovsdb-execution.at @@ -11,7 +11,7 @@ ordinal_schema () { "columns": { "number": {"type": "integer"}, "name": {"type": "string"}}, - "indexes": [["number"]]}}, + "indexes": [["number"], ["number", "name"]]}}, "version": "5.1.3", "cksum": "12345678 9"} EOF @@ -273,6 +273,38 @@ dnl Attempt to insert second row with same UUID (fails). [{"details":"This UUID would duplicate a UUID already present within the table or deleted within the same transaction.","error":"duplicate uuid","syntax":"\"ffffffff-971b-4cba-bf42-520515973b7e\""}] ]]) +OVSDB_CHECK_EXECUTION([insert rows, query by index], + [ordinal_schema], +dnl Insert initial rows. + [[[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 0, "name": "zero"}}]]], + [[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 1, "name": "one"}}]]], + [[["ordinals", + {"op": "insert", + "table": "ordinals", + "row": {"number": 2, "name": "two"}}]]], +dnl Query with first index. + [[["ordinals", + {"op": "select", + "table": "ordinals", + "where": [["number", "==", 1]]}]]], +dnl Query with second index. + [[["ordinals", + {"op": "select", + "table": "ordinals", + "where": [["name", "==", "two"], ["number", "==", 2]]}]]]], +[[[{"uuid":["uuid","<0>"]}] +[{"uuid":["uuid","<1>"]}] +[{"uuid":["uuid","<2>"]}] +[{"rows":[{"_uuid":["uuid","<1>"],"_version":["uuid","<3>"],"name":"one","number":1}]}] +[{"rows":[{"_uuid":["uuid","<2>"],"_version":["uuid","<4>"],"name":"two","number":2}]}] +]]) + OVSDB_CHECK_EXECUTION([insert rows, query by value], [ordinal_schema], [[[["ordinals", diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at index ce6d32aee..7b4b280e1 100644 --- a/tests/ovsdb-server.at +++ b/tests/ovsdb-server.at @@ -1007,7 +1007,7 @@ ovsdb_check_online_compaction() { AT_CHECK([[uuidfilt db | grep -v ^OVSDB | \ sed 's/"_date":[0-9]*/"_date":0/' | sed 's/"_is_diff":true,//' | \ ovstest test-json --multiple -]], [0], -[[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"]]}},"version":"5.1.3"} +[[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"],["number","name"]]}},"version":"5.1.3"} {"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}} {"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}} {"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}} diff --git a/tests/ovsdb-tool.at b/tests/ovsdb-tool.at index d8d2b1c99..cf4a0fd2e 100644 --- a/tests/ovsdb-tool.at +++ b/tests/ovsdb-tool.at @@ -95,7 +95,7 @@ AT_CHECK( dnl Check that all the crap is in fact in the database log. AT_CHECK([[uuidfilt db | grep -v ^OVSDB | sed 's/"_date":[0-9]*/"_date":0/' | \ sed 's/"_is_diff":true,//' | ovstest test-json --multiple -]], [0], - [[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"]]}},"version":"5.1.3"} + [[{"cksum":"12345678 9","name":"ordinals","tables":{"ordinals":{"columns":{"name":{"type":"string"},"number":{"type":"integer"}},"indexes":[["number"],["number","name"]]}},"version":"5.1.3"} {"_comment":"add row for zero 0","_date":0,"ordinals":{"<0>":{"name":"zero"}}} {"_comment":"delete row for 0","_date":0,"ordinals":{"<0>":null}} {"_comment":"add back row for zero 0","_date":0,"ordinals":{"<1>":{"name":"zero"}}} -- 2.39.3 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
