1 /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include "ovsdb-idl.h"
28 #include "openvswitch/dynamic-string.h"
29 #include "fatal-signal.h"
30 #include "openvswitch/json.h"
32 #include "ovsdb/ovsdb.h"
33 #include "ovsdb/table.h"
34 #include "ovsdb-data.h"
35 #include "ovsdb-error.h"
36 #include "ovsdb-idl-provider.h"
37 #include "ovsdb-parser.h"
38 #include "poll-loop.h"
39 #include "openvswitch/shash.h"
42 #include "openvswitch/vlog.h"
44 VLOG_DEFINE_THIS_MODULE(ovsdb_idl
);
46 COVERAGE_DEFINE(txn_uncommitted
);
47 COVERAGE_DEFINE(txn_unchanged
);
48 COVERAGE_DEFINE(txn_incomplete
);
49 COVERAGE_DEFINE(txn_aborted
);
50 COVERAGE_DEFINE(txn_success
);
51 COVERAGE_DEFINE(txn_try_again
);
52 COVERAGE_DEFINE(txn_not_locked
);
53 COVERAGE_DEFINE(txn_error
);
55 /* An arc from one idl_row to another. When row A contains a UUID that
56 * references row B, this is represented by an arc from A (the source) to B
59 * Arcs from a row to itself are omitted, that is, src and dst are always
62 * Arcs are never duplicated, that is, even if there are multiple references
63 * from A to B, there is only a single arc from A to B.
65 * Arcs are directed: an arc from A to B is the converse of an an arc from B to
66 * A. Both an arc and its converse may both be present, if each row refers
67 * to the other circularly.
69 * The source and destination row may be in the same table or in different
72 struct ovsdb_idl_arc
{
73 struct ovs_list src_node
; /* In src->src_arcs list. */
74 struct ovs_list dst_node
; /* In dst->dst_arcs list. */
75 struct ovsdb_idl_row
*src
; /* Source row. */
76 struct ovsdb_idl_row
*dst
; /* Destination row. */
79 enum ovsdb_idl_state
{
80 IDL_S_SCHEMA_REQUESTED
,
81 IDL_S_MONITOR_REQUESTED
,
83 IDL_S_MONITOR_COND_REQUESTED
,
84 IDL_S_MONITORING_COND
,
89 const struct ovsdb_idl_class
*class;
90 struct jsonrpc_session
*session
;
92 struct shash table_by_name
; /* Contains "struct ovsdb_idl_table *"s.*/
93 struct ovsdb_idl_table
*tables
; /* Array of ->class->n_tables elements. */
94 unsigned int change_seqno
;
95 bool verify_write_only
;
98 unsigned int state_seqno
;
99 enum ovsdb_idl_state state
;
100 struct json
*request_id
;
103 /* Database locking. */
104 char *lock_name
; /* Name of lock we need, NULL if none. */
105 bool has_lock
; /* Has db server told us we have the lock? */
106 bool is_lock_contended
; /* Has db server told us we can't get lock? */
107 struct json
*lock_request_id
; /* JSON-RPC ID of in-flight lock request. */
109 /* Transaction support. */
110 struct ovsdb_idl_txn
*txn
;
111 struct hmap outstanding_txns
;
113 /* Conditional monitoring. */
115 unsigned int cond_seqno
; /* Keep track of condition clauses changes
116 over a single conditional monitoring session.
117 Reverts to zero when idl session
121 struct ovsdb_idl_txn
{
122 struct hmap_node hmap_node
;
123 struct json
*request_id
;
124 struct ovsdb_idl
*idl
;
125 struct hmap txn_rows
;
126 enum ovsdb_idl_txn_status status
;
132 const char *inc_table
;
133 const char *inc_column
;
136 unsigned int inc_index
;
137 int64_t inc_new_value
;
140 struct hmap inserted_rows
; /* Contains "struct ovsdb_idl_txn_insert"s. */
143 struct ovsdb_idl_txn_insert
{
144 struct hmap_node hmap_node
; /* In struct ovsdb_idl_txn's inserted_rows. */
145 struct uuid dummy
; /* Dummy UUID used locally. */
146 int op_index
; /* Index into transaction's operation array. */
147 struct uuid real
; /* Real UUID used by database server. */
150 enum ovsdb_update_version
{
151 OVSDB_UPDATE
, /* RFC 7047 "update" method. */
152 OVSDB_UPDATE2
/* "update2" Extension to RFC 7047.
153 See ovsdb-server(1) for more information. */
156 /* Name arrays indexed by 'enum ovsdb_update_version'. */
157 static const char *table_updates_names
[] = {"table_updates", "table_updates2"};
158 static const char *table_update_names
[] = {"table_update", "table_update2"};
159 static const char *row_update_names
[] = {"row_update", "row_update2"};
161 static struct vlog_rate_limit syntax_rl
= VLOG_RATE_LIMIT_INIT(1, 5);
162 static struct vlog_rate_limit semantic_rl
= VLOG_RATE_LIMIT_INIT(1, 5);
164 static void ovsdb_idl_clear(struct ovsdb_idl
*);
165 static void ovsdb_idl_send_schema_request(struct ovsdb_idl
*);
166 static void ovsdb_idl_send_monitor_request(struct ovsdb_idl
*);
167 static void ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl
*);
168 static void ovsdb_idl_parse_update(struct ovsdb_idl
*, const struct json
*,
169 enum ovsdb_update_version
);
170 static struct ovsdb_error
*ovsdb_idl_parse_update__(struct ovsdb_idl
*,
172 enum ovsdb_update_version
);
173 static bool ovsdb_idl_process_update(struct ovsdb_idl_table
*,
175 const struct json
*old
,
176 const struct json
*new);
177 static bool ovsdb_idl_process_update2(struct ovsdb_idl_table
*,
179 const char *operation
,
180 const struct json
*row
);
181 static void ovsdb_idl_insert_row(struct ovsdb_idl_row
*, const struct json
*);
182 static void ovsdb_idl_delete_row(struct ovsdb_idl_row
*);
183 static bool ovsdb_idl_modify_row(struct ovsdb_idl_row
*, const struct json
*);
184 static bool ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row
*,
185 const struct json
*);
187 static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row
*);
188 static struct ovsdb_idl_row
*ovsdb_idl_row_create__(
189 const struct ovsdb_idl_table_class
*);
190 static struct ovsdb_idl_row
*ovsdb_idl_row_create(struct ovsdb_idl_table
*,
191 const struct uuid
*);
192 static void ovsdb_idl_row_destroy(struct ovsdb_idl_row
*);
193 static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl
*);
194 static void ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row
*);
195 static void ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row
*);
197 static void ovsdb_idl_row_parse(struct ovsdb_idl_row
*);
198 static void ovsdb_idl_row_unparse(struct ovsdb_idl_row
*);
199 static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row
*);
200 static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row
*);
201 static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row
*, bool destroy_dsts
);
203 static void ovsdb_idl_txn_abort_all(struct ovsdb_idl
*);
204 static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl
*,
205 const struct jsonrpc_msg
*msg
);
206 static bool ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row
*,
208 static void ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row
*,
209 const struct ovsdb_idl_column
*,
210 struct ovsdb_datum
*,
212 static void ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row
*,
213 const struct ovsdb_idl_column
*,
214 struct ovsdb_datum
*,
217 static void ovsdb_idl_send_lock_request(struct ovsdb_idl
*);
218 static void ovsdb_idl_send_unlock_request(struct ovsdb_idl
*);
219 static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl
*,
220 const struct json
*);
221 static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl
*,
222 const struct json
*params
,
224 static struct ovsdb_idl_table
*
225 ovsdb_idl_table_from_class(const struct ovsdb_idl
*,
226 const struct ovsdb_idl_table_class
*);
227 static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table
*table
);
228 static void ovsdb_idl_send_cond_change(struct ovsdb_idl
*idl
);
230 /* Creates and returns a connection to database 'remote', which should be in a
231 * form acceptable to jsonrpc_session_open(). The connection will maintain an
232 * in-memory replica of the remote database whose schema is described by
233 * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically
236 * Passes 'retry' to jsonrpc_session_open(). See that function for
239 * If 'monitor_everything_by_default' is true, then everything in the remote
240 * database will be replicated by default. ovsdb_idl_omit() and
241 * ovsdb_idl_omit_alert() may be used to selectively drop some columns from
244 * If 'monitor_everything_by_default' is false, then no columns or tables will
245 * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table()
246 * must be used to choose some columns or tables to replicate.
249 ovsdb_idl_create(const char *remote
, const struct ovsdb_idl_class
*class,
250 bool monitor_everything_by_default
, bool retry
)
252 struct ovsdb_idl
*idl
;
253 uint8_t default_mode
;
256 default_mode
= (monitor_everything_by_default
257 ? OVSDB_IDL_MONITOR
| OVSDB_IDL_ALERT
260 idl
= xzalloc(sizeof *idl
);
262 idl
->session
= jsonrpc_session_open(remote
, retry
);
263 shash_init(&idl
->table_by_name
);
264 idl
->tables
= xmalloc(class->n_tables
* sizeof *idl
->tables
);
265 for (i
= 0; i
< class->n_tables
; i
++) {
266 const struct ovsdb_idl_table_class
*tc
= &class->tables
[i
];
267 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
270 shash_add_assert(&idl
->table_by_name
, tc
->name
, table
);
272 table
->modes
= xmalloc(tc
->n_columns
);
273 memset(table
->modes
, default_mode
, tc
->n_columns
);
274 table
->need_table
= false;
275 shash_init(&table
->columns
);
276 for (j
= 0; j
< tc
->n_columns
; j
++) {
277 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
279 shash_add_assert(&table
->columns
, column
->name
, column
);
281 hmap_init(&table
->rows
);
282 ovs_list_init(&table
->track_list
);
283 table
->change_seqno
[OVSDB_IDL_CHANGE_INSERT
]
284 = table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
]
285 = table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
] = 0;
287 ovsdb_idl_condition_init(&table
->condition
);
288 ovsdb_idl_condition_add_clause_true(&table
->condition
);
289 table
->cond_changed
= false;
292 idl
->cond_changed
= false;
294 idl
->state_seqno
= UINT_MAX
;
295 idl
->request_id
= NULL
;
298 hmap_init(&idl
->outstanding_txns
);
299 uuid_generate(&idl
->uuid
);
304 /* Changes the remote and creates a new session. */
306 ovsdb_idl_set_remote(struct ovsdb_idl
*idl
, const char *remote
,
310 ovs_assert(!idl
->txn
);
311 jsonrpc_session_close(idl
->session
);
312 idl
->session
= jsonrpc_session_open(remote
, retry
);
313 idl
->state_seqno
= UINT_MAX
;
317 /* Destroys 'idl' and all of the data structures that it manages. */
319 ovsdb_idl_destroy(struct ovsdb_idl
*idl
)
324 ovs_assert(!idl
->txn
);
325 ovsdb_idl_clear(idl
);
326 jsonrpc_session_close(idl
->session
);
328 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
329 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
330 ovsdb_idl_condition_destroy(&table
->condition
);
331 shash_destroy(&table
->columns
);
332 hmap_destroy(&table
->rows
);
335 shash_destroy(&idl
->table_by_name
);
337 json_destroy(idl
->request_id
);
338 free(idl
->lock_name
);
339 json_destroy(idl
->lock_request_id
);
340 json_destroy(idl
->schema
);
341 hmap_destroy(&idl
->outstanding_txns
);
347 ovsdb_idl_clear(struct ovsdb_idl
*idl
)
349 bool changed
= false;
352 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
353 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
354 struct ovsdb_idl_row
*row
, *next_row
;
356 table
->cond_changed
= false;
357 if (hmap_is_empty(&table
->rows
)) {
362 HMAP_FOR_EACH_SAFE (row
, next_row
, hmap_node
, &table
->rows
) {
363 struct ovsdb_idl_arc
*arc
, *next_arc
;
365 if (!ovsdb_idl_row_is_orphan(row
)) {
366 ovsdb_idl_row_unparse(row
);
368 LIST_FOR_EACH_SAFE (arc
, next_arc
, src_node
, &row
->src_arcs
) {
371 /* No need to do anything with dst_arcs: some node has those arcs
372 * as forward arcs and will destroy them itself. */
374 if (!ovs_list_is_empty(&row
->track_node
)) {
375 ovs_list_remove(&row
->track_node
);
378 ovsdb_idl_row_destroy(row
);
382 idl
->cond_changed
= false;
384 ovsdb_idl_track_clear(idl
);
391 /* Processes a batch of messages from the database server on 'idl'. This may
392 * cause the IDL's contents to change. The client may check for that with
393 * ovsdb_idl_get_seqno(). */
395 ovsdb_idl_run(struct ovsdb_idl
*idl
)
399 ovs_assert(!idl
->txn
);
401 ovsdb_idl_send_cond_change(idl
);
403 jsonrpc_session_run(idl
->session
);
404 for (i
= 0; jsonrpc_session_is_connected(idl
->session
) && i
< 50; i
++) {
405 struct jsonrpc_msg
*msg
;
408 seqno
= jsonrpc_session_get_seqno(idl
->session
);
409 if (idl
->state_seqno
!= seqno
) {
410 idl
->state_seqno
= seqno
;
411 json_destroy(idl
->request_id
);
412 idl
->request_id
= NULL
;
413 ovsdb_idl_txn_abort_all(idl
);
415 ovsdb_idl_send_schema_request(idl
);
416 idl
->state
= IDL_S_SCHEMA_REQUESTED
;
417 if (idl
->lock_name
) {
418 ovsdb_idl_send_lock_request(idl
);
422 msg
= jsonrpc_session_recv(idl
->session
);
427 if (msg
->type
== JSONRPC_NOTIFY
428 && !strcmp(msg
->method
, "update2")
429 && msg
->params
->type
== JSON_ARRAY
430 && msg
->params
->u
.array
.n
== 2
431 && msg
->params
->u
.array
.elems
[0]->type
== JSON_STRING
) {
432 /* Database contents changed. */
433 ovsdb_idl_parse_update(idl
, msg
->params
->u
.array
.elems
[1],
435 } else if (msg
->type
== JSONRPC_REPLY
437 && json_equal(idl
->request_id
, msg
->id
)) {
438 json_destroy(idl
->request_id
);
439 idl
->request_id
= NULL
;
441 switch (idl
->state
) {
442 case IDL_S_SCHEMA_REQUESTED
:
443 /* Reply to our "get_schema" request. */
444 idl
->schema
= json_clone(msg
->result
);
445 ovsdb_idl_send_monitor_cond_request(idl
);
446 idl
->state
= IDL_S_MONITOR_COND_REQUESTED
;
449 case IDL_S_MONITOR_REQUESTED
:
450 case IDL_S_MONITOR_COND_REQUESTED
:
451 /* Reply to our "monitor" or "monitor_cond" request. */
453 ovsdb_idl_clear(idl
);
454 if (idl
->state
== IDL_S_MONITOR_REQUESTED
) {
455 idl
->state
= IDL_S_MONITORING
;
456 ovsdb_idl_parse_update(idl
, msg
->result
, OVSDB_UPDATE
);
457 } else { /* IDL_S_MONITOR_COND_REQUESTED. */
458 idl
->state
= IDL_S_MONITORING_COND
;
459 ovsdb_idl_parse_update(idl
, msg
->result
, OVSDB_UPDATE2
);
462 /* Schema is not useful after monitor request is accepted
464 json_destroy(idl
->schema
);
468 case IDL_S_MONITORING_COND
:
469 /* Conditional monitor clauses were updated. Send out
470 * the next condition changes, in any, immediately. */
471 ovsdb_idl_send_cond_change(idl
);
475 case IDL_S_MONITORING
:
476 case IDL_S_NO_SCHEMA
:
480 } else if (msg
->type
== JSONRPC_NOTIFY
481 && !strcmp(msg
->method
, "update")
482 && msg
->params
->type
== JSON_ARRAY
483 && msg
->params
->u
.array
.n
== 2
484 && msg
->params
->u
.array
.elems
[0]->type
== JSON_STRING
) {
485 /* Database contents changed. */
486 ovsdb_idl_parse_update(idl
, msg
->params
->u
.array
.elems
[1],
488 } else if (msg
->type
== JSONRPC_REPLY
489 && idl
->lock_request_id
490 && json_equal(idl
->lock_request_id
, msg
->id
)) {
491 /* Reply to our "lock" request. */
492 ovsdb_idl_parse_lock_reply(idl
, msg
->result
);
493 } else if (msg
->type
== JSONRPC_NOTIFY
494 && !strcmp(msg
->method
, "locked")) {
495 /* We got our lock. */
496 ovsdb_idl_parse_lock_notify(idl
, msg
->params
, true);
497 } else if (msg
->type
== JSONRPC_NOTIFY
498 && !strcmp(msg
->method
, "stolen")) {
499 /* Someone else stole our lock. */
500 ovsdb_idl_parse_lock_notify(idl
, msg
->params
, false);
501 } else if (msg
->type
== JSONRPC_ERROR
502 && idl
->state
== IDL_S_MONITOR_COND_REQUESTED
504 && json_equal(idl
->request_id
, msg
->id
)) {
505 if (msg
->error
&& !strcmp(json_string(msg
->error
),
507 /* Fall back to using "monitor" method. */
508 json_destroy(idl
->request_id
);
509 idl
->request_id
= NULL
;
510 ovsdb_idl_send_monitor_request(idl
);
511 idl
->state
= IDL_S_MONITOR_REQUESTED
;
513 } else if (msg
->type
== JSONRPC_ERROR
514 && idl
->state
== IDL_S_MONITORING_COND
516 && json_equal(idl
->request_id
, msg
->id
)) {
517 json_destroy(idl
->request_id
);
518 idl
->request_id
= NULL
;
519 VLOG_ERR("%s: conditional monitor update failed",
520 jsonrpc_session_get_name(idl
->session
));
521 idl
->state
= IDL_S_NO_SCHEMA
;
522 } else if (msg
->type
== JSONRPC_ERROR
523 && idl
->state
== IDL_S_SCHEMA_REQUESTED
525 && json_equal(idl
->request_id
, msg
->id
)) {
526 json_destroy(idl
->request_id
);
527 idl
->request_id
= NULL
;
528 VLOG_ERR("%s: requested schema not found",
529 jsonrpc_session_get_name(idl
->session
));
530 idl
->state
= IDL_S_NO_SCHEMA
;
531 } else if ((msg
->type
== JSONRPC_ERROR
532 || msg
->type
== JSONRPC_REPLY
)
533 && ovsdb_idl_txn_process_reply(idl
, msg
)) {
534 /* ovsdb_idl_txn_process_reply() did everything needful. */
536 /* This can happen if ovsdb_idl_txn_destroy() is called to destroy
537 * a transaction before we receive the reply, so keep the log level
539 VLOG_DBG("%s: received unexpected %s message",
540 jsonrpc_session_get_name(idl
->session
),
541 jsonrpc_msg_type_to_string(msg
->type
));
543 jsonrpc_msg_destroy(msg
);
545 ovsdb_idl_row_destroy_postprocess(idl
);
548 /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to
549 * do or when activity occurs on a transaction on 'idl'. */
551 ovsdb_idl_wait(struct ovsdb_idl
*idl
)
553 jsonrpc_session_wait(idl
->session
);
554 jsonrpc_session_recv_wait(idl
->session
);
557 /* Returns a "sequence number" that represents the state of 'idl'. When
558 * ovsdb_idl_run() changes the database, the sequence number changes. The
559 * initial fetch of the entire contents of the remote database is considered to
560 * be one kind of change. Successfully acquiring a lock, if one has been
561 * configured with ovsdb_idl_set_lock(), is also considered to be a change.
563 * As long as the sequence number does not change, the client may continue to
564 * use any data structures it obtains from 'idl'. But when it changes, the
565 * client must not access any of these data structures again, because they
566 * could have freed or reused for other purposes.
568 * The sequence number can occasionally change even if the database does not.
569 * This happens if the connection to the database drops and reconnects, which
570 * causes the database contents to be reloaded even if they didn't change. (It
571 * could also happen if the database server sends out a "change" that reflects
572 * what the IDL already thought was in the database. The database server is
573 * not supposed to do that, but bugs could in theory cause it to do so.) */
575 ovsdb_idl_get_seqno(const struct ovsdb_idl
*idl
)
577 return idl
->change_seqno
;
580 /* Returns a "sequence number" that represents the number of conditional
581 * monitoring updates successfully received by the OVSDB server of an IDL
584 * ovsdb_idl_set_condition() sets a new condition that is different from
585 * the current condtion, the next expected "sequence number" is returned.
587 * Whenever ovsdb_idl_get_cond_seqno() returns a value that matches
588 * the return value of ovsdb_idl_set_condition(), The client is
590 * - The ovsdb_idl_set_condition() changes has been acknowledged by
593 * - 'idl' now contains the content matches the new conditions. */
595 ovsdb_idl_get_condition_seqno(const struct ovsdb_idl
*idl
)
597 return idl
->cond_seqno
;
600 /* Returns true if 'idl' successfully connected to the remote database and
601 * retrieved its contents (even if the connection subsequently dropped and is
602 * in the process of reconnecting). If so, then 'idl' contains an atomic
603 * snapshot of the database's contents (but it might be arbitrarily old if the
604 * connection dropped).
606 * Returns false if 'idl' has never connected or retrieved the database's
607 * contents. If so, 'idl' is empty. */
609 ovsdb_idl_has_ever_connected(const struct ovsdb_idl
*idl
)
611 return ovsdb_idl_get_seqno(idl
) != 0;
614 /* Reconfigures 'idl' so that it would reconnect to the database, if
615 * connection was dropped. */
617 ovsdb_idl_enable_reconnect(struct ovsdb_idl
*idl
)
619 jsonrpc_session_enable_reconnect(idl
->session
);
622 /* Forces 'idl' to drop its connection to the database and reconnect. In the
623 * meantime, the contents of 'idl' will not change. */
625 ovsdb_idl_force_reconnect(struct ovsdb_idl
*idl
)
627 jsonrpc_session_force_reconnect(idl
->session
);
630 /* Some IDL users should only write to write-only columns. Furthermore,
631 * writing to a column which is not write-only can cause serious performance
632 * degradations for these users. This function causes 'idl' to reject writes
633 * to columns which are not marked write only using ovsdb_idl_omit_alert(). */
635 ovsdb_idl_verify_write_only(struct ovsdb_idl
*idl
)
637 idl
->verify_write_only
= true;
640 /* Returns true if 'idl' is currently connected or trying to connect
641 * and a negative response to a schema request has not been received */
643 ovsdb_idl_is_alive(const struct ovsdb_idl
*idl
)
645 return jsonrpc_session_is_alive(idl
->session
) &&
646 idl
->state
!= IDL_S_NO_SCHEMA
;
649 /* Returns the last error reported on a connection by 'idl'. The return value
650 * is 0 only if no connection made by 'idl' has ever encountered an error and
651 * a negative response to a schema request has never been received. See
652 * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value
655 ovsdb_idl_get_last_error(const struct ovsdb_idl
*idl
)
659 err
= jsonrpc_session_get_last_error(idl
->session
);
663 } else if (idl
->state
== IDL_S_NO_SCHEMA
) {
670 /* Sets the "probe interval" for 'idl->session' to 'probe_interval', in
674 ovsdb_idl_set_probe_interval(const struct ovsdb_idl
*idl
, int probe_interval
)
676 jsonrpc_session_set_probe_interval(idl
->session
, probe_interval
);
680 find_uuid_in_array(const struct uuid
*target
,
681 const struct uuid
*array
, size_t n
)
683 for (size_t i
= 0; i
< n
; i
++) {
684 if (uuid_equals(&array
[i
], target
)) {
692 array_contains_uuid(const struct uuid
*target
,
693 const struct uuid
*array
, size_t n
)
695 return find_uuid_in_array(target
, array
, n
) != SIZE_MAX
;
699 remove_uuid_from_array(const struct uuid
*target
,
700 struct uuid
*array
, size_t *n
)
702 size_t i
= find_uuid_in_array(target
, array
, *n
);
704 array
[i
] = array
[--*n
];
712 add_row_references(const struct ovsdb_base_type
*type
,
713 const union ovsdb_atom
*atoms
, size_t n_atoms
,
714 const struct uuid
*exclude_uuid
,
715 struct uuid
**dstsp
, size_t *n_dstsp
,
716 size_t *allocated_dstsp
)
718 if (type
->type
!= OVSDB_TYPE_UUID
|| !type
->u
.uuid
.refTableName
) {
722 for (size_t i
= 0; i
< n_atoms
; i
++) {
723 const struct uuid
*uuid
= &atoms
[i
].uuid
;
724 if (!uuid_equals(uuid
, exclude_uuid
)
725 && !array_contains_uuid(uuid
, *dstsp
, *n_dstsp
)) {
726 if (*n_dstsp
>= *allocated_dstsp
) {
727 *dstsp
= x2nrealloc(*dstsp
, allocated_dstsp
,
731 (*dstsp
)[*n_dstsp
] = *uuid
;
737 /* Checks for consistency in 'idl''s graph of arcs between database rows. Each
738 * reference from one row to a different row should be reflected as a "struct
739 * ovsdb_idl_arc" between those rows.
741 * This function is slow, big-O wise, and aborts if it finds an inconsistency,
742 * thus it is only for use in test programs. */
744 ovsdb_idl_check_consistency(const struct ovsdb_idl
*idl
)
746 /* Consistency is broken while a transaction is in progress. */
753 struct uuid
*dsts
= NULL
;
754 size_t allocated_dsts
= 0;
756 for (size_t i
= 0; i
< idl
->class->n_tables
; i
++) {
757 const struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
758 const struct ovsdb_idl_table_class
*class = table
->class;
760 const struct ovsdb_idl_row
*row
;
761 HMAP_FOR_EACH (row
, hmap_node
, &table
->rows
) {
764 size_t n_columns
= shash_count(&row
->table
->columns
);
765 for (size_t j
= 0; j
< n_columns
; j
++) {
766 const struct ovsdb_type
*type
= &class->columns
[j
].type
;
767 const struct ovsdb_datum
*datum
= &row
->new[j
];
768 add_row_references(&type
->key
,
769 datum
->keys
, datum
->n
, &row
->uuid
,
770 &dsts
, &n_dsts
, &allocated_dsts
);
771 add_row_references(&type
->value
,
772 datum
->values
, datum
->n
, &row
->uuid
,
773 &dsts
, &n_dsts
, &allocated_dsts
);
776 const struct ovsdb_idl_arc
*arc
;
777 LIST_FOR_EACH (arc
, src_node
, &row
->src_arcs
) {
778 if (!remove_uuid_from_array(&arc
->dst
->uuid
,
780 VLOG_ERR("unexpected arc from %s row "UUID_FMT
" to %s "
783 UUID_ARGS(&row
->uuid
),
784 arc
->dst
->table
->class->name
,
785 UUID_ARGS(&arc
->dst
->uuid
));
789 for (size_t i
= 0; i
< n_dsts
; i
++) {
790 VLOG_ERR("%s row "UUID_FMT
" missing arc to row "UUID_FMT
,
791 table
->class->name
, UUID_ARGS(&row
->uuid
),
792 UUID_ARGS(&dsts
[i
]));
801 const struct ovsdb_idl_class
*
802 ovsdb_idl_get_class(const struct ovsdb_idl
*idl
)
807 /* Given 'column' in some table in 'class', returns the table's class. */
808 const struct ovsdb_idl_table_class
*
809 ovsdb_idl_table_class_from_column(const struct ovsdb_idl_class
*class,
810 const struct ovsdb_idl_column
*column
)
812 for (size_t i
= 0; i
< class->n_tables
; i
++) {
813 const struct ovsdb_idl_table_class
*tc
= &class->tables
[i
];
814 if (column
>= tc
->columns
&& column
< &tc
->columns
[tc
->n_columns
]) {
822 /* Given 'column' in some table in 'idl', returns the table. */
823 static struct ovsdb_idl_table
*
824 ovsdb_idl_table_from_column(struct ovsdb_idl
*idl
,
825 const struct ovsdb_idl_column
*column
)
827 const struct ovsdb_idl_table_class
*tc
=
828 ovsdb_idl_table_class_from_column(idl
->class, column
);
829 return &idl
->tables
[tc
- idl
->class->tables
];
832 static unsigned char *
833 ovsdb_idl_get_mode(struct ovsdb_idl
*idl
,
834 const struct ovsdb_idl_column
*column
)
836 ovs_assert(!idl
->change_seqno
);
838 const struct ovsdb_idl_table
*table
= ovsdb_idl_table_from_column(idl
,
840 return &table
->modes
[column
- table
->class->columns
];
844 add_ref_table(struct ovsdb_idl
*idl
, const struct ovsdb_base_type
*base
)
846 if (base
->type
== OVSDB_TYPE_UUID
&& base
->u
.uuid
.refTableName
) {
847 struct ovsdb_idl_table
*table
;
849 table
= shash_find_data(&idl
->table_by_name
,
850 base
->u
.uuid
.refTableName
);
852 table
->need_table
= true;
854 VLOG_WARN("%s IDL class missing referenced table %s",
855 idl
->class->database
, base
->u
.uuid
.refTableName
);
860 /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also
861 * ensures that any tables referenced by 'column' will be replicated, even if
862 * no columns in that table are selected for replication (see
863 * ovsdb_idl_add_table() for more information).
865 * This function is only useful if 'monitor_everything_by_default' was false in
866 * the call to ovsdb_idl_create(). This function should be called between
867 * ovsdb_idl_create() and the first call to ovsdb_idl_run().
870 ovsdb_idl_add_column(struct ovsdb_idl
*idl
,
871 const struct ovsdb_idl_column
*column
)
873 *ovsdb_idl_get_mode(idl
, column
) = OVSDB_IDL_MONITOR
| OVSDB_IDL_ALERT
;
874 add_ref_table(idl
, &column
->type
.key
);
875 add_ref_table(idl
, &column
->type
.value
);
878 /* Ensures that the table with class 'tc' will be replicated on 'idl' even if
879 * no columns are selected for replication. Just the necessary data for table
880 * references will be replicated (the UUID of the rows, for instance), any
881 * columns not selected for replication will remain unreplicated.
882 * This can be useful because it allows 'idl' to keep track of what rows in the
883 * table actually exist, which in turn allows columns that reference the table
884 * to have accurate contents. (The IDL presents the database with references to
885 * rows that do not exist removed.)
887 * This function is only useful if 'monitor_everything_by_default' was false in
888 * the call to ovsdb_idl_create(). This function should be called between
889 * ovsdb_idl_create() and the first call to ovsdb_idl_run().
892 ovsdb_idl_add_table(struct ovsdb_idl
*idl
,
893 const struct ovsdb_idl_table_class
*tc
)
897 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
898 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
900 if (table
->class == tc
) {
901 table
->need_table
= true;
909 /* A single clause within an ovsdb_idl_condition. */
910 struct ovsdb_idl_clause
{
911 struct hmap_node hmap_node
; /* In struct ovsdb_idl_condition. */
912 enum ovsdb_function function
; /* Never OVSDB_F_TRUE or OVSDB_F_FALSE. */
913 const struct ovsdb_idl_column
*column
; /* Must be nonnull. */
914 struct ovsdb_datum arg
; /* Has ovsdb_type ->column->type. */
918 ovsdb_idl_clause_hash(const struct ovsdb_idl_clause
*clause
)
920 uint32_t hash
= hash_pointer(clause
->column
, clause
->function
);
921 return ovsdb_datum_hash(&clause
->arg
, &clause
->column
->type
, hash
);
925 ovsdb_idl_clause_equals(const struct ovsdb_idl_clause
*a
,
926 const struct ovsdb_idl_clause
*b
)
928 return (a
->function
== b
->function
929 && a
->column
== b
->column
930 && ovsdb_datum_equals(&a
->arg
, &b
->arg
, &a
->column
->type
));
934 ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause
*clause
)
936 const char *function
= ovsdb_function_to_string(clause
->function
);
937 return json_array_create_3(json_string_create(clause
->column
->name
),
938 json_string_create(function
),
939 ovsdb_datum_to_json(&clause
->arg
,
940 &clause
->column
->type
));
944 ovsdb_idl_clause_destroy(struct ovsdb_idl_clause
*clause
)
947 ovsdb_datum_destroy(&clause
->arg
, &clause
->column
->type
);
952 /* ovsdb_idl_condition. */
955 ovsdb_idl_condition_init(struct ovsdb_idl_condition
*cnd
)
957 hmap_init(&cnd
->clauses
);
958 cnd
->is_true
= false;
962 ovsdb_idl_condition_destroy(struct ovsdb_idl_condition
*cond
)
965 ovsdb_idl_condition_clear(cond
);
966 hmap_destroy(&cond
->clauses
);
971 ovsdb_idl_condition_clear(struct ovsdb_idl_condition
*cond
)
973 struct ovsdb_idl_clause
*clause
, *next
;
974 HMAP_FOR_EACH_SAFE (clause
, next
, hmap_node
, &cond
->clauses
) {
975 hmap_remove(&cond
->clauses
, &clause
->hmap_node
);
976 ovsdb_idl_clause_destroy(clause
);
978 cond
->is_true
= false;
982 ovsdb_idl_condition_is_true(const struct ovsdb_idl_condition
*condition
)
984 return condition
->is_true
;
987 static struct ovsdb_idl_clause
*
988 ovsdb_idl_condition_find_clause(const struct ovsdb_idl_condition
*condition
,
989 const struct ovsdb_idl_clause
*target
,
992 struct ovsdb_idl_clause
*clause
;
993 HMAP_FOR_EACH_WITH_HASH (clause
, hmap_node
, hash
, &condition
->clauses
) {
994 if (ovsdb_idl_clause_equals(clause
, target
)) {
1002 ovsdb_idl_condition_add_clause__(struct ovsdb_idl_condition
*condition
,
1003 const struct ovsdb_idl_clause
*src
,
1006 struct ovsdb_idl_clause
*clause
= xmalloc(sizeof *clause
);
1007 clause
->function
= src
->function
;
1008 clause
->column
= src
->column
;
1009 ovsdb_datum_clone(&clause
->arg
, &src
->arg
, &src
->column
->type
);
1010 hmap_insert(&condition
->clauses
, &clause
->hmap_node
, hash
);
1013 /* Adds a clause to the condition for replicating the table with class 'tc' in
1016 * The IDL replicates only rows in a table that satisfy at least one clause in
1017 * the table's condition. The default condition for a table has a single
1018 * clause with function OVSDB_F_TRUE, so that the IDL replicates all rows in
1019 * the table. When the IDL client replaces the default condition by one of its
1020 * own, the condition can have any number of clauses. If it has no conditions,
1021 * then no rows are replicated.
1023 * Two distinct of clauses can usefully be added:
1025 * - A 'function' of OVSDB_F_TRUE. A "true" clause causes every row to be
1026 * replicated, regardless of whether other clauses exist. 'column' and
1027 * 'arg' are ignored.
1029 * - Binary 'functions' add a clause of the form "<column> <function>
1030 * <arg>", e.g. "column == 5" or "column <= 10". In this case, 'arg' must
1031 * have a type that is compatible with 'column'.
1034 ovsdb_idl_condition_add_clause(struct ovsdb_idl_condition
*condition
,
1035 enum ovsdb_function function
,
1036 const struct ovsdb_idl_column
*column
,
1037 const struct ovsdb_datum
*arg
)
1039 if (condition
->is_true
) {
1040 /* Adding a clause to an always-true condition has no effect. */
1041 } else if (function
== OVSDB_F_TRUE
) {
1042 ovsdb_idl_condition_add_clause_true(condition
);
1043 } else if (function
== OVSDB_F_FALSE
) {
1044 /* Adding a "false" clause never has any effect. */
1046 struct ovsdb_idl_clause clause
= {
1047 .function
= function
,
1051 uint32_t hash
= ovsdb_idl_clause_hash(&clause
);
1052 if (!ovsdb_idl_condition_find_clause(condition
, &clause
, hash
)) {
1053 ovsdb_idl_condition_add_clause__(condition
, &clause
, hash
);
1059 ovsdb_idl_condition_add_clause_true(struct ovsdb_idl_condition
*condition
)
1061 if (!condition
->is_true
) {
1062 ovsdb_idl_condition_clear(condition
);
1063 condition
->is_true
= true;
1068 ovsdb_idl_condition_equals(const struct ovsdb_idl_condition
*a
,
1069 const struct ovsdb_idl_condition
*b
)
1071 if (hmap_count(&a
->clauses
) != hmap_count(&b
->clauses
)) {
1074 if (a
->is_true
!= b
->is_true
) {
1078 const struct ovsdb_idl_clause
*clause
;
1079 HMAP_FOR_EACH (clause
, hmap_node
, &a
->clauses
) {
1080 if (!ovsdb_idl_condition_find_clause(b
, clause
,
1081 clause
->hmap_node
.hash
)) {
1089 ovsdb_idl_condition_clone(struct ovsdb_idl_condition
*dst
,
1090 const struct ovsdb_idl_condition
*src
)
1092 ovsdb_idl_condition_init(dst
);
1094 dst
->is_true
= src
->is_true
;
1096 const struct ovsdb_idl_clause
*clause
;
1097 HMAP_FOR_EACH (clause
, hmap_node
, &src
->clauses
) {
1098 ovsdb_idl_condition_add_clause__(dst
, clause
, clause
->hmap_node
.hash
);
1102 /* Sets the replication condition for 'tc' in 'idl' to 'condition' and
1103 * arranges to send the new condition to the database server.
1105 * Return the next conditional update sequence number. When this
1106 * value and ovsdb_idl_get_condition_seqno() matchs, the 'idl'
1107 * contains rows that match the 'condition'.
1110 ovsdb_idl_set_condition(struct ovsdb_idl
*idl
,
1111 const struct ovsdb_idl_table_class
*tc
,
1112 const struct ovsdb_idl_condition
*condition
)
1114 struct ovsdb_idl_table
*table
= ovsdb_idl_table_from_class(idl
, tc
);
1115 unsigned int seqno
= idl
->cond_seqno
;
1116 if (!ovsdb_idl_condition_equals(condition
, &table
->condition
)) {
1117 ovsdb_idl_condition_destroy(&table
->condition
);
1118 ovsdb_idl_condition_clone(&table
->condition
, condition
);
1119 idl
->cond_changed
= table
->cond_changed
= true;
1120 poll_immediate_wake();
1127 static struct json
*
1128 ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition
*cnd
)
1131 return json_array_create_empty();
1134 size_t n
= hmap_count(&cnd
->clauses
);
1136 return json_array_create_1(json_boolean_create(false));
1139 struct json
**clauses
= xmalloc(n
* sizeof *clauses
);
1140 const struct ovsdb_idl_clause
*clause
;
1142 HMAP_FOR_EACH (clause
, hmap_node
, &cnd
->clauses
) {
1143 clauses
[i
++] = ovsdb_idl_clause_to_json(clause
);
1146 return json_array_create(clauses
, n
);
1149 static struct json
*
1150 ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table
*table
)
1152 const struct ovsdb_idl_condition
*cond
= &table
->condition
;
1153 struct json
*monitor_cond_change_request
= json_object_create();
1154 struct json
*cond_json
= ovsdb_idl_condition_to_json(cond
);
1156 json_object_put(monitor_cond_change_request
, "where", cond_json
);
1158 return monitor_cond_change_request
;
1162 ovsdb_idl_send_cond_change(struct ovsdb_idl
*idl
)
1165 char uuid
[UUID_LEN
+ 1];
1166 struct json
*params
, *json_uuid
;
1167 struct jsonrpc_msg
*request
;
1169 /* When 'idl-request_id' is not NULL, there is an outstanding
1170 * conditional monitoring update request that we have not heard
1171 * from the server yet. Don't generate another request in this case. */
1172 if (!idl
->cond_changed
|| !jsonrpc_session_is_connected(idl
->session
) ||
1173 idl
->state
!= IDL_S_MONITORING_COND
|| idl
->request_id
) {
1177 struct json
*monitor_cond_change_requests
= NULL
;
1179 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1180 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1182 if (table
->cond_changed
) {
1183 struct json
*req
= ovsdb_idl_create_cond_change_req(table
);
1185 if (!monitor_cond_change_requests
) {
1186 monitor_cond_change_requests
= json_object_create();
1188 json_object_put(monitor_cond_change_requests
,
1190 json_array_create_1(req
));
1192 table
->cond_changed
= false;
1196 /* Send request if not empty. */
1197 if (monitor_cond_change_requests
) {
1198 snprintf(uuid
, sizeof uuid
, UUID_FMT
,
1199 UUID_ARGS(&idl
->uuid
));
1200 json_uuid
= json_string_create(uuid
);
1202 /* Create a new uuid */
1203 uuid_generate(&idl
->uuid
);
1204 snprintf(uuid
, sizeof uuid
, UUID_FMT
,
1205 UUID_ARGS(&idl
->uuid
));
1206 params
= json_array_create_3(json_uuid
, json_string_create(uuid
),
1207 monitor_cond_change_requests
);
1209 request
= jsonrpc_create_request("monitor_cond_change", params
,
1211 jsonrpc_session_send(idl
->session
, request
);
1213 idl
->cond_changed
= false;
1216 /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'.
1218 * This function should be called between ovsdb_idl_create() and the first call
1219 * to ovsdb_idl_run().
1222 ovsdb_idl_omit_alert(struct ovsdb_idl
*idl
,
1223 const struct ovsdb_idl_column
*column
)
1225 *ovsdb_idl_get_mode(idl
, column
) &= ~OVSDB_IDL_ALERT
;
1228 /* Sets the mode for 'column' in 'idl' to 0. See the big comment above
1229 * OVSDB_IDL_MONITOR for details.
1231 * This function should be called between ovsdb_idl_create() and the first call
1232 * to ovsdb_idl_run().
1235 ovsdb_idl_omit(struct ovsdb_idl
*idl
, const struct ovsdb_idl_column
*column
)
1237 *ovsdb_idl_get_mode(idl
, column
) = 0;
1240 /* Returns the most recent IDL change sequence number that caused a
1241 * insert, modify or delete update to the table with class 'table_class'.
1244 ovsdb_idl_table_get_seqno(const struct ovsdb_idl
*idl
,
1245 const struct ovsdb_idl_table_class
*table_class
)
1247 struct ovsdb_idl_table
*table
1248 = ovsdb_idl_table_from_class(idl
, table_class
);
1249 unsigned int max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_INSERT
];
1251 if (max_seqno
< table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
]) {
1252 max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
];
1254 if (max_seqno
< table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]) {
1255 max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
];
1260 /* For each row that contains tracked columns, IDL stores the most
1261 * recent IDL change sequence numbers associateed with insert, modify
1262 * and delete updates to the table.
1265 ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row
*row
,
1266 enum ovsdb_idl_change change
)
1268 return row
->change_seqno
[change
];
1271 /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that
1272 * all rows whose 'column' is modified are traced. Similarly, insert
1273 * or delete of rows having 'column' are tracked. Clients are able
1274 * to retrive the tracked rows with the ovsdb_idl_track_get_*()
1277 * This function should be called between ovsdb_idl_create() and
1278 * the first call to ovsdb_idl_run(). The column to be tracked
1279 * should have OVSDB_IDL_ALERT turned on.
1282 ovsdb_idl_track_add_column(struct ovsdb_idl
*idl
,
1283 const struct ovsdb_idl_column
*column
)
1285 if (!(*ovsdb_idl_get_mode(idl
, column
) & OVSDB_IDL_ALERT
)) {
1286 ovsdb_idl_add_column(idl
, column
);
1288 *ovsdb_idl_get_mode(idl
, column
) |= OVSDB_IDL_TRACK
;
1292 ovsdb_idl_track_add_all(struct ovsdb_idl
*idl
)
1296 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1297 const struct ovsdb_idl_table_class
*tc
= &idl
->class->tables
[i
];
1299 for (j
= 0; j
< tc
->n_columns
; j
++) {
1300 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
1301 ovsdb_idl_track_add_column(idl
, column
);
1306 /* Returns true if 'table' has any tracked column. */
1308 ovsdb_idl_track_is_set(struct ovsdb_idl_table
*table
)
1312 for (i
= 0; i
< table
->class->n_columns
; i
++) {
1313 if (table
->modes
[i
] & OVSDB_IDL_TRACK
) {
1320 /* Returns the first tracked row in table with class 'table_class'
1321 * for the specified 'idl'. Returns NULL if there are no tracked rows */
1322 const struct ovsdb_idl_row
*
1323 ovsdb_idl_track_get_first(const struct ovsdb_idl
*idl
,
1324 const struct ovsdb_idl_table_class
*table_class
)
1326 struct ovsdb_idl_table
*table
1327 = ovsdb_idl_table_from_class(idl
, table_class
);
1329 if (!ovs_list_is_empty(&table
->track_list
)) {
1330 return CONTAINER_OF(ovs_list_front(&table
->track_list
), struct ovsdb_idl_row
, track_node
);
1335 /* Returns the next tracked row in table after the specified 'row'
1336 * (in no particular order). Returns NULL if there are no tracked rows */
1337 const struct ovsdb_idl_row
*
1338 ovsdb_idl_track_get_next(const struct ovsdb_idl_row
*row
)
1340 if (row
->track_node
.next
!= &row
->table
->track_list
) {
1341 return CONTAINER_OF(row
->track_node
.next
, struct ovsdb_idl_row
, track_node
);
1347 /* Returns true if a tracked 'column' in 'row' was updated by IDL, false
1348 * otherwise. The tracking data is cleared by ovsdb_idl_track_clear()
1350 * Function returns false if 'column' is not tracked (see
1351 * ovsdb_idl_track_add_column()).
1354 ovsdb_idl_track_is_updated(const struct ovsdb_idl_row
*row
,
1355 const struct ovsdb_idl_column
*column
)
1357 const struct ovsdb_idl_table_class
*class;
1360 class = row
->table
->class;
1361 column_idx
= column
- class->columns
;
1363 if (row
->updated
&& bitmap_is_set(row
->updated
, column_idx
)) {
1370 /* Flushes the tracked rows. Client calls this function after calling
1371 * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*()
1372 * functions. This is usually done at the end of the client's processing
1373 * loop when it is ready to do ovsdb_idl_run() again.
1376 ovsdb_idl_track_clear(const struct ovsdb_idl
*idl
)
1380 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1381 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1383 if (!ovs_list_is_empty(&table
->track_list
)) {
1384 struct ovsdb_idl_row
*row
, *next
;
1386 LIST_FOR_EACH_SAFE(row
, next
, track_node
, &table
->track_list
) {
1389 row
->updated
= NULL
;
1391 ovs_list_remove(&row
->track_node
);
1392 ovs_list_init(&row
->track_node
);
1393 if (ovsdb_idl_row_is_orphan(row
)) {
1394 ovsdb_idl_row_clear_old(row
);
1404 ovsdb_idl_send_schema_request(struct ovsdb_idl
*idl
)
1406 struct jsonrpc_msg
*msg
;
1408 json_destroy(idl
->request_id
);
1409 msg
= jsonrpc_create_request(
1411 json_array_create_1(json_string_create(idl
->class->database
)),
1413 jsonrpc_session_send(idl
->session
, msg
);
1417 log_error(struct ovsdb_error
*error
)
1419 char *s
= ovsdb_error_to_string(error
);
1420 VLOG_WARN("error parsing database schema: %s", s
);
1422 ovsdb_error_destroy(error
);
1425 /* Frees 'schema', which is in the format returned by parse_schema(). */
1427 free_schema(struct shash
*schema
)
1430 struct shash_node
*node
, *next
;
1432 SHASH_FOR_EACH_SAFE (node
, next
, schema
) {
1433 struct sset
*sset
= node
->data
;
1436 shash_delete(schema
, node
);
1438 shash_destroy(schema
);
1443 /* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC
1444 * 7047, to obtain the names of its rows and columns. If successful, returns
1445 * an shash whose keys are table names and whose values are ssets, where each
1446 * sset contains the names of its table's columns. On failure (due to a parse
1447 * error), returns NULL.
1449 * It would also be possible to use the general-purpose OVSDB schema parser in
1450 * ovsdb-server, but that's overkill, possibly too strict for the current use
1451 * case, and would require restructuring ovsdb-server to separate the schema
1452 * code from the rest. */
1453 static struct shash
*
1454 parse_schema(const struct json
*schema_json
)
1456 struct ovsdb_parser parser
;
1457 const struct json
*tables_json
;
1458 struct ovsdb_error
*error
;
1459 struct shash_node
*node
;
1460 struct shash
*schema
;
1462 ovsdb_parser_init(&parser
, schema_json
, "database schema");
1463 tables_json
= ovsdb_parser_member(&parser
, "tables", OP_OBJECT
);
1464 error
= ovsdb_parser_destroy(&parser
);
1470 schema
= xmalloc(sizeof *schema
);
1472 SHASH_FOR_EACH (node
, json_object(tables_json
)) {
1473 const char *table_name
= node
->name
;
1474 const struct json
*json
= node
->data
;
1475 const struct json
*columns_json
;
1477 ovsdb_parser_init(&parser
, json
, "table schema for table %s",
1479 columns_json
= ovsdb_parser_member(&parser
, "columns", OP_OBJECT
);
1480 error
= ovsdb_parser_destroy(&parser
);
1483 free_schema(schema
);
1487 struct sset
*columns
= xmalloc(sizeof *columns
);
1490 struct shash_node
*node2
;
1491 SHASH_FOR_EACH (node2
, json_object(columns_json
)) {
1492 const char *column_name
= node2
->name
;
1493 sset_add(columns
, column_name
);
1495 shash_add(schema
, table_name
, columns
);
1501 ovsdb_idl_send_monitor_request__(struct ovsdb_idl
*idl
,
1504 struct shash
*schema
;
1505 struct json
*monitor_requests
;
1506 struct jsonrpc_msg
*msg
;
1507 char uuid
[UUID_LEN
+ 1];
1510 schema
= parse_schema(idl
->schema
);
1511 monitor_requests
= json_object_create();
1512 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1513 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1514 const struct ovsdb_idl_table_class
*tc
= table
->class;
1515 struct json
*monitor_request
, *columns
, *where
;
1516 const struct sset
*table_schema
;
1519 table_schema
= (schema
1520 ? shash_find_data(schema
, table
->class->name
)
1523 columns
= table
->need_table
? json_array_create_empty() : NULL
;
1524 for (j
= 0; j
< tc
->n_columns
; j
++) {
1525 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
1526 if (table
->modes
[j
] & OVSDB_IDL_MONITOR
) {
1528 && !sset_contains(table_schema
, column
->name
)) {
1529 VLOG_WARN("%s table in %s database lacks %s column "
1530 "(database needs upgrade?)",
1531 table
->class->name
, idl
->class->database
,
1536 columns
= json_array_create_empty();
1538 json_array_add(columns
, json_string_create(column
->name
));
1543 if (schema
&& !table_schema
) {
1544 VLOG_WARN("%s database lacks %s table "
1545 "(database needs upgrade?)",
1546 idl
->class->database
, table
->class->name
);
1547 json_destroy(columns
);
1551 monitor_request
= json_object_create();
1552 json_object_put(monitor_request
, "columns", columns
);
1553 if (!strcmp(method
, "monitor_cond")
1554 && !ovsdb_idl_condition_is_true(&table
->condition
)) {
1555 where
= ovsdb_idl_condition_to_json(&table
->condition
);
1556 json_object_put(monitor_request
, "where", where
);
1557 table
->cond_changed
= false;
1559 json_object_put(monitor_requests
, tc
->name
, monitor_request
);
1562 free_schema(schema
);
1564 json_destroy(idl
->request_id
);
1566 snprintf(uuid
, sizeof uuid
, UUID_FMT
, UUID_ARGS(&idl
->uuid
));
1567 msg
= jsonrpc_create_request(
1569 json_array_create_3(json_string_create(idl
->class->database
),
1570 json_string_create(uuid
), monitor_requests
),
1572 jsonrpc_session_send(idl
->session
, msg
);
1573 idl
->cond_changed
= false;
1577 ovsdb_idl_send_monitor_request(struct ovsdb_idl
*idl
)
1579 ovsdb_idl_send_monitor_request__(idl
, "monitor");
1583 log_parse_update_error(struct ovsdb_error
*error
)
1585 if (!VLOG_DROP_WARN(&syntax_rl
)) {
1586 char *s
= ovsdb_error_to_string(error
);
1587 VLOG_WARN_RL(&syntax_rl
, "%s", s
);
1590 ovsdb_error_destroy(error
);
1594 ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl
*idl
)
1596 ovsdb_idl_send_monitor_request__(idl
, "monitor_cond");
1600 ovsdb_idl_parse_update(struct ovsdb_idl
*idl
, const struct json
*table_updates
,
1601 enum ovsdb_update_version version
)
1603 struct ovsdb_error
*error
= ovsdb_idl_parse_update__(idl
, table_updates
,
1606 log_parse_update_error(error
);
1610 static struct ovsdb_error
*
1611 ovsdb_idl_parse_update__(struct ovsdb_idl
*idl
,
1612 const struct json
*table_updates
,
1613 enum ovsdb_update_version version
)
1615 const struct shash_node
*tables_node
;
1616 const char *table_updates_name
= table_updates_names
[version
];
1617 const char *table_update_name
= table_update_names
[version
];
1618 const char *row_update_name
= row_update_names
[version
];
1620 if (table_updates
->type
!= JSON_OBJECT
) {
1621 return ovsdb_syntax_error(table_updates
, NULL
,
1622 "<%s> is not an object",
1623 table_updates_name
);
1626 SHASH_FOR_EACH (tables_node
, json_object(table_updates
)) {
1627 const struct json
*table_update
= tables_node
->data
;
1628 const struct shash_node
*table_node
;
1629 struct ovsdb_idl_table
*table
;
1631 table
= shash_find_data(&idl
->table_by_name
, tables_node
->name
);
1633 return ovsdb_syntax_error(
1634 table_updates
, NULL
,
1635 "<%s> includes unknown table \"%s\"",
1640 if (table_update
->type
!= JSON_OBJECT
) {
1641 return ovsdb_syntax_error(table_update
, NULL
,
1642 "<%s> for table \"%s\" is "
1645 table
->class->name
);
1647 SHASH_FOR_EACH (table_node
, json_object(table_update
)) {
1648 const struct json
*row_update
= table_node
->data
;
1649 const struct json
*old_json
, *new_json
;
1652 if (!uuid_from_string(&uuid
, table_node
->name
)) {
1653 return ovsdb_syntax_error(table_update
, NULL
,
1654 "<%s> for table \"%s\" "
1655 "contains bad UUID "
1656 "\"%s\" as member name",
1661 if (row_update
->type
!= JSON_OBJECT
) {
1662 return ovsdb_syntax_error(row_update
, NULL
,
1663 "<%s> for table \"%s\" "
1664 "contains <%s> for %s that "
1674 old_json
= shash_find_data(json_object(row_update
), "old");
1675 new_json
= shash_find_data(json_object(row_update
), "new");
1676 if (old_json
&& old_json
->type
!= JSON_OBJECT
) {
1677 return ovsdb_syntax_error(old_json
, NULL
,
1678 "\"old\" <row> is not object");
1679 } else if (new_json
&& new_json
->type
!= JSON_OBJECT
) {
1680 return ovsdb_syntax_error(new_json
, NULL
,
1681 "\"new\" <row> is not object");
1682 } else if ((old_json
!= NULL
) + (new_json
!= NULL
)
1683 != shash_count(json_object(row_update
))) {
1684 return ovsdb_syntax_error(row_update
, NULL
,
1685 "<row-update> contains "
1686 "unexpected member");
1687 } else if (!old_json
&& !new_json
) {
1688 return ovsdb_syntax_error(row_update
, NULL
,
1689 "<row-update> missing \"old\" "
1690 "and \"new\" members");
1693 if (ovsdb_idl_process_update(table
, &uuid
, old_json
,
1695 idl
->change_seqno
++;
1699 case OVSDB_UPDATE2
: {
1700 const char *ops
[] = {"modify", "insert", "delete", "initial"};
1701 const char *operation
;
1702 const struct json
*row
;
1705 for (i
= 0; i
< ARRAY_SIZE(ops
); i
++) {
1707 row
= shash_find_data(json_object(row_update
), operation
);
1710 if (ovsdb_idl_process_update2(table
, &uuid
, operation
,
1712 idl
->change_seqno
++;
1718 /* row_update2 should contain one of the objects */
1719 if (i
== ARRAY_SIZE(ops
)) {
1720 return ovsdb_syntax_error(row_update
, NULL
,
1721 "<row_update2> includes unknown "
1736 static struct ovsdb_idl_row
*
1737 ovsdb_idl_get_row(struct ovsdb_idl_table
*table
, const struct uuid
*uuid
)
1739 struct ovsdb_idl_row
*row
;
1741 HMAP_FOR_EACH_WITH_HASH (row
, hmap_node
, uuid_hash(uuid
), &table
->rows
) {
1742 if (uuid_equals(&row
->uuid
, uuid
)) {
1749 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1752 ovsdb_idl_process_update(struct ovsdb_idl_table
*table
,
1753 const struct uuid
*uuid
, const struct json
*old
,
1754 const struct json
*new)
1756 struct ovsdb_idl_row
*row
;
1758 row
= ovsdb_idl_get_row(table
, uuid
);
1761 if (row
&& !ovsdb_idl_row_is_orphan(row
)) {
1762 /* XXX perhaps we should check the 'old' values? */
1763 ovsdb_idl_delete_row(row
);
1765 VLOG_WARN_RL(&semantic_rl
, "cannot delete missing row "UUID_FMT
" "
1767 UUID_ARGS(uuid
), table
->class->name
);
1773 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), new);
1774 } else if (ovsdb_idl_row_is_orphan(row
)) {
1775 ovsdb_idl_insert_row(row
, new);
1777 VLOG_WARN_RL(&semantic_rl
, "cannot add existing row "UUID_FMT
" to "
1778 "table %s", UUID_ARGS(uuid
), table
->class->name
);
1779 return ovsdb_idl_modify_row(row
, new);
1784 /* XXX perhaps we should check the 'old' values? */
1785 if (!ovsdb_idl_row_is_orphan(row
)) {
1786 return ovsdb_idl_modify_row(row
, new);
1788 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing but "
1789 "referenced row "UUID_FMT
" in table %s",
1790 UUID_ARGS(uuid
), table
->class->name
);
1791 ovsdb_idl_insert_row(row
, new);
1794 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing row "UUID_FMT
" "
1795 "in table %s", UUID_ARGS(uuid
), table
->class->name
);
1796 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), new);
1803 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1806 ovsdb_idl_process_update2(struct ovsdb_idl_table
*table
,
1807 const struct uuid
*uuid
,
1808 const char *operation
,
1809 const struct json
*json_row
)
1811 struct ovsdb_idl_row
*row
;
1813 row
= ovsdb_idl_get_row(table
, uuid
);
1814 if (!strcmp(operation
, "delete")) {
1816 if (row
&& !ovsdb_idl_row_is_orphan(row
)) {
1817 ovsdb_idl_delete_row(row
);
1819 VLOG_WARN_RL(&semantic_rl
, "cannot delete missing row "UUID_FMT
" "
1821 UUID_ARGS(uuid
), table
->class->name
);
1824 } else if (!strcmp(operation
, "insert") || !strcmp(operation
, "initial")) {
1827 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), json_row
);
1828 } else if (ovsdb_idl_row_is_orphan(row
)) {
1829 ovsdb_idl_insert_row(row
, json_row
);
1831 VLOG_WARN_RL(&semantic_rl
, "cannot add existing row "UUID_FMT
" to "
1832 "table %s", UUID_ARGS(uuid
), table
->class->name
);
1833 ovsdb_idl_delete_row(row
);
1834 ovsdb_idl_insert_row(row
, json_row
);
1836 } else if (!strcmp(operation
, "modify")) {
1839 if (!ovsdb_idl_row_is_orphan(row
)) {
1840 return ovsdb_idl_modify_row_by_diff(row
, json_row
);
1842 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing but "
1843 "referenced row "UUID_FMT
" in table %s",
1844 UUID_ARGS(uuid
), table
->class->name
);
1848 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing row "UUID_FMT
" "
1849 "in table %s", UUID_ARGS(uuid
), table
->class->name
);
1853 VLOG_WARN_RL(&semantic_rl
, "unknown operation %s to "
1854 "table %s", operation
, table
->class->name
);
1861 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1864 * Change 'row' either with the content of 'row_json' or by apply 'diff'.
1865 * Caller needs to provide either valid 'row_json' or 'diff', but not
1868 ovsdb_idl_row_change__(struct ovsdb_idl_row
*row
, const struct json
*row_json
,
1869 const struct json
*diff_json
,
1870 enum ovsdb_idl_change change
)
1872 struct ovsdb_idl_table
*table
= row
->table
;
1873 const struct ovsdb_idl_table_class
*class = table
->class;
1874 struct shash_node
*node
;
1875 bool changed
= false;
1876 bool apply_diff
= diff_json
!= NULL
;
1877 const struct json
*json
= apply_diff
? diff_json
: row_json
;
1879 SHASH_FOR_EACH (node
, json_object(json
)) {
1880 const char *column_name
= node
->name
;
1881 const struct ovsdb_idl_column
*column
;
1882 struct ovsdb_datum datum
;
1883 struct ovsdb_error
*error
;
1884 unsigned int column_idx
;
1885 struct ovsdb_datum
*old
;
1887 column
= shash_find_data(&table
->columns
, column_name
);
1889 VLOG_WARN_RL(&syntax_rl
, "unknown column %s updating row "UUID_FMT
,
1890 column_name
, UUID_ARGS(&row
->uuid
));
1894 column_idx
= column
- table
->class->columns
;
1895 old
= &row
->old
[column_idx
];
1899 struct ovsdb_datum diff
;
1901 ovs_assert(!row_json
);
1902 error
= ovsdb_transient_datum_from_json(&diff
, &column
->type
,
1905 error
= ovsdb_datum_apply_diff(&datum
, old
, &diff
,
1907 ovsdb_datum_destroy(&diff
, &column
->type
);
1910 ovs_assert(!diff_json
);
1911 error
= ovsdb_datum_from_json(&datum
, &column
->type
, node
->data
,
1916 if (!ovsdb_datum_equals(old
, &datum
, &column
->type
)) {
1917 ovsdb_datum_swap(old
, &datum
);
1918 if (table
->modes
[column_idx
] & OVSDB_IDL_ALERT
) {
1920 row
->change_seqno
[change
]
1921 = row
->table
->change_seqno
[change
]
1922 = row
->table
->idl
->change_seqno
+ 1;
1923 if (table
->modes
[column_idx
] & OVSDB_IDL_TRACK
) {
1924 if (!ovs_list_is_empty(&row
->track_node
)) {
1925 ovs_list_remove(&row
->track_node
);
1927 ovs_list_push_back(&row
->table
->track_list
,
1929 if (!row
->updated
) {
1930 row
->updated
= bitmap_allocate(class->n_columns
);
1932 bitmap_set1(row
->updated
, column_idx
);
1936 /* Didn't really change but the OVSDB monitor protocol always
1937 * includes every value in a row. */
1940 ovsdb_datum_destroy(&datum
, &column
->type
);
1942 char *s
= ovsdb_error_to_string(error
);
1943 VLOG_WARN_RL(&syntax_rl
, "error parsing column %s in row "UUID_FMT
1944 " in table %s: %s", column_name
,
1945 UUID_ARGS(&row
->uuid
), table
->class->name
, s
);
1947 ovsdb_error_destroy(error
);
1954 ovsdb_idl_row_update(struct ovsdb_idl_row
*row
, const struct json
*row_json
,
1955 enum ovsdb_idl_change change
)
1957 return ovsdb_idl_row_change__(row
, row_json
, NULL
, change
);
1961 ovsdb_idl_row_apply_diff(struct ovsdb_idl_row
*row
,
1962 const struct json
*diff_json
,
1963 enum ovsdb_idl_change change
)
1965 return ovsdb_idl_row_change__(row
, NULL
, diff_json
, change
);
1968 /* When a row A refers to row B through a column with a "refTable" constraint,
1969 * but row B does not exist, row B is called an "orphan row". Orphan rows
1970 * should not persist, because the database enforces referential integrity, but
1971 * they can appear transiently as changes from the database are received (the
1972 * database doesn't try to topologically sort them and circular references mean
1973 * it isn't always possible anyhow).
1975 * This function returns true if 'row' is an orphan row, otherwise false.
1978 ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row
*row
)
1980 return !row
->old
&& !row
->new;
1983 /* Returns true if 'row' is conceptually part of the database as modified by
1984 * the current transaction (if any), false otherwise.
1986 * This function will return true if 'row' is not an orphan (see the comment on
1987 * ovsdb_idl_row_is_orphan()) and:
1989 * - 'row' exists in the database and has not been deleted within the
1990 * current transaction (if any).
1992 * - 'row' was inserted within the current transaction and has not been
1993 * deleted. (In the latter case you should not have passed 'row' in at
1994 * all, because ovsdb_idl_txn_delete() freed it.)
1996 * This function will return false if 'row' is an orphan or if 'row' was
1997 * deleted within the current transaction.
2000 ovsdb_idl_row_exists(const struct ovsdb_idl_row
*row
)
2002 return row
->new != NULL
;
2006 ovsdb_idl_row_parse(struct ovsdb_idl_row
*row
)
2008 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2011 for (i
= 0; i
< class->n_columns
; i
++) {
2012 const struct ovsdb_idl_column
*c
= &class->columns
[i
];
2013 (c
->parse
)(row
, &row
->old
[i
]);
2018 ovsdb_idl_row_unparse(struct ovsdb_idl_row
*row
)
2020 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2023 for (i
= 0; i
< class->n_columns
; i
++) {
2024 const struct ovsdb_idl_column
*c
= &class->columns
[i
];
2030 ovsdb_idl_row_clear_old(struct ovsdb_idl_row
*row
)
2032 ovs_assert(row
->old
== row
->new);
2033 if (!ovsdb_idl_row_is_orphan(row
)) {
2034 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2037 for (i
= 0; i
< class->n_columns
; i
++) {
2038 ovsdb_datum_destroy(&row
->old
[i
], &class->columns
[i
].type
);
2041 row
->old
= row
->new = NULL
;
2046 ovsdb_idl_row_clear_new(struct ovsdb_idl_row
*row
)
2048 if (row
->old
!= row
->new) {
2050 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2054 BITMAP_FOR_EACH_1 (i
, class->n_columns
, row
->written
) {
2055 ovsdb_datum_destroy(&row
->new[i
], &class->columns
[i
].type
);
2060 row
->written
= NULL
;
2062 row
->new = row
->old
;
2067 ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row
*row
, bool destroy_dsts
)
2069 struct ovsdb_idl_arc
*arc
, *next
;
2071 /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
2072 * that this causes to be unreferenced, if tracking is not enabled.
2073 * If tracking is enabled, orphaned nodes are removed from hmap but not
2076 LIST_FOR_EACH_SAFE (arc
, next
, src_node
, &row
->src_arcs
) {
2077 ovs_list_remove(&arc
->dst_node
);
2079 && ovsdb_idl_row_is_orphan(arc
->dst
)
2080 && ovs_list_is_empty(&arc
->dst
->dst_arcs
)) {
2081 ovsdb_idl_row_destroy(arc
->dst
);
2085 ovs_list_init(&row
->src_arcs
);
2088 /* Force nodes that reference 'row' to reparse. */
2090 ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row
*row
)
2092 struct ovsdb_idl_arc
*arc
, *next
;
2094 /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
2095 * 'arc', so we need to use the "safe" variant of list traversal. However,
2096 * calling an ovsdb_idl_column's 'parse' function will add an arc
2097 * equivalent to 'arc' to row->arcs. That could be a problem for
2098 * traversal, but it adds it at the beginning of the list to prevent us
2099 * from stumbling upon it again.
2101 * (If duplicate arcs were possible then we would need to make sure that
2102 * 'next' didn't also point into 'arc''s destination, but we forbid
2103 * duplicate arcs.) */
2104 LIST_FOR_EACH_SAFE (arc
, next
, dst_node
, &row
->dst_arcs
) {
2105 struct ovsdb_idl_row
*ref
= arc
->src
;
2107 ovsdb_idl_row_unparse(ref
);
2108 ovsdb_idl_row_clear_arcs(ref
, false);
2109 ovsdb_idl_row_parse(ref
);
2113 static struct ovsdb_idl_row
*
2114 ovsdb_idl_row_create__(const struct ovsdb_idl_table_class
*class)
2116 struct ovsdb_idl_row
*row
= xzalloc(class->allocation_size
);
2117 class->row_init(row
);
2118 ovs_list_init(&row
->src_arcs
);
2119 ovs_list_init(&row
->dst_arcs
);
2120 hmap_node_nullify(&row
->txn_node
);
2121 ovs_list_init(&row
->track_node
);
2125 static struct ovsdb_idl_row
*
2126 ovsdb_idl_row_create(struct ovsdb_idl_table
*table
, const struct uuid
*uuid
)
2128 struct ovsdb_idl_row
*row
= ovsdb_idl_row_create__(table
->class);
2129 hmap_insert(&table
->rows
, &row
->hmap_node
, uuid_hash(uuid
));
2132 row
->map_op_written
= NULL
;
2133 row
->map_op_lists
= NULL
;
2134 row
->set_op_written
= NULL
;
2135 row
->set_op_lists
= NULL
;
2140 ovsdb_idl_row_destroy(struct ovsdb_idl_row
*row
)
2143 ovsdb_idl_row_clear_old(row
);
2144 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
2145 ovsdb_idl_destroy_all_map_op_lists(row
);
2146 ovsdb_idl_destroy_all_set_op_lists(row
);
2147 if (ovsdb_idl_track_is_set(row
->table
)) {
2148 row
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]
2149 = row
->table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]
2150 = row
->table
->idl
->change_seqno
+ 1;
2152 if (!ovs_list_is_empty(&row
->track_node
)) {
2153 ovs_list_remove(&row
->track_node
);
2155 ovs_list_push_back(&row
->table
->track_list
, &row
->track_node
);
2160 ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row
*row
)
2162 if (row
->map_op_written
) {
2163 /* Clear Map Operation Lists */
2164 size_t idx
, n_columns
;
2165 const struct ovsdb_idl_column
*columns
;
2166 const struct ovsdb_type
*type
;
2167 n_columns
= row
->table
->class->n_columns
;
2168 columns
= row
->table
->class->columns
;
2169 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->map_op_written
) {
2170 type
= &columns
[idx
].type
;
2171 map_op_list_destroy(row
->map_op_lists
[idx
], type
);
2173 free(row
->map_op_lists
);
2174 bitmap_free(row
->map_op_written
);
2175 row
->map_op_lists
= NULL
;
2176 row
->map_op_written
= NULL
;
2181 ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row
*row
)
2183 if (row
->set_op_written
) {
2184 /* Clear Set Operation Lists */
2185 size_t idx
, n_columns
;
2186 const struct ovsdb_idl_column
*columns
;
2187 const struct ovsdb_type
*type
;
2188 n_columns
= row
->table
->class->n_columns
;
2189 columns
= row
->table
->class->columns
;
2190 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->set_op_written
) {
2191 type
= &columns
[idx
].type
;
2192 set_op_list_destroy(row
->set_op_lists
[idx
], type
);
2194 free(row
->set_op_lists
);
2195 bitmap_free(row
->set_op_written
);
2196 row
->set_op_lists
= NULL
;
2197 row
->set_op_written
= NULL
;
2202 ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl
*idl
)
2206 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
2207 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
2209 if (!ovs_list_is_empty(&table
->track_list
)) {
2210 struct ovsdb_idl_row
*row
, *next
;
2212 LIST_FOR_EACH_SAFE(row
, next
, track_node
, &table
->track_list
) {
2213 if (!ovsdb_idl_track_is_set(row
->table
)) {
2214 ovs_list_remove(&row
->track_node
);
2223 ovsdb_idl_insert_row(struct ovsdb_idl_row
*row
, const struct json
*row_json
)
2225 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2228 ovs_assert(!row
->old
&& !row
->new);
2229 row
->old
= row
->new = xmalloc(class->n_columns
* sizeof *row
->old
);
2230 for (i
= 0; i
< class->n_columns
; i
++) {
2231 ovsdb_datum_init_default(&row
->old
[i
], &class->columns
[i
].type
);
2233 ovsdb_idl_row_update(row
, row_json
, OVSDB_IDL_CHANGE_INSERT
);
2234 ovsdb_idl_row_parse(row
);
2236 ovsdb_idl_row_reparse_backrefs(row
);
2240 ovsdb_idl_delete_row(struct ovsdb_idl_row
*row
)
2242 ovsdb_idl_row_unparse(row
);
2243 ovsdb_idl_row_clear_arcs(row
, true);
2244 ovsdb_idl_row_clear_old(row
);
2245 if (ovs_list_is_empty(&row
->dst_arcs
)) {
2246 ovsdb_idl_row_destroy(row
);
2248 ovsdb_idl_row_reparse_backrefs(row
);
2252 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
2255 ovsdb_idl_modify_row(struct ovsdb_idl_row
*row
, const struct json
*row_json
)
2259 ovsdb_idl_row_unparse(row
);
2260 ovsdb_idl_row_clear_arcs(row
, true);
2261 changed
= ovsdb_idl_row_update(row
, row_json
, OVSDB_IDL_CHANGE_MODIFY
);
2262 ovsdb_idl_row_parse(row
);
2268 ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row
*row
,
2269 const struct json
*diff_json
)
2273 ovsdb_idl_row_unparse(row
);
2274 ovsdb_idl_row_clear_arcs(row
, true);
2275 changed
= ovsdb_idl_row_apply_diff(row
, diff_json
,
2276 OVSDB_IDL_CHANGE_MODIFY
);
2277 ovsdb_idl_row_parse(row
);
2283 may_add_arc(const struct ovsdb_idl_row
*src
, const struct ovsdb_idl_row
*dst
)
2285 const struct ovsdb_idl_arc
*arc
;
2292 /* No duplicate arcs.
2294 * We only need to test whether the first arc in dst->dst_arcs originates
2295 * at 'src', since we add all of the arcs from a given source in a clump
2296 * (in a single call to ovsdb_idl_row_parse()) and new arcs are always
2297 * added at the front of the dst_arcs list. */
2298 if (ovs_list_is_empty(&dst
->dst_arcs
)) {
2301 arc
= CONTAINER_OF(dst
->dst_arcs
.next
, struct ovsdb_idl_arc
, dst_node
);
2302 return arc
->src
!= src
;
2305 static struct ovsdb_idl_table
*
2306 ovsdb_idl_table_from_class(const struct ovsdb_idl
*idl
,
2307 const struct ovsdb_idl_table_class
*table_class
)
2309 return &idl
->tables
[table_class
- idl
->class->tables
];
2312 /* Called by ovsdb-idlc generated code. */
2313 struct ovsdb_idl_row
*
2314 ovsdb_idl_get_row_arc(struct ovsdb_idl_row
*src
,
2315 const struct ovsdb_idl_table_class
*dst_table_class
,
2316 const struct uuid
*dst_uuid
)
2318 struct ovsdb_idl
*idl
= src
->table
->idl
;
2319 struct ovsdb_idl_table
*dst_table
;
2320 struct ovsdb_idl_arc
*arc
;
2321 struct ovsdb_idl_row
*dst
;
2323 dst_table
= ovsdb_idl_table_from_class(idl
, dst_table_class
);
2324 dst
= ovsdb_idl_get_row(dst_table
, dst_uuid
);
2326 /* We're being called from ovsdb_idl_txn_write(). We must not update
2327 * any arcs, because the transaction will be backed out at commit or
2328 * abort time and we don't want our graph screwed up.
2330 * Just return the destination row, if there is one and it has not been
2332 if (dst
&& (hmap_node_is_null(&dst
->txn_node
) || dst
->new)) {
2337 /* We're being called from some other context. Update the graph. */
2339 dst
= ovsdb_idl_row_create(dst_table
, dst_uuid
);
2342 /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
2343 if (may_add_arc(src
, dst
)) {
2344 /* The arc *must* be added at the front of the dst_arcs list. See
2345 * ovsdb_idl_row_reparse_backrefs() for details. */
2346 arc
= xmalloc(sizeof *arc
);
2347 ovs_list_push_front(&src
->src_arcs
, &arc
->src_node
);
2348 ovs_list_push_front(&dst
->dst_arcs
, &arc
->dst_node
);
2353 return !ovsdb_idl_row_is_orphan(dst
) ? dst
: NULL
;
2357 /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a
2358 * pointer to the row if there is one, otherwise a null pointer. */
2359 const struct ovsdb_idl_row
*
2360 ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl
*idl
,
2361 const struct ovsdb_idl_table_class
*tc
,
2362 const struct uuid
*uuid
)
2364 return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl
, tc
), uuid
);
2367 static struct ovsdb_idl_row
*
2368 next_real_row(struct ovsdb_idl_table
*table
, struct hmap_node
*node
)
2370 for (; node
; node
= hmap_next(&table
->rows
, node
)) {
2371 struct ovsdb_idl_row
*row
;
2373 row
= CONTAINER_OF(node
, struct ovsdb_idl_row
, hmap_node
);
2374 if (ovsdb_idl_row_exists(row
)) {
2381 /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that
2384 * Database tables are internally maintained as hash tables, so adding or
2385 * removing rows while traversing the same table can cause some rows to be
2386 * visited twice or not at apply. */
2387 const struct ovsdb_idl_row
*
2388 ovsdb_idl_first_row(const struct ovsdb_idl
*idl
,
2389 const struct ovsdb_idl_table_class
*table_class
)
2391 struct ovsdb_idl_table
*table
2392 = ovsdb_idl_table_from_class(idl
, table_class
);
2393 return next_real_row(table
, hmap_first(&table
->rows
));
2396 /* Returns a row following 'row' within its table, or a null pointer if 'row'
2397 * is the last row in its table. */
2398 const struct ovsdb_idl_row
*
2399 ovsdb_idl_next_row(const struct ovsdb_idl_row
*row
)
2401 struct ovsdb_idl_table
*table
= row
->table
;
2403 return next_real_row(table
, hmap_next(&table
->rows
, &row
->hmap_node
));
2406 /* Reads and returns the value of 'column' within 'row'. If an ongoing
2407 * transaction has changed 'column''s value, the modified value is returned.
2409 * The caller must not modify or free the returned value.
2411 * Various kinds of changes can invalidate the returned value: writing to the
2412 * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row'
2413 * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction
2414 * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the
2415 * returned value is needed for a long time, it is best to make a copy of it
2416 * with ovsdb_datum_clone(). */
2417 const struct ovsdb_datum
*
2418 ovsdb_idl_read(const struct ovsdb_idl_row
*row
,
2419 const struct ovsdb_idl_column
*column
)
2421 const struct ovsdb_idl_table_class
*class;
2424 ovs_assert(!ovsdb_idl_row_is_synthetic(row
));
2426 class = row
->table
->class;
2427 column_idx
= column
- class->columns
;
2429 ovs_assert(row
->new != NULL
);
2430 ovs_assert(column_idx
< class->n_columns
);
2432 if (row
->written
&& bitmap_is_set(row
->written
, column_idx
)) {
2433 return &row
->new[column_idx
];
2434 } else if (row
->old
) {
2435 return &row
->old
[column_idx
];
2437 return ovsdb_datum_default(&column
->type
);
2441 /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key
2442 * type 'key_type' and value type 'value_type'. (Scalar and set types will
2443 * have a value type of OVSDB_TYPE_VOID.)
2445 * This is useful in code that "knows" that a particular column has a given
2446 * type, so that it will abort if someone changes the column's type without
2447 * updating the code that uses it. */
2448 const struct ovsdb_datum
*
2449 ovsdb_idl_get(const struct ovsdb_idl_row
*row
,
2450 const struct ovsdb_idl_column
*column
,
2451 enum ovsdb_atomic_type key_type OVS_UNUSED
,
2452 enum ovsdb_atomic_type value_type OVS_UNUSED
)
2454 ovs_assert(column
->type
.key
.type
== key_type
);
2455 ovs_assert(column
->type
.value
.type
== value_type
);
2457 return ovsdb_idl_read(row
, column
);
2460 /* Returns true if the field represented by 'column' in 'row' may be modified,
2461 * false if it is immutable.
2463 * Normally, whether a field is mutable is controlled by its column's schema.
2464 * However, an immutable column can be set to any initial value at the time of
2465 * insertion, so if 'row' is a new row (one that is being added as part of the
2466 * current transaction, supposing that a transaction is in progress) then even
2467 * its "immutable" fields are actually mutable. */
2469 ovsdb_idl_is_mutable(const struct ovsdb_idl_row
*row
,
2470 const struct ovsdb_idl_column
*column
)
2472 return column
->mutable || (row
->new && !row
->old
);
2475 /* Returns false if 'row' was obtained from the IDL, true if it was initialized
2476 * to all-zero-bits by some other entity. If 'row' was set up some other way
2477 * then the return value is indeterminate. */
2479 ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row
*row
)
2481 return row
->table
== NULL
;
2486 static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn
*txn
,
2487 enum ovsdb_idl_txn_status
);
2489 /* Returns a string representation of 'status'. The caller must not modify or
2490 * free the returned string.
2492 * The return value is probably useful only for debug log messages and unit
2495 ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status
)
2498 case TXN_UNCOMMITTED
:
2499 return "uncommitted";
2502 case TXN_INCOMPLETE
:
2503 return "incomplete";
2510 case TXN_NOT_LOCKED
:
2511 return "not locked";
2518 /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single
2519 * active transaction at a time. See the large comment in ovsdb-idl.h for
2520 * general information on transactions. */
2521 struct ovsdb_idl_txn
*
2522 ovsdb_idl_txn_create(struct ovsdb_idl
*idl
)
2524 struct ovsdb_idl_txn
*txn
;
2526 ovs_assert(!idl
->txn
);
2527 idl
->txn
= txn
= xmalloc(sizeof *txn
);
2528 txn
->request_id
= NULL
;
2530 hmap_init(&txn
->txn_rows
);
2531 txn
->status
= TXN_UNCOMMITTED
;
2533 txn
->dry_run
= false;
2534 ds_init(&txn
->comment
);
2536 txn
->inc_table
= NULL
;
2537 txn
->inc_column
= NULL
;
2539 hmap_init(&txn
->inserted_rows
);
2544 /* Appends 's', which is treated as a printf()-type format string, to the
2545 * comments that will be passed to the OVSDB server when 'txn' is committed.
2546 * (The comment will be committed to the OVSDB log, which "ovsdb-tool
2547 * show-log" can print in a relatively human-readable form.) */
2549 ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn
*txn
, const char *s
, ...)
2553 if (txn
->comment
.length
) {
2554 ds_put_char(&txn
->comment
, '\n');
2558 ds_put_format_valist(&txn
->comment
, s
, args
);
2562 /* Marks 'txn' as a transaction that will not actually modify the database. In
2563 * almost every way, the transaction is treated like other transactions. It
2564 * must be committed or aborted like other transactions, it will be sent to the
2565 * database server like other transactions, and so on. The only difference is
2566 * that the operations sent to the database server will include, as the last
2567 * step, an "abort" operation, so that any changes made by the transaction will
2568 * not actually take effect. */
2570 ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn
*txn
)
2572 txn
->dry_run
= true;
2575 /* Causes 'txn', when committed, to increment the value of 'column' within
2576 * 'row' by 1. 'column' must have an integer type. After 'txn' commits
2577 * successfully, the client may retrieve the final (incremented) value of
2578 * 'column' with ovsdb_idl_txn_get_increment_new_value().
2580 * If at time of commit the transaction is otherwise empty, that is, it doesn't
2581 * change the database, then 'force' is important. If 'force' is false in this
2582 * case, the IDL suppresses the increment and skips a round trip to the
2583 * database server. If 'force' is true, the IDL will still increment the
2586 * The client could accomplish something similar with ovsdb_idl_read(),
2587 * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc
2588 * generated wrappers for these functions. However, ovsdb_idl_txn_increment()
2589 * will never (by itself) fail because of a verify error.
2591 * The intended use is for incrementing the "next_cfg" column in the
2592 * Open_vSwitch table. */
2594 ovsdb_idl_txn_increment(struct ovsdb_idl_txn
*txn
,
2595 const struct ovsdb_idl_row
*row
,
2596 const struct ovsdb_idl_column
*column
,
2599 ovs_assert(!txn
->inc_table
);
2600 ovs_assert(column
->type
.key
.type
== OVSDB_TYPE_INTEGER
);
2601 ovs_assert(column
->type
.value
.type
== OVSDB_TYPE_VOID
);
2603 txn
->inc_table
= row
->table
->class->name
;
2604 txn
->inc_column
= column
->name
;
2605 txn
->inc_row
= row
->uuid
;
2606 txn
->inc_force
= force
;
2609 /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit()
2610 * has been called for 'txn' but the commit is still incomplete (that is, the
2611 * last call returned TXN_INCOMPLETE) then the transaction may or may not still
2612 * end up committing at the database server, but the client will not be able to
2613 * get any further status information back. */
2615 ovsdb_idl_txn_destroy(struct ovsdb_idl_txn
*txn
)
2617 struct ovsdb_idl_txn_insert
*insert
, *next
;
2619 json_destroy(txn
->request_id
);
2620 if (txn
->status
== TXN_INCOMPLETE
) {
2621 hmap_remove(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
);
2623 ovsdb_idl_txn_abort(txn
);
2624 ds_destroy(&txn
->comment
);
2626 HMAP_FOR_EACH_SAFE (insert
, next
, hmap_node
, &txn
->inserted_rows
) {
2629 hmap_destroy(&txn
->inserted_rows
);
2633 /* Causes poll_block() to wake up if 'txn' has completed committing. */
2635 ovsdb_idl_txn_wait(const struct ovsdb_idl_txn
*txn
)
2637 if (txn
->status
!= TXN_UNCOMMITTED
&& txn
->status
!= TXN_INCOMPLETE
) {
2638 poll_immediate_wake();
2642 static struct json
*
2643 where_uuid_equals(const struct uuid
*uuid
)
2646 json_array_create_1(
2647 json_array_create_3(
2648 json_string_create("_uuid"),
2649 json_string_create("=="),
2650 json_array_create_2(
2651 json_string_create("uuid"),
2652 json_string_create_nocopy(
2653 xasprintf(UUID_FMT
, UUID_ARGS(uuid
))))));
2657 uuid_name_from_uuid(const struct uuid
*uuid
)
2662 name
= xasprintf("row"UUID_FMT
, UUID_ARGS(uuid
));
2663 for (p
= name
; *p
!= '\0'; p
++) {
2672 static const struct ovsdb_idl_row
*
2673 ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn
*txn
, const struct uuid
*uuid
)
2675 const struct ovsdb_idl_row
*row
;
2677 HMAP_FOR_EACH_WITH_HASH (row
, txn_node
, uuid_hash(uuid
), &txn
->txn_rows
) {
2678 if (uuid_equals(&row
->uuid
, uuid
)) {
2685 /* XXX there must be a cleaner way to do this */
2686 static struct json
*
2687 substitute_uuids(struct json
*json
, const struct ovsdb_idl_txn
*txn
)
2689 if (json
->type
== JSON_ARRAY
) {
2693 if (json
->u
.array
.n
== 2
2694 && json
->u
.array
.elems
[0]->type
== JSON_STRING
2695 && json
->u
.array
.elems
[1]->type
== JSON_STRING
2696 && !strcmp(json
->u
.array
.elems
[0]->u
.string
, "uuid")
2697 && uuid_from_string(&uuid
, json
->u
.array
.elems
[1]->u
.string
)) {
2698 const struct ovsdb_idl_row
*row
;
2700 row
= ovsdb_idl_txn_get_row(txn
, &uuid
);
2701 if (row
&& !row
->old
&& row
->new) {
2704 return json_array_create_2(
2705 json_string_create("named-uuid"),
2706 json_string_create_nocopy(uuid_name_from_uuid(&uuid
)));
2710 for (i
= 0; i
< json
->u
.array
.n
; i
++) {
2711 json
->u
.array
.elems
[i
] = substitute_uuids(json
->u
.array
.elems
[i
],
2714 } else if (json
->type
== JSON_OBJECT
) {
2715 struct shash_node
*node
;
2717 SHASH_FOR_EACH (node
, json_object(json
)) {
2718 node
->data
= substitute_uuids(node
->data
, txn
);
2725 ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn
*txn
)
2727 struct ovsdb_idl_row
*row
, *next
;
2729 /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an
2730 * ovsdb_idl_column's 'parse' function, which will call
2731 * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a
2732 * transaction and fail to update the graph. */
2733 txn
->idl
->txn
= NULL
;
2735 HMAP_FOR_EACH_SAFE (row
, next
, txn_node
, &txn
->txn_rows
) {
2736 ovsdb_idl_destroy_all_map_op_lists(row
);
2737 ovsdb_idl_destroy_all_set_op_lists(row
);
2740 ovsdb_idl_row_unparse(row
);
2741 ovsdb_idl_row_clear_arcs(row
, false);
2742 ovsdb_idl_row_parse(row
);
2745 ovsdb_idl_row_unparse(row
);
2747 ovsdb_idl_row_clear_new(row
);
2750 row
->prereqs
= NULL
;
2753 row
->written
= NULL
;
2755 hmap_remove(&txn
->txn_rows
, &row
->txn_node
);
2756 hmap_node_nullify(&row
->txn_node
);
2758 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
2762 hmap_destroy(&txn
->txn_rows
);
2763 hmap_init(&txn
->txn_rows
);
2767 ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row
*row
,
2768 struct json
*mutations
)
2770 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2772 bool any_mutations
= false;
2774 if (row
->map_op_written
) {
2775 BITMAP_FOR_EACH_1(idx
, class->n_columns
, row
->map_op_written
) {
2776 struct map_op_list
*map_op_list
;
2777 const struct ovsdb_idl_column
*column
;
2778 const struct ovsdb_datum
*old_datum
;
2779 enum ovsdb_atomic_type key_type
, value_type
;
2780 struct json
*mutation
, *map
, *col_name
, *mutator
;
2781 struct json
*del_set
, *ins_map
;
2782 bool any_del
, any_ins
;
2784 map_op_list
= row
->map_op_lists
[idx
];
2785 column
= &class->columns
[idx
];
2786 key_type
= column
->type
.key
.type
;
2787 value_type
= column
->type
.value
.type
;
2789 /* Get the value to be changed */
2790 if (row
->new && row
->written
&& bitmap_is_set(row
->written
,idx
)) {
2791 old_datum
= &row
->new[idx
];
2792 } else if (row
->old
!= NULL
) {
2793 old_datum
= &row
->old
[idx
];
2795 old_datum
= ovsdb_datum_default(&column
->type
);
2798 del_set
= json_array_create_empty();
2799 ins_map
= json_array_create_empty();
2803 for (struct map_op
*map_op
= map_op_list_first(map_op_list
); map_op
;
2804 map_op
= map_op_list_next(map_op_list
, map_op
)) {
2806 if (map_op_type(map_op
) == MAP_OP_UPDATE
) {
2807 /* Find out if value really changed. */
2808 struct ovsdb_datum
*new_datum
;
2810 new_datum
= map_op_datum(map_op
);
2811 pos
= ovsdb_datum_find_key(old_datum
,
2812 &new_datum
->keys
[0],
2814 if (ovsdb_atom_equals(&new_datum
->values
[0],
2815 &old_datum
->values
[pos
],
2817 /* No change in value. Move on to next update. */
2820 } else if (map_op_type(map_op
) == MAP_OP_DELETE
){
2821 /* Verify that there is a key to delete. */
2823 pos
= ovsdb_datum_find_key(old_datum
,
2824 &map_op_datum(map_op
)->keys
[0],
2826 if (pos
== UINT_MAX
) {
2827 /* No key to delete. Move on to next update. */
2828 VLOG_WARN("Trying to delete a key that doesn't "
2829 "exist in the map.");
2834 if (map_op_type(map_op
) == MAP_OP_INSERT
) {
2835 map
= json_array_create_2(
2836 ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2838 ovsdb_atom_to_json(&map_op_datum(map_op
)->values
[0],
2840 json_array_add(ins_map
, map
);
2842 } else { /* MAP_OP_UPDATE or MAP_OP_DELETE */
2843 map
= ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2845 json_array_add(del_set
, map
);
2849 /* Generate an additional insert mutate for updates. */
2850 if (map_op_type(map_op
) == MAP_OP_UPDATE
) {
2851 map
= json_array_create_2(
2852 ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2854 ovsdb_atom_to_json(&map_op_datum(map_op
)->values
[0],
2856 json_array_add(ins_map
, map
);
2862 col_name
= json_string_create(column
->name
);
2863 mutator
= json_string_create("delete");
2864 map
= json_array_create_2(json_string_create("set"), del_set
);
2865 mutation
= json_array_create_3(col_name
, mutator
, map
);
2866 json_array_add(mutations
, mutation
);
2867 any_mutations
= true;
2869 json_destroy(del_set
);
2872 col_name
= json_string_create(column
->name
);
2873 mutator
= json_string_create("insert");
2874 map
= json_array_create_2(json_string_create("map"), ins_map
);
2875 mutation
= json_array_create_3(col_name
, mutator
, map
);
2876 json_array_add(mutations
, mutation
);
2877 any_mutations
= true;
2879 json_destroy(ins_map
);
2883 if (row
->set_op_written
) {
2884 BITMAP_FOR_EACH_1(idx
, class->n_columns
, row
->set_op_written
) {
2885 struct set_op_list
*set_op_list
;
2886 const struct ovsdb_idl_column
*column
;
2887 const struct ovsdb_datum
*old_datum
;
2888 enum ovsdb_atomic_type key_type
;
2889 struct json
*mutation
, *set
, *col_name
, *mutator
;
2890 struct json
*del_set
, *ins_set
;
2891 bool any_del
, any_ins
;
2893 set_op_list
= row
->set_op_lists
[idx
];
2894 column
= &class->columns
[idx
];
2895 key_type
= column
->type
.key
.type
;
2897 /* Get the value to be changed */
2898 if (row
->new && row
->written
&& bitmap_is_set(row
->written
,idx
)) {
2899 old_datum
= &row
->new[idx
];
2900 } else if (row
->old
!= NULL
) {
2901 old_datum
= &row
->old
[idx
];
2903 old_datum
= ovsdb_datum_default(&column
->type
);
2906 del_set
= json_array_create_empty();
2907 ins_set
= json_array_create_empty();
2911 for (struct set_op
*set_op
= set_op_list_first(set_op_list
); set_op
;
2912 set_op
= set_op_list_next(set_op_list
, set_op
)) {
2913 if (set_op_type(set_op
) == SET_OP_INSERT
) {
2914 set
= ovsdb_atom_to_json(&set_op_datum(set_op
)->keys
[0],
2916 json_array_add(ins_set
, set
);
2918 } else { /* SETP_OP_DELETE */
2919 /* Verify that there is a key to delete. */
2921 pos
= ovsdb_datum_find_key(old_datum
,
2922 &set_op_datum(set_op
)->keys
[0],
2924 if (pos
== UINT_MAX
) {
2925 /* No key to delete. Move on to next update. */
2926 VLOG_WARN("Trying to delete a key that doesn't "
2927 "exist in the set.");
2930 set
= ovsdb_atom_to_json(&set_op_datum(set_op
)->keys
[0],
2932 json_array_add(del_set
, set
);
2937 col_name
= json_string_create(column
->name
);
2938 mutator
= json_string_create("delete");
2939 set
= json_array_create_2(json_string_create("set"), del_set
);
2940 mutation
= json_array_create_3(col_name
, mutator
, set
);
2941 json_array_add(mutations
, mutation
);
2942 any_mutations
= true;
2944 json_destroy(del_set
);
2947 col_name
= json_string_create(column
->name
);
2948 mutator
= json_string_create("insert");
2949 set
= json_array_create_2(json_string_create("set"), ins_set
);
2950 mutation
= json_array_create_3(col_name
, mutator
, set
);
2951 json_array_add(mutations
, mutation
);
2952 any_mutations
= true;
2954 json_destroy(ins_set
);
2958 return any_mutations
;
2961 /* Attempts to commit 'txn'. Returns the status of the commit operation, one
2962 * of the following TXN_* constants:
2966 * The transaction is in progress, but not yet complete. The caller
2967 * should call again later, after calling ovsdb_idl_run() to let the IDL
2968 * do OVSDB protocol processing.
2972 * The transaction is complete. (It didn't actually change the database,
2973 * so the IDL didn't send any request to the database server.)
2977 * The caller previously called ovsdb_idl_txn_abort().
2981 * The transaction was successful. The update made by the transaction
2982 * (and possibly other changes made by other database clients) should
2983 * already be visible in the IDL.
2987 * The transaction failed for some transient reason, e.g. because a
2988 * "verify" operation reported an inconsistency or due to a network
2989 * problem. The caller should wait for a change to the database, then
2990 * compose a new transaction, and commit the new transaction.
2992 * Use the return value of ovsdb_idl_get_seqno() to wait for a change in
2993 * the database. It is important to use its return value *before* the
2994 * initial call to ovsdb_idl_txn_commit() as the baseline for this
2995 * purpose, because the change that one should wait for can happen after
2996 * the initial call but before the call that returns TXN_TRY_AGAIN, and
2997 * using some other baseline value in that situation could cause an
2998 * indefinite wait if the database rarely changes.
3002 * The transaction failed because the IDL has been configured to require
3003 * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or
3004 * has already lost it.
3006 * Committing a transaction rolls back all of the changes that it made to the
3007 * IDL's copy of the database. If the transaction commits successfully, then
3008 * the database server will send an update and, thus, the IDL will be updated
3009 * with the committed changes. */
3010 enum ovsdb_idl_txn_status
3011 ovsdb_idl_txn_commit(struct ovsdb_idl_txn
*txn
)
3013 struct ovsdb_idl_row
*row
;
3014 struct json
*operations
;
3017 if (txn
!= txn
->idl
->txn
) {
3021 /* If we need a lock but don't have it, give up quickly. */
3022 if (txn
->idl
->lock_name
&& !ovsdb_idl_has_lock(txn
->idl
)) {
3023 txn
->status
= TXN_NOT_LOCKED
;
3024 goto disassemble_out
;
3027 operations
= json_array_create_1(
3028 json_string_create(txn
->idl
->class->database
));
3030 /* Assert that we have the required lock (avoiding a race). */
3031 if (txn
->idl
->lock_name
) {
3032 struct json
*op
= json_object_create();
3033 json_array_add(operations
, op
);
3034 json_object_put_string(op
, "op", "assert");
3035 json_object_put_string(op
, "lock", txn
->idl
->lock_name
);
3038 /* Add prerequisites and declarations of new rows. */
3039 HMAP_FOR_EACH (row
, txn_node
, &txn
->txn_rows
) {
3040 /* XXX check that deleted rows exist even if no prereqs? */
3042 const struct ovsdb_idl_table_class
*class = row
->table
->class;
3043 size_t n_columns
= class->n_columns
;
3044 struct json
*op
, *columns
, *row_json
;
3047 op
= json_object_create();
3048 json_array_add(operations
, op
);
3049 json_object_put_string(op
, "op", "wait");
3050 json_object_put_string(op
, "table", class->name
);
3051 json_object_put(op
, "timeout", json_integer_create(0));
3052 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3053 json_object_put_string(op
, "until", "==");
3054 columns
= json_array_create_empty();
3055 json_object_put(op
, "columns", columns
);
3056 row_json
= json_object_create();
3057 json_object_put(op
, "rows", json_array_create_1(row_json
));
3059 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->prereqs
) {
3060 const struct ovsdb_idl_column
*column
= &class->columns
[idx
];
3061 json_array_add(columns
, json_string_create(column
->name
));
3062 json_object_put(row_json
, column
->name
,
3063 ovsdb_datum_to_json(&row
->old
[idx
],
3070 any_updates
= false;
3071 HMAP_FOR_EACH (row
, txn_node
, &txn
->txn_rows
) {
3072 const struct ovsdb_idl_table_class
*class = row
->table
->class;
3075 if (class->is_root
) {
3076 struct json
*op
= json_object_create();
3077 json_object_put_string(op
, "op", "delete");
3078 json_object_put_string(op
, "table", class->name
);
3079 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3080 json_array_add(operations
, op
);
3083 /* Let ovsdb-server decide whether to really delete it. */
3085 } else if (row
->old
!= row
->new) {
3086 struct json
*row_json
;
3090 op
= json_object_create();
3091 json_object_put_string(op
, "op", row
->old
? "update" : "insert");
3092 json_object_put_string(op
, "table", class->name
);
3094 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3096 struct ovsdb_idl_txn_insert
*insert
;
3100 json_object_put(op
, "uuid-name",
3101 json_string_create_nocopy(
3102 uuid_name_from_uuid(&row
->uuid
)));
3104 insert
= xmalloc(sizeof *insert
);
3105 insert
->dummy
= row
->uuid
;
3106 insert
->op_index
= operations
->u
.array
.n
- 1;
3107 uuid_zero(&insert
->real
);
3108 hmap_insert(&txn
->inserted_rows
, &insert
->hmap_node
,
3109 uuid_hash(&insert
->dummy
));
3111 row_json
= json_object_create();
3112 json_object_put(op
, "row", row_json
);
3115 BITMAP_FOR_EACH_1 (idx
, class->n_columns
, row
->written
) {
3116 const struct ovsdb_idl_column
*column
=
3117 &class->columns
[idx
];
3120 || !ovsdb_datum_is_default(&row
->new[idx
],
3122 json_object_put(row_json
, column
->name
,
3124 ovsdb_datum_to_json(&row
->new[idx
],
3128 /* If anything really changed, consider it an update.
3129 * We can't suppress not-really-changed values earlier
3130 * or transactions would become nonatomic (see the big
3131 * comment inside ovsdb_idl_txn_write()). */
3132 if (!any_updates
&& row
->old
&&
3133 !ovsdb_datum_equals(&row
->old
[idx
], &row
->new[idx
],
3141 if (!row
->old
|| !shash_is_empty(json_object(row_json
))) {
3142 json_array_add(operations
, op
);
3148 /* Add mutate operation, for partial map or partial set updates. */
3149 if (row
->map_op_written
|| row
->set_op_written
) {
3150 struct json
*op
, *mutations
;
3153 op
= json_object_create();
3154 json_object_put_string(op
, "op", "mutate");
3155 json_object_put_string(op
, "table", class->name
);
3156 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3157 mutations
= json_array_create_empty();
3158 any_mutations
= ovsdb_idl_txn_extract_mutations(row
, mutations
);
3159 json_object_put(op
, "mutations", mutations
);
3161 if (any_mutations
) {
3162 op
= substitute_uuids(op
, txn
);
3163 json_array_add(operations
, op
);
3171 /* Add increment. */
3172 if (txn
->inc_table
&& (any_updates
|| txn
->inc_force
)) {
3174 txn
->inc_index
= operations
->u
.array
.n
- 1;
3176 struct json
*op
= json_object_create();
3177 json_object_put_string(op
, "op", "mutate");
3178 json_object_put_string(op
, "table", txn
->inc_table
);
3179 json_object_put(op
, "where",
3180 substitute_uuids(where_uuid_equals(&txn
->inc_row
),
3182 json_object_put(op
, "mutations",
3183 json_array_create_1(
3184 json_array_create_3(
3185 json_string_create(txn
->inc_column
),
3186 json_string_create("+="),
3187 json_integer_create(1))));
3188 json_array_add(operations
, op
);
3190 op
= json_object_create();
3191 json_object_put_string(op
, "op", "select");
3192 json_object_put_string(op
, "table", txn
->inc_table
);
3193 json_object_put(op
, "where",
3194 substitute_uuids(where_uuid_equals(&txn
->inc_row
),
3196 json_object_put(op
, "columns",
3197 json_array_create_1(json_string_create(
3199 json_array_add(operations
, op
);
3202 if (txn
->comment
.length
) {
3203 struct json
*op
= json_object_create();
3204 json_object_put_string(op
, "op", "comment");
3205 json_object_put_string(op
, "comment", ds_cstr(&txn
->comment
));
3206 json_array_add(operations
, op
);
3210 struct json
*op
= json_object_create();
3211 json_object_put_string(op
, "op", "abort");
3212 json_array_add(operations
, op
);
3216 txn
->status
= TXN_UNCHANGED
;
3217 json_destroy(operations
);
3218 } else if (!jsonrpc_session_send(
3220 jsonrpc_create_request(
3221 "transact", operations
, &txn
->request_id
))) {
3222 hmap_insert(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
,
3223 json_hash(txn
->request_id
, 0));
3224 txn
->status
= TXN_INCOMPLETE
;
3226 txn
->status
= TXN_TRY_AGAIN
;
3230 ovsdb_idl_txn_disassemble(txn
);
3232 switch (txn
->status
) {
3233 case TXN_UNCOMMITTED
: COVERAGE_INC(txn_uncommitted
); break;
3234 case TXN_UNCHANGED
: COVERAGE_INC(txn_unchanged
); break;
3235 case TXN_INCOMPLETE
: COVERAGE_INC(txn_incomplete
); break;
3236 case TXN_ABORTED
: COVERAGE_INC(txn_aborted
); break;
3237 case TXN_SUCCESS
: COVERAGE_INC(txn_success
); break;
3238 case TXN_TRY_AGAIN
: COVERAGE_INC(txn_try_again
); break;
3239 case TXN_NOT_LOCKED
: COVERAGE_INC(txn_not_locked
); break;
3240 case TXN_ERROR
: COVERAGE_INC(txn_error
); break;
3246 /* Attempts to commit 'txn', blocking until the commit either succeeds or
3247 * fails. Returns the final commit status, which may be any TXN_* value other
3248 * than TXN_INCOMPLETE.
3250 * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the
3251 * return value of ovsdb_idl_get_seqno() to change. */
3252 enum ovsdb_idl_txn_status
3253 ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn
*txn
)
3255 enum ovsdb_idl_txn_status status
;
3258 while ((status
= ovsdb_idl_txn_commit(txn
)) == TXN_INCOMPLETE
) {
3259 ovsdb_idl_run(txn
->idl
);
3260 ovsdb_idl_wait(txn
->idl
);
3261 ovsdb_idl_txn_wait(txn
);
3267 /* Returns the final (incremented) value of the column in 'txn' that was set to
3268 * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed
3271 ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn
*txn
)
3273 ovs_assert(txn
->status
== TXN_SUCCESS
);
3274 return txn
->inc_new_value
;
3277 /* Aborts 'txn' without sending it to the database server. This is effective
3278 * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'.
3279 * Otherwise, it has no effect.
3281 * Aborting a transaction doesn't free its memory. Use
3282 * ovsdb_idl_txn_destroy() to do that. */
3284 ovsdb_idl_txn_abort(struct ovsdb_idl_txn
*txn
)
3286 ovsdb_idl_txn_disassemble(txn
);
3287 if (txn
->status
== TXN_UNCOMMITTED
|| txn
->status
== TXN_INCOMPLETE
) {
3288 txn
->status
= TXN_ABORTED
;
3292 /* Returns a string that reports the error status for 'txn'. The caller must
3293 * not modify or free the returned string. A call to ovsdb_idl_txn_destroy()
3294 * for 'txn' may free the returned string.
3296 * The return value is ordinarily one of the strings that
3297 * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed
3298 * due to an error reported by the database server, the return value is that
3301 ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn
*txn
)
3303 if (txn
->status
!= TXN_ERROR
) {
3304 return ovsdb_idl_txn_status_to_string(txn
->status
);
3305 } else if (txn
->error
) {
3308 return "no error details available";
3313 ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn
*txn
,
3314 const struct json
*json
)
3316 if (txn
->error
== NULL
) {
3317 txn
->error
= json_to_string(json
, JSSF_SORT
);
3321 /* For transaction 'txn' that completed successfully, finds and returns the
3322 * permanent UUID that the database assigned to a newly inserted row, given the
3323 * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row.
3325 * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or
3326 * if it was assigned by that function and then deleted by
3327 * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted
3328 * and then deleted within a single transaction are never sent to the database
3329 * server, so it never assigns them a permanent UUID.) */
3331 ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn
*txn
,
3332 const struct uuid
*uuid
)
3334 const struct ovsdb_idl_txn_insert
*insert
;
3336 ovs_assert(txn
->status
== TXN_SUCCESS
|| txn
->status
== TXN_UNCHANGED
);
3337 HMAP_FOR_EACH_IN_BUCKET (insert
, hmap_node
,
3338 uuid_hash(uuid
), &txn
->inserted_rows
) {
3339 if (uuid_equals(uuid
, &insert
->dummy
)) {
3340 return &insert
->real
;
3347 ovsdb_idl_txn_complete(struct ovsdb_idl_txn
*txn
,
3348 enum ovsdb_idl_txn_status status
)
3350 txn
->status
= status
;
3351 hmap_remove(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
);
3355 ovsdb_idl_txn_write__(const struct ovsdb_idl_row
*row_
,
3356 const struct ovsdb_idl_column
*column
,
3357 struct ovsdb_datum
*datum
, bool owns_datum
)
3359 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3360 const struct ovsdb_idl_table_class
*class;
3364 if (ovsdb_idl_row_is_synthetic(row
)) {
3368 class = row
->table
->class;
3369 column_idx
= column
- class->columns
;
3370 write_only
= row
->table
->modes
[column_idx
] == OVSDB_IDL_MONITOR
;
3372 ovs_assert(row
->new != NULL
);
3373 ovs_assert(column_idx
< class->n_columns
);
3374 ovs_assert(row
->old
== NULL
||
3375 row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
);
3377 if (row
->table
->idl
->verify_write_only
&& !write_only
) {
3378 VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
3379 " explicitly configured not to.", class->name
, column
->name
);
3383 /* If this is a write-only column and the datum being written is the same
3384 * as the one already there, just skip the update entirely. This is worth
3385 * optimizing because we have a lot of columns that get periodically
3386 * refreshed into the database but don't actually change that often.
3388 * We don't do this for read/write columns because that would break
3389 * atomicity of transactions--some other client might have written a
3390 * different value in that column since we read it. (But if a whole
3391 * transaction only does writes of existing values, without making any real
3392 * changes, we will drop the whole transaction later in
3393 * ovsdb_idl_txn_commit().) */
3394 if (write_only
&& ovsdb_datum_equals(ovsdb_idl_read(row
, column
),
3395 datum
, &column
->type
)) {
3399 if (hmap_node_is_null(&row
->txn_node
)) {
3400 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3401 uuid_hash(&row
->uuid
));
3403 if (row
->old
== row
->new) {
3404 row
->new = xmalloc(class->n_columns
* sizeof *row
->new);
3406 if (!row
->written
) {
3407 row
->written
= bitmap_allocate(class->n_columns
);
3409 if (bitmap_is_set(row
->written
, column_idx
)) {
3410 ovsdb_datum_destroy(&row
->new[column_idx
], &column
->type
);
3412 bitmap_set1(row
->written
, column_idx
);
3415 row
->new[column_idx
] = *datum
;
3417 ovsdb_datum_clone(&row
->new[column_idx
], datum
, &column
->type
);
3419 (column
->unparse
)(row
);
3420 (column
->parse
)(row
, &row
->new[column_idx
]);
3425 ovsdb_datum_destroy(datum
, &column
->type
);
3429 /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_'
3430 * itself and the structs derived from it (e.g. the "struct ovsrec_*", for
3433 * 'datum' must have the correct type for its column, but it needs not be
3434 * sorted or unique because this function will take care of that. The IDL does
3435 * not check that it meets schema constraints, but ovsdb-server will do so at
3436 * commit time so it had better be correct.
3438 * A transaction must be in progress. Replication of 'column' must not have
3439 * been disabled (by calling ovsdb_idl_omit()).
3441 * Usually this function is used indirectly through one of the "set" functions
3442 * generated by ovsdb-idlc.
3444 * Takes ownership of what 'datum' points to (and in some cases destroys that
3445 * data before returning) but makes a copy of 'datum' itself. (Commonly
3446 * 'datum' is on the caller's stack.) */
3448 ovsdb_idl_txn_write(const struct ovsdb_idl_row
*row
,
3449 const struct ovsdb_idl_column
*column
,
3450 struct ovsdb_datum
*datum
)
3452 ovsdb_datum_sort_unique(datum
,
3453 column
->type
.key
.type
, column
->type
.value
.type
);
3454 ovsdb_idl_txn_write__(row
, column
, datum
, true);
3457 /* Similar to ovsdb_idl_txn_write(), except:
3459 * - The caller retains ownership of 'datum' and what it points to.
3461 * - The caller must ensure that 'datum' is sorted and unique (e.g. via
3462 * ovsdb_datum_sort_unique().) */
3464 ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row
*row
,
3465 const struct ovsdb_idl_column
*column
,
3466 const struct ovsdb_datum
*datum
)
3468 ovsdb_idl_txn_write__(row
, column
,
3469 CONST_CAST(struct ovsdb_datum
*, datum
), false);
3472 /* Causes the original contents of 'column' in 'row_' to be verified as a
3473 * prerequisite to completing the transaction. That is, if 'column' in 'row_'
3474 * changed (or if 'row_' was deleted) between the time that the IDL originally
3475 * read its contents and the time that the transaction commits, then the
3476 * transaction aborts and ovsdb_idl_txn_commit() returns TXN_TRY_AGAIN.
3478 * The intention is that, to ensure that no transaction commits based on dirty
3479 * reads, an application should call ovsdb_idl_txn_verify() on each data item
3480 * read as part of a read-modify-write operation.
3482 * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current
3483 * value of 'column' is already known:
3485 * - If 'row_' is a row created by the current transaction (returned by
3486 * ovsdb_idl_txn_insert()).
3488 * - If 'column' has already been modified (with ovsdb_idl_txn_write())
3489 * within the current transaction.
3491 * Because of the latter property, always call ovsdb_idl_txn_verify() *before*
3492 * ovsdb_idl_txn_write() for a given read-modify-write.
3494 * A transaction must be in progress.
3496 * Usually this function is used indirectly through one of the "verify"
3497 * functions generated by ovsdb-idlc. */
3499 ovsdb_idl_txn_verify(const struct ovsdb_idl_row
*row_
,
3500 const struct ovsdb_idl_column
*column
)
3502 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3503 const struct ovsdb_idl_table_class
*class;
3506 if (ovsdb_idl_row_is_synthetic(row
)) {
3510 class = row
->table
->class;
3511 column_idx
= column
- class->columns
;
3513 ovs_assert(row
->new != NULL
);
3514 ovs_assert(row
->old
== NULL
||
3515 row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
);
3517 || (row
->written
&& bitmap_is_set(row
->written
, column_idx
))) {
3521 if (hmap_node_is_null(&row
->txn_node
)) {
3522 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3523 uuid_hash(&row
->uuid
));
3525 if (!row
->prereqs
) {
3526 row
->prereqs
= bitmap_allocate(class->n_columns
);
3528 bitmap_set1(row
->prereqs
, column_idx
);
3531 /* Deletes 'row_' from its table. May free 'row_', so it must not be
3532 * accessed afterward.
3534 * A transaction must be in progress.
3536 * Usually this function is used indirectly through one of the "delete"
3537 * functions generated by ovsdb-idlc. */
3539 ovsdb_idl_txn_delete(const struct ovsdb_idl_row
*row_
)
3541 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3543 if (ovsdb_idl_row_is_synthetic(row
)) {
3547 ovs_assert(row
->new != NULL
);
3549 ovsdb_idl_row_unparse(row
);
3550 ovsdb_idl_row_clear_new(row
);
3551 ovs_assert(!row
->prereqs
);
3552 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
3553 hmap_remove(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
);
3557 if (hmap_node_is_null(&row
->txn_node
)) {
3558 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3559 uuid_hash(&row
->uuid
));
3561 ovsdb_idl_row_clear_new(row
);
3565 /* Inserts and returns a new row in the table with the specified 'class' in the
3566 * database with open transaction 'txn'.
3568 * The new row is assigned a provisional UUID. If 'uuid' is null then one is
3569 * randomly generated; otherwise 'uuid' should specify a randomly generated
3570 * UUID not otherwise in use. ovsdb-server will assign a different UUID when
3571 * 'txn' is committed, but the IDL will replace any uses of the provisional
3572 * UUID in the data to be to be committed by the UUID assigned by
3575 * Usually this function is used indirectly through one of the "insert"
3576 * functions generated by ovsdb-idlc. */
3577 const struct ovsdb_idl_row
*
3578 ovsdb_idl_txn_insert(struct ovsdb_idl_txn
*txn
,
3579 const struct ovsdb_idl_table_class
*class,
3580 const struct uuid
*uuid
)
3582 struct ovsdb_idl_row
*row
= ovsdb_idl_row_create__(class);
3585 ovs_assert(!ovsdb_idl_txn_get_row(txn
, uuid
));
3588 uuid_generate(&row
->uuid
);
3591 row
->table
= ovsdb_idl_table_from_class(txn
->idl
, class);
3592 row
->new = xmalloc(class->n_columns
* sizeof *row
->new);
3593 hmap_insert(&row
->table
->rows
, &row
->hmap_node
, uuid_hash(&row
->uuid
));
3594 hmap_insert(&txn
->txn_rows
, &row
->txn_node
, uuid_hash(&row
->uuid
));
3599 ovsdb_idl_txn_abort_all(struct ovsdb_idl
*idl
)
3601 struct ovsdb_idl_txn
*txn
;
3603 HMAP_FOR_EACH (txn
, hmap_node
, &idl
->outstanding_txns
) {
3604 ovsdb_idl_txn_complete(txn
, TXN_TRY_AGAIN
);
3608 static struct ovsdb_idl_txn
*
3609 ovsdb_idl_txn_find(struct ovsdb_idl
*idl
, const struct json
*id
)
3611 struct ovsdb_idl_txn
*txn
;
3613 HMAP_FOR_EACH_WITH_HASH (txn
, hmap_node
,
3614 json_hash(id
, 0), &idl
->outstanding_txns
) {
3615 if (json_equal(id
, txn
->request_id
)) {
3623 check_json_type(const struct json
*json
, enum json_type type
, const char *name
)
3626 VLOG_WARN_RL(&syntax_rl
, "%s is missing", name
);
3628 } else if (json
->type
!= type
) {
3629 VLOG_WARN_RL(&syntax_rl
, "%s is %s instead of %s",
3630 name
, json_type_to_string(json
->type
),
3631 json_type_to_string(type
));
3639 ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn
*txn
,
3640 const struct json_array
*results
)
3642 struct json
*count
, *rows
, *row
, *column
;
3643 struct shash
*mutate
, *select
;
3645 if (txn
->inc_index
+ 2 > results
->n
) {
3646 VLOG_WARN_RL(&syntax_rl
, "reply does not contain enough operations "
3647 "for increment (has %"PRIuSIZE
", needs %u)",
3648 results
->n
, txn
->inc_index
+ 2);
3652 /* We know that this is a JSON object because the loop in
3653 * ovsdb_idl_txn_process_reply() checked. */
3654 mutate
= json_object(results
->elems
[txn
->inc_index
]);
3655 count
= shash_find_data(mutate
, "count");
3656 if (!check_json_type(count
, JSON_INTEGER
, "\"mutate\" reply \"count\"")) {
3659 if (count
->u
.integer
!= 1) {
3660 VLOG_WARN_RL(&syntax_rl
,
3661 "\"mutate\" reply \"count\" is %lld instead of 1",
3666 select
= json_object(results
->elems
[txn
->inc_index
+ 1]);
3667 rows
= shash_find_data(select
, "rows");
3668 if (!check_json_type(rows
, JSON_ARRAY
, "\"select\" reply \"rows\"")) {
3671 if (rows
->u
.array
.n
!= 1) {
3672 VLOG_WARN_RL(&syntax_rl
, "\"select\" reply \"rows\" has %"PRIuSIZE
" elements "
3677 row
= rows
->u
.array
.elems
[0];
3678 if (!check_json_type(row
, JSON_OBJECT
, "\"select\" reply row")) {
3681 column
= shash_find_data(json_object(row
), txn
->inc_column
);
3682 if (!check_json_type(column
, JSON_INTEGER
,
3683 "\"select\" reply inc column")) {
3686 txn
->inc_new_value
= column
->u
.integer
;
3691 ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert
*insert
,
3692 const struct json_array
*results
)
3694 static const struct ovsdb_base_type uuid_type
= OVSDB_BASE_UUID_INIT
;
3695 struct ovsdb_error
*error
;
3696 struct json
*json_uuid
;
3697 union ovsdb_atom uuid
;
3698 struct shash
*reply
;
3700 if (insert
->op_index
>= results
->n
) {
3701 VLOG_WARN_RL(&syntax_rl
, "reply does not contain enough operations "
3702 "for insert (has %"PRIuSIZE
", needs %u)",
3703 results
->n
, insert
->op_index
);
3707 /* We know that this is a JSON object because the loop in
3708 * ovsdb_idl_txn_process_reply() checked. */
3709 reply
= json_object(results
->elems
[insert
->op_index
]);
3710 json_uuid
= shash_find_data(reply
, "uuid");
3711 if (!check_json_type(json_uuid
, JSON_ARRAY
, "\"insert\" reply \"uuid\"")) {
3715 error
= ovsdb_atom_from_json(&uuid
, &uuid_type
, json_uuid
, NULL
);
3717 char *s
= ovsdb_error_to_string(error
);
3718 VLOG_WARN_RL(&syntax_rl
, "\"insert\" reply \"uuid\" is not a JSON "
3721 ovsdb_error_destroy(error
);
3725 insert
->real
= uuid
.uuid
;
3731 ovsdb_idl_txn_process_reply(struct ovsdb_idl
*idl
,
3732 const struct jsonrpc_msg
*msg
)
3734 struct ovsdb_idl_txn
*txn
;
3735 enum ovsdb_idl_txn_status status
;
3737 txn
= ovsdb_idl_txn_find(idl
, msg
->id
);
3742 if (msg
->type
== JSONRPC_ERROR
) {
3744 } else if (msg
->result
->type
!= JSON_ARRAY
) {
3745 VLOG_WARN_RL(&syntax_rl
, "reply to \"transact\" is not JSON array");
3748 struct json_array
*ops
= &msg
->result
->u
.array
;
3749 int hard_errors
= 0;
3750 int soft_errors
= 0;
3751 int lock_errors
= 0;
3754 for (i
= 0; i
< ops
->n
; i
++) {
3755 struct json
*op
= ops
->elems
[i
];
3757 if (op
->type
== JSON_NULL
) {
3758 /* This isn't an error in itself but indicates that some prior
3759 * operation failed, so make sure that we know about it. */
3761 } else if (op
->type
== JSON_OBJECT
) {
3764 error
= shash_find_data(json_object(op
), "error");
3766 if (error
->type
== JSON_STRING
) {
3767 if (!strcmp(error
->u
.string
, "timed out")) {
3769 } else if (!strcmp(error
->u
.string
, "not owner")) {
3771 } else if (strcmp(error
->u
.string
, "aborted")) {
3773 ovsdb_idl_txn_set_error_json(txn
, op
);
3777 ovsdb_idl_txn_set_error_json(txn
, op
);
3778 VLOG_WARN_RL(&syntax_rl
,
3779 "\"error\" in reply is not JSON string");
3784 ovsdb_idl_txn_set_error_json(txn
, op
);
3785 VLOG_WARN_RL(&syntax_rl
,
3786 "operation reply is not JSON null or object");
3790 if (!soft_errors
&& !hard_errors
&& !lock_errors
) {
3791 struct ovsdb_idl_txn_insert
*insert
;
3793 if (txn
->inc_table
&& !ovsdb_idl_txn_process_inc_reply(txn
, ops
)) {
3797 HMAP_FOR_EACH (insert
, hmap_node
, &txn
->inserted_rows
) {
3798 if (!ovsdb_idl_txn_process_insert_reply(insert
, ops
)) {
3804 status
= (hard_errors
? TXN_ERROR
3805 : lock_errors
? TXN_NOT_LOCKED
3806 : soft_errors
? TXN_TRY_AGAIN
3810 ovsdb_idl_txn_complete(txn
, status
);
3814 /* Returns the transaction currently active for 'row''s IDL. A transaction
3815 * must currently be active. */
3816 struct ovsdb_idl_txn
*
3817 ovsdb_idl_txn_get(const struct ovsdb_idl_row
*row
)
3819 struct ovsdb_idl_txn
*txn
= row
->table
->idl
->txn
;
3820 ovs_assert(txn
!= NULL
);
3824 /* Returns the IDL on which 'txn' acts. */
3826 ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn
*txn
)
3831 /* Blocks until 'idl' successfully connects to the remote database and
3832 * retrieves its contents. */
3834 ovsdb_idl_get_initial_snapshot(struct ovsdb_idl
*idl
)
3838 if (ovsdb_idl_has_ever_connected(idl
)) {
3841 ovsdb_idl_wait(idl
);
3846 /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from
3847 * the database server and to avoid modifying the database when the lock cannot
3848 * be acquired (that is, when another client has the same lock).
3850 * If 'lock_name' is NULL, drops the locking requirement and releases the
3853 ovsdb_idl_set_lock(struct ovsdb_idl
*idl
, const char *lock_name
)
3855 ovs_assert(!idl
->txn
);
3856 ovs_assert(hmap_is_empty(&idl
->outstanding_txns
));
3858 if (idl
->lock_name
&& (!lock_name
|| strcmp(lock_name
, idl
->lock_name
))) {
3859 /* Release previous lock. */
3860 ovsdb_idl_send_unlock_request(idl
);
3861 free(idl
->lock_name
);
3862 idl
->lock_name
= NULL
;
3863 idl
->is_lock_contended
= false;
3866 if (lock_name
&& !idl
->lock_name
) {
3867 /* Acquire new lock. */
3868 idl
->lock_name
= xstrdup(lock_name
);
3869 ovsdb_idl_send_lock_request(idl
);
3873 /* Returns true if 'idl' is configured to obtain a lock and owns that lock.
3875 * Locking and unlocking happens asynchronously from the database client's
3876 * point of view, so the information is only useful for optimization (e.g. if
3877 * the client doesn't have the lock then there's no point in trying to write to
3880 ovsdb_idl_has_lock(const struct ovsdb_idl
*idl
)
3882 return idl
->has_lock
;
3885 /* Returns true if 'idl' is configured to obtain a lock but the database server
3886 * has indicated that some other client already owns the requested lock. */
3888 ovsdb_idl_is_lock_contended(const struct ovsdb_idl
*idl
)
3890 return idl
->is_lock_contended
;
3894 ovsdb_idl_update_has_lock(struct ovsdb_idl
*idl
, bool new_has_lock
)
3896 if (new_has_lock
&& !idl
->has_lock
) {
3897 if (idl
->state
== IDL_S_MONITORING
||
3898 idl
->state
== IDL_S_MONITORING_COND
) {
3899 idl
->change_seqno
++;
3901 /* We're setting up a session, so don't signal that the database
3902 * changed. Finalizing the session will increment change_seqno
3905 idl
->is_lock_contended
= false;
3907 idl
->has_lock
= new_has_lock
;
3911 ovsdb_idl_send_lock_request__(struct ovsdb_idl
*idl
, const char *method
,
3914 ovsdb_idl_update_has_lock(idl
, false);
3916 json_destroy(idl
->lock_request_id
);
3917 idl
->lock_request_id
= NULL
;
3919 if (jsonrpc_session_is_connected(idl
->session
)) {
3920 struct json
*params
;
3922 params
= json_array_create_1(json_string_create(idl
->lock_name
));
3923 jsonrpc_session_send(idl
->session
,
3924 jsonrpc_create_request(method
, params
, idp
));
3929 ovsdb_idl_send_lock_request(struct ovsdb_idl
*idl
)
3931 ovsdb_idl_send_lock_request__(idl
, "lock", &idl
->lock_request_id
);
3935 ovsdb_idl_send_unlock_request(struct ovsdb_idl
*idl
)
3937 ovsdb_idl_send_lock_request__(idl
, "unlock", NULL
);
3941 ovsdb_idl_parse_lock_reply(struct ovsdb_idl
*idl
, const struct json
*result
)
3945 json_destroy(idl
->lock_request_id
);
3946 idl
->lock_request_id
= NULL
;
3948 if (result
->type
== JSON_OBJECT
) {
3949 const struct json
*locked
;
3951 locked
= shash_find_data(json_object(result
), "locked");
3952 got_lock
= locked
&& locked
->type
== JSON_TRUE
;
3957 ovsdb_idl_update_has_lock(idl
, got_lock
);
3959 idl
->is_lock_contended
= true;
3964 ovsdb_idl_parse_lock_notify(struct ovsdb_idl
*idl
,
3965 const struct json
*params
,
3969 && params
->type
== JSON_ARRAY
3970 && json_array(params
)->n
> 0
3971 && json_array(params
)->elems
[0]->type
== JSON_STRING
) {
3972 const char *lock_name
= json_string(json_array(params
)->elems
[0]);
3974 if (!strcmp(idl
->lock_name
, lock_name
)) {
3975 ovsdb_idl_update_has_lock(idl
, new_has_lock
);
3976 if (!new_has_lock
) {
3977 idl
->is_lock_contended
= true;
3983 /* Inserts a new Map Operation into current transaction. */
3985 ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row
*row
,
3986 const struct ovsdb_idl_column
*column
,
3987 struct ovsdb_datum
*datum
,
3988 enum map_op_type op_type
)
3990 const struct ovsdb_idl_table_class
*class;
3992 struct map_op
*map_op
;
3994 class = row
->table
->class;
3995 column_idx
= column
- class->columns
;
3997 /* Check if a map operation list exists for this column. */
3998 if (!row
->map_op_written
) {
3999 row
->map_op_written
= bitmap_allocate(class->n_columns
);
4000 row
->map_op_lists
= xzalloc(class->n_columns
*
4001 sizeof *row
->map_op_lists
);
4003 if (!row
->map_op_lists
[column_idx
]) {
4004 row
->map_op_lists
[column_idx
] = map_op_list_create();
4007 /* Add a map operation to the corresponding list. */
4008 map_op
= map_op_create(datum
, op_type
);
4009 bitmap_set1(row
->map_op_written
, column_idx
);
4010 map_op_list_add(row
->map_op_lists
[column_idx
], map_op
, &column
->type
);
4012 /* Add this row to transaction's list of rows. */
4013 if (hmap_node_is_null(&row
->txn_node
)) {
4014 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
4015 uuid_hash(&row
->uuid
));
4019 /* Inserts a new Set Operation into current transaction. */
4021 ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row
*row
,
4022 const struct ovsdb_idl_column
*column
,
4023 struct ovsdb_datum
*datum
,
4024 enum set_op_type op_type
)
4026 const struct ovsdb_idl_table_class
*class;
4028 struct set_op
*set_op
;
4030 class = row
->table
->class;
4031 column_idx
= column
- class->columns
;
4033 /* Check if a set operation list exists for this column. */
4034 if (!row
->set_op_written
) {
4035 row
->set_op_written
= bitmap_allocate(class->n_columns
);
4036 row
->set_op_lists
= xzalloc(class->n_columns
*
4037 sizeof *row
->set_op_lists
);
4039 if (!row
->set_op_lists
[column_idx
]) {
4040 row
->set_op_lists
[column_idx
] = set_op_list_create();
4043 /* Add a set operation to the corresponding list. */
4044 set_op
= set_op_create(datum
, op_type
);
4045 bitmap_set1(row
->set_op_written
, column_idx
);
4046 set_op_list_add(row
->set_op_lists
[column_idx
], set_op
, &column
->type
);
4048 /* Add this row to the transactions's list of rows. */
4049 if (hmap_node_is_null(&row
->txn_node
)) {
4050 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
4051 uuid_hash(&row
->uuid
));
4056 is_valid_partial_update(const struct ovsdb_idl_row
*row
,
4057 const struct ovsdb_idl_column
*column
,
4058 struct ovsdb_datum
*datum
)
4060 /* Verify that this column is being monitored. */
4061 unsigned int column_idx
= column
- row
->table
->class->columns
;
4062 if (!(row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
)) {
4063 VLOG_WARN("cannot partially update non-monitored column");
4067 /* Verify that the update affects a single element. */
4068 if (datum
->n
!= 1) {
4069 VLOG_WARN("invalid datum for partial update");
4076 /* Inserts the value described in 'datum' into the map in 'column' in
4077 * 'row_'. If the value doesn't already exist in 'column' then it's value
4078 * is added. The value in 'datum' must be of the same type as the values
4079 * in 'column'. This function takes ownership of 'datum'.
4081 * Usually this function is used indirectly through one of the "update"
4082 * functions generated by vswitch-idl. */
4084 ovsdb_idl_txn_write_partial_set(const struct ovsdb_idl_row
*row_
,
4085 const struct ovsdb_idl_column
*column
,
4086 struct ovsdb_datum
*datum
)
4088 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4089 enum set_op_type op_type
;
4091 if (!is_valid_partial_update(row
, column
, datum
)) {
4092 ovsdb_datum_destroy(datum
, &column
->type
);
4097 op_type
= SET_OP_INSERT
;
4099 ovsdb_idl_txn_add_set_op(row
, column
, datum
, op_type
);
4102 /* Deletes the value specified in 'datum' from the set in 'column' in 'row_'.
4103 * The value in 'datum' must be of the same type as the keys in 'column'.
4104 * This function takes ownership of 'datum'.
4106 * Usually this function is used indirectly through one of the "update"
4107 * functions generated by vswitch-idl. */
4109 ovsdb_idl_txn_delete_partial_set(const struct ovsdb_idl_row
*row_
,
4110 const struct ovsdb_idl_column
*column
,
4111 struct ovsdb_datum
*datum
)
4113 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4115 if (!is_valid_partial_update(row
, column
, datum
)) {
4116 struct ovsdb_type type_
= column
->type
;
4117 type_
.value
.type
= OVSDB_TYPE_VOID
;
4118 ovsdb_datum_destroy(datum
, &type_
);
4122 ovsdb_idl_txn_add_set_op(row
, column
, datum
, SET_OP_DELETE
);
4125 /* Inserts the key-value specified in 'datum' into the map in 'column' in
4126 * 'row_'. If the key already exist in 'column', then it's value is updated
4127 * with the value in 'datum'. The key-value in 'datum' must be of the same type
4128 * as the keys-values in 'column'. This function takes ownership of 'datum'.
4130 * Usually this function is used indirectly through one of the "update"
4131 * functions generated by vswitch-idl. */
4133 ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row
*row_
,
4134 const struct ovsdb_idl_column
*column
,
4135 struct ovsdb_datum
*datum
)
4137 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4138 enum ovsdb_atomic_type key_type
;
4139 enum map_op_type op_type
;
4141 const struct ovsdb_datum
*old_datum
;
4143 if (!is_valid_partial_update(row
, column
, datum
)) {
4144 ovsdb_datum_destroy(datum
, &column
->type
);
4149 /* Find out if this is an insert or an update. */
4150 key_type
= column
->type
.key
.type
;
4151 old_datum
= ovsdb_idl_read(row
, column
);
4152 pos
= ovsdb_datum_find_key(old_datum
, &datum
->keys
[0], key_type
);
4153 op_type
= pos
== UINT_MAX
? MAP_OP_INSERT
: MAP_OP_UPDATE
;
4155 ovsdb_idl_txn_add_map_op(row
, column
, datum
, op_type
);
4158 /* Deletes the key specified in 'datum' from the map in 'column' in 'row_'.
4159 * The key in 'datum' must be of the same type as the keys in 'column'.
4160 * The value in 'datum' must be NULL. This function takes ownership of
4163 * Usually this function is used indirectly through one of the "update"
4164 * functions generated by vswitch-idl. */
4166 ovsdb_idl_txn_delete_partial_map(const struct ovsdb_idl_row
*row_
,
4167 const struct ovsdb_idl_column
*column
,
4168 struct ovsdb_datum
*datum
)
4170 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4172 if (!is_valid_partial_update(row
, column
, datum
)) {
4173 struct ovsdb_type type_
= column
->type
;
4174 type_
.value
.type
= OVSDB_TYPE_VOID
;
4175 ovsdb_datum_destroy(datum
, &type_
);
4179 ovsdb_idl_txn_add_map_op(row
, column
, datum
, MAP_OP_DELETE
);
4183 ovsdb_idl_loop_destroy(struct ovsdb_idl_loop
*loop
)
4186 ovsdb_idl_destroy(loop
->idl
);
4190 struct ovsdb_idl_txn
*
4191 ovsdb_idl_loop_run(struct ovsdb_idl_loop
*loop
)
4193 ovsdb_idl_run(loop
->idl
);
4194 loop
->open_txn
= (loop
->committing_txn
4195 || ovsdb_idl_get_seqno(loop
->idl
) == loop
->skip_seqno
4197 : ovsdb_idl_txn_create(loop
->idl
));
4198 return loop
->open_txn
;
4201 /* Attempts to commit the current transaction, if one is open, and sets up the
4202 * poll loop to wake up when some more work might be needed.
4204 * If a transaction was open, in this or a previous iteration of the main loop,
4205 * and had not before finished committing (successfully or unsuccessfully), the
4206 * return value is one of:
4208 * 1: The transaction committed successfully (or it did not change anything in
4210 * 0: The transaction failed.
4211 * -1: The commit is still in progress.
4213 * Thus, the return value is -1 if the transaction is in progress and otherwise
4214 * true for success, false for failure.
4216 * (In the corner case where the IDL sends a transaction to the database and
4217 * the database commits it, and the connection between the IDL and the database
4218 * drops before the IDL receives the message confirming the commit, this
4219 * function can return 0 even though the transaction succeeded.)
4222 ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop
*loop
)
4224 if (loop
->open_txn
) {
4225 loop
->committing_txn
= loop
->open_txn
;
4226 loop
->open_txn
= NULL
;
4228 loop
->precommit_seqno
= ovsdb_idl_get_seqno(loop
->idl
);
4231 struct ovsdb_idl_txn
*txn
= loop
->committing_txn
;
4234 enum ovsdb_idl_txn_status status
= ovsdb_idl_txn_commit(txn
);
4235 if (status
!= TXN_INCOMPLETE
) {
4238 /* We want to re-evaluate the database when it's changed from
4239 * the contents that it had when we started the commit. (That
4240 * might have already happened.) */
4241 loop
->skip_seqno
= loop
->precommit_seqno
;
4242 if (ovsdb_idl_get_seqno(loop
->idl
) != loop
->skip_seqno
) {
4243 poll_immediate_wake();
4249 /* Possibly some work on the database was deferred because no
4250 * further transaction could proceed. Wake up again. */
4252 loop
->cur_cfg
= loop
->next_cfg
;
4253 poll_immediate_wake();
4258 loop
->cur_cfg
= loop
->next_cfg
;
4262 case TXN_NOT_LOCKED
:
4267 case TXN_UNCOMMITTED
:
4268 case TXN_INCOMPLETE
:
4272 ovsdb_idl_txn_destroy(txn
);
4273 loop
->committing_txn
= NULL
;
4278 /* Not a meaningful return value: no transaction was in progress. */
4282 ovsdb_idl_wait(loop
->idl
);