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);
163 static struct vlog_rate_limit other_rl
= VLOG_RATE_LIMIT_INIT(1, 5);
165 static void ovsdb_idl_clear(struct ovsdb_idl
*);
166 static void ovsdb_idl_send_schema_request(struct ovsdb_idl
*);
167 static void ovsdb_idl_send_monitor_request(struct ovsdb_idl
*);
168 static void ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl
*);
169 static void ovsdb_idl_parse_update(struct ovsdb_idl
*, const struct json
*,
170 enum ovsdb_update_version
);
171 static struct ovsdb_error
*ovsdb_idl_parse_update__(struct ovsdb_idl
*,
173 enum ovsdb_update_version
);
174 static bool ovsdb_idl_process_update(struct ovsdb_idl_table
*,
176 const struct json
*old
,
177 const struct json
*new);
178 static bool ovsdb_idl_process_update2(struct ovsdb_idl_table
*,
180 const char *operation
,
181 const struct json
*row
);
182 static void ovsdb_idl_insert_row(struct ovsdb_idl_row
*, const struct json
*);
183 static void ovsdb_idl_delete_row(struct ovsdb_idl_row
*);
184 static bool ovsdb_idl_modify_row(struct ovsdb_idl_row
*, const struct json
*);
185 static bool ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row
*,
186 const struct json
*);
188 static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row
*);
189 static struct ovsdb_idl_row
*ovsdb_idl_row_create__(
190 const struct ovsdb_idl_table_class
*);
191 static struct ovsdb_idl_row
*ovsdb_idl_row_create(struct ovsdb_idl_table
*,
192 const struct uuid
*);
193 static void ovsdb_idl_row_destroy(struct ovsdb_idl_row
*);
194 static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl
*);
195 static void ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row
*);
196 static void ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row
*);
198 static void ovsdb_idl_row_parse(struct ovsdb_idl_row
*);
199 static void ovsdb_idl_row_unparse(struct ovsdb_idl_row
*);
200 static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row
*);
201 static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row
*);
202 static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row
*, bool destroy_dsts
);
204 static void ovsdb_idl_txn_abort_all(struct ovsdb_idl
*);
205 static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl
*,
206 const struct jsonrpc_msg
*msg
);
207 static bool ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row
*,
209 static void ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row
*,
210 const struct ovsdb_idl_column
*,
211 struct ovsdb_datum
*,
213 static void ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row
*,
214 const struct ovsdb_idl_column
*,
215 struct ovsdb_datum
*,
218 static void ovsdb_idl_send_lock_request(struct ovsdb_idl
*);
219 static void ovsdb_idl_send_unlock_request(struct ovsdb_idl
*);
220 static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl
*,
221 const struct json
*);
222 static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl
*,
223 const struct json
*params
,
225 static struct ovsdb_idl_table
*
226 ovsdb_idl_table_from_class(const struct ovsdb_idl
*,
227 const struct ovsdb_idl_table_class
*);
228 static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table
*table
);
229 static void ovsdb_idl_send_cond_change(struct ovsdb_idl
*idl
);
231 /* Creates and returns a connection to database 'remote', which should be in a
232 * form acceptable to jsonrpc_session_open(). The connection will maintain an
233 * in-memory replica of the remote database whose schema is described by
234 * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically
237 * Passes 'retry' to jsonrpc_session_open(). See that function for
240 * If 'monitor_everything_by_default' is true, then everything in the remote
241 * database will be replicated by default. ovsdb_idl_omit() and
242 * ovsdb_idl_omit_alert() may be used to selectively drop some columns from
245 * If 'monitor_everything_by_default' is false, then no columns or tables will
246 * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table()
247 * must be used to choose some columns or tables to replicate.
250 ovsdb_idl_create(const char *remote
, const struct ovsdb_idl_class
*class,
251 bool monitor_everything_by_default
, bool retry
)
253 struct ovsdb_idl
*idl
;
254 uint8_t default_mode
;
257 default_mode
= (monitor_everything_by_default
258 ? OVSDB_IDL_MONITOR
| OVSDB_IDL_ALERT
261 idl
= xzalloc(sizeof *idl
);
263 idl
->session
= jsonrpc_session_open(remote
, retry
);
264 shash_init(&idl
->table_by_name
);
265 idl
->tables
= xmalloc(class->n_tables
* sizeof *idl
->tables
);
266 for (i
= 0; i
< class->n_tables
; i
++) {
267 const struct ovsdb_idl_table_class
*tc
= &class->tables
[i
];
268 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
271 shash_add_assert(&idl
->table_by_name
, tc
->name
, table
);
273 table
->modes
= xmalloc(tc
->n_columns
);
274 memset(table
->modes
, default_mode
, tc
->n_columns
);
275 table
->need_table
= false;
276 shash_init(&table
->columns
);
277 for (j
= 0; j
< tc
->n_columns
; j
++) {
278 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
280 shash_add_assert(&table
->columns
, column
->name
, column
);
282 hmap_init(&table
->rows
);
283 ovs_list_init(&table
->track_list
);
284 table
->change_seqno
[OVSDB_IDL_CHANGE_INSERT
]
285 = table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
]
286 = table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
] = 0;
288 ovsdb_idl_condition_init(&table
->condition
);
289 ovsdb_idl_condition_add_clause_true(&table
->condition
);
290 table
->cond_changed
= false;
293 idl
->cond_changed
= false;
295 idl
->state_seqno
= UINT_MAX
;
296 idl
->request_id
= NULL
;
299 hmap_init(&idl
->outstanding_txns
);
300 uuid_generate(&idl
->uuid
);
305 /* Changes the remote and creates a new session. */
307 ovsdb_idl_set_remote(struct ovsdb_idl
*idl
, const char *remote
,
311 ovs_assert(!idl
->txn
);
312 jsonrpc_session_close(idl
->session
);
313 idl
->session
= jsonrpc_session_open(remote
, retry
);
314 idl
->state_seqno
= UINT_MAX
;
318 /* Destroys 'idl' and all of the data structures that it manages. */
320 ovsdb_idl_destroy(struct ovsdb_idl
*idl
)
325 ovs_assert(!idl
->txn
);
326 ovsdb_idl_clear(idl
);
327 jsonrpc_session_close(idl
->session
);
329 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
330 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
331 ovsdb_idl_condition_destroy(&table
->condition
);
332 shash_destroy(&table
->columns
);
333 hmap_destroy(&table
->rows
);
336 shash_destroy(&idl
->table_by_name
);
338 json_destroy(idl
->request_id
);
339 free(idl
->lock_name
);
340 json_destroy(idl
->lock_request_id
);
341 json_destroy(idl
->schema
);
342 hmap_destroy(&idl
->outstanding_txns
);
348 ovsdb_idl_clear(struct ovsdb_idl
*idl
)
350 bool changed
= false;
353 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
354 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
355 struct ovsdb_idl_row
*row
, *next_row
;
357 table
->cond_changed
= false;
358 if (hmap_is_empty(&table
->rows
)) {
363 HMAP_FOR_EACH_SAFE (row
, next_row
, hmap_node
, &table
->rows
) {
364 struct ovsdb_idl_arc
*arc
, *next_arc
;
366 if (!ovsdb_idl_row_is_orphan(row
)) {
367 ovsdb_idl_row_unparse(row
);
369 LIST_FOR_EACH_SAFE (arc
, next_arc
, src_node
, &row
->src_arcs
) {
372 /* No need to do anything with dst_arcs: some node has those arcs
373 * as forward arcs and will destroy them itself. */
375 if (!ovs_list_is_empty(&row
->track_node
)) {
376 ovs_list_remove(&row
->track_node
);
379 ovsdb_idl_row_destroy(row
);
383 idl
->cond_changed
= false;
385 ovsdb_idl_track_clear(idl
);
392 /* Processes a batch of messages from the database server on 'idl'. This may
393 * cause the IDL's contents to change. The client may check for that with
394 * ovsdb_idl_get_seqno(). */
396 ovsdb_idl_run(struct ovsdb_idl
*idl
)
400 ovs_assert(!idl
->txn
);
402 ovsdb_idl_send_cond_change(idl
);
404 jsonrpc_session_run(idl
->session
);
405 for (i
= 0; jsonrpc_session_is_connected(idl
->session
) && i
< 50; i
++) {
406 struct jsonrpc_msg
*msg
;
409 seqno
= jsonrpc_session_get_seqno(idl
->session
);
410 if (idl
->state_seqno
!= seqno
) {
411 idl
->state_seqno
= seqno
;
412 json_destroy(idl
->request_id
);
413 idl
->request_id
= NULL
;
414 ovsdb_idl_txn_abort_all(idl
);
416 ovsdb_idl_send_schema_request(idl
);
417 idl
->state
= IDL_S_SCHEMA_REQUESTED
;
418 if (idl
->lock_name
) {
419 ovsdb_idl_send_lock_request(idl
);
423 msg
= jsonrpc_session_recv(idl
->session
);
428 if (msg
->type
== JSONRPC_NOTIFY
429 && !strcmp(msg
->method
, "update2")
430 && msg
->params
->type
== JSON_ARRAY
431 && msg
->params
->u
.array
.n
== 2
432 && msg
->params
->u
.array
.elems
[0]->type
== JSON_STRING
) {
433 /* Database contents changed. */
434 ovsdb_idl_parse_update(idl
, msg
->params
->u
.array
.elems
[1],
436 } else if (msg
->type
== JSONRPC_REPLY
438 && json_equal(idl
->request_id
, msg
->id
)) {
439 json_destroy(idl
->request_id
);
440 idl
->request_id
= NULL
;
442 switch (idl
->state
) {
443 case IDL_S_SCHEMA_REQUESTED
:
444 /* Reply to our "get_schema" request. */
445 idl
->schema
= json_clone(msg
->result
);
446 ovsdb_idl_send_monitor_cond_request(idl
);
447 idl
->state
= IDL_S_MONITOR_COND_REQUESTED
;
450 case IDL_S_MONITOR_REQUESTED
:
451 case IDL_S_MONITOR_COND_REQUESTED
:
452 /* Reply to our "monitor" or "monitor_cond" request. */
454 ovsdb_idl_clear(idl
);
455 if (idl
->state
== IDL_S_MONITOR_REQUESTED
) {
456 idl
->state
= IDL_S_MONITORING
;
457 ovsdb_idl_parse_update(idl
, msg
->result
, OVSDB_UPDATE
);
458 } else { /* IDL_S_MONITOR_COND_REQUESTED. */
459 idl
->state
= IDL_S_MONITORING_COND
;
460 ovsdb_idl_parse_update(idl
, msg
->result
, OVSDB_UPDATE2
);
463 /* Schema is not useful after monitor request is accepted
465 json_destroy(idl
->schema
);
469 case IDL_S_MONITORING_COND
:
470 /* Conditional monitor clauses were updated. Send out
471 * the next condition changes, in any, immediately. */
472 ovsdb_idl_send_cond_change(idl
);
476 case IDL_S_MONITORING
:
477 case IDL_S_NO_SCHEMA
:
481 } else if (msg
->type
== JSONRPC_NOTIFY
482 && !strcmp(msg
->method
, "update")
483 && msg
->params
->type
== JSON_ARRAY
484 && msg
->params
->u
.array
.n
== 2
485 && msg
->params
->u
.array
.elems
[0]->type
== JSON_STRING
) {
486 /* Database contents changed. */
487 ovsdb_idl_parse_update(idl
, msg
->params
->u
.array
.elems
[1],
489 } else if (msg
->type
== JSONRPC_REPLY
490 && idl
->lock_request_id
491 && json_equal(idl
->lock_request_id
, msg
->id
)) {
492 /* Reply to our "lock" request. */
493 ovsdb_idl_parse_lock_reply(idl
, msg
->result
);
494 } else if (msg
->type
== JSONRPC_NOTIFY
495 && !strcmp(msg
->method
, "locked")) {
496 /* We got our lock. */
497 ovsdb_idl_parse_lock_notify(idl
, msg
->params
, true);
498 } else if (msg
->type
== JSONRPC_NOTIFY
499 && !strcmp(msg
->method
, "stolen")) {
500 /* Someone else stole our lock. */
501 ovsdb_idl_parse_lock_notify(idl
, msg
->params
, false);
502 } else if (msg
->type
== JSONRPC_ERROR
503 && idl
->state
== IDL_S_MONITOR_COND_REQUESTED
505 && json_equal(idl
->request_id
, msg
->id
)) {
506 if (msg
->error
&& !strcmp(json_string(msg
->error
),
508 /* Fall back to using "monitor" method. */
509 json_destroy(idl
->request_id
);
510 idl
->request_id
= NULL
;
511 ovsdb_idl_send_monitor_request(idl
);
512 idl
->state
= IDL_S_MONITOR_REQUESTED
;
514 } else if (msg
->type
== JSONRPC_ERROR
515 && idl
->state
== IDL_S_MONITORING_COND
517 && json_equal(idl
->request_id
, msg
->id
)) {
518 json_destroy(idl
->request_id
);
519 idl
->request_id
= NULL
;
520 VLOG_ERR("%s: conditional monitor update failed",
521 jsonrpc_session_get_name(idl
->session
));
522 idl
->state
= IDL_S_NO_SCHEMA
;
523 } else if (msg
->type
== JSONRPC_ERROR
524 && idl
->state
== IDL_S_SCHEMA_REQUESTED
526 && json_equal(idl
->request_id
, msg
->id
)) {
527 json_destroy(idl
->request_id
);
528 idl
->request_id
= NULL
;
529 VLOG_ERR("%s: requested schema not found",
530 jsonrpc_session_get_name(idl
->session
));
531 idl
->state
= IDL_S_NO_SCHEMA
;
532 } else if ((msg
->type
== JSONRPC_ERROR
533 || msg
->type
== JSONRPC_REPLY
)
534 && ovsdb_idl_txn_process_reply(idl
, msg
)) {
535 /* ovsdb_idl_txn_process_reply() did everything needful. */
537 /* This can happen if ovsdb_idl_txn_destroy() is called to destroy
538 * a transaction before we receive the reply, so keep the log level
540 VLOG_DBG("%s: received unexpected %s message",
541 jsonrpc_session_get_name(idl
->session
),
542 jsonrpc_msg_type_to_string(msg
->type
));
544 jsonrpc_msg_destroy(msg
);
546 ovsdb_idl_row_destroy_postprocess(idl
);
549 /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to
550 * do or when activity occurs on a transaction on 'idl'. */
552 ovsdb_idl_wait(struct ovsdb_idl
*idl
)
554 jsonrpc_session_wait(idl
->session
);
555 jsonrpc_session_recv_wait(idl
->session
);
558 /* Returns a "sequence number" that represents the state of 'idl'. When
559 * ovsdb_idl_run() changes the database, the sequence number changes. The
560 * initial fetch of the entire contents of the remote database is considered to
561 * be one kind of change. Successfully acquiring a lock, if one has been
562 * configured with ovsdb_idl_set_lock(), is also considered to be a change.
564 * As long as the sequence number does not change, the client may continue to
565 * use any data structures it obtains from 'idl'. But when it changes, the
566 * client must not access any of these data structures again, because they
567 * could have freed or reused for other purposes.
569 * The sequence number can occasionally change even if the database does not.
570 * This happens if the connection to the database drops and reconnects, which
571 * causes the database contents to be reloaded even if they didn't change. (It
572 * could also happen if the database server sends out a "change" that reflects
573 * what the IDL already thought was in the database. The database server is
574 * not supposed to do that, but bugs could in theory cause it to do so.) */
576 ovsdb_idl_get_seqno(const struct ovsdb_idl
*idl
)
578 return idl
->change_seqno
;
581 /* Returns a "sequence number" that represents the number of conditional
582 * monitoring updates successfully received by the OVSDB server of an IDL
585 * ovsdb_idl_set_condition() sets a new condition that is different from
586 * the current condtion, the next expected "sequence number" is returned.
588 * Whenever ovsdb_idl_get_cond_seqno() returns a value that matches
589 * the return value of ovsdb_idl_set_condition(), The client is
591 * - The ovsdb_idl_set_condition() changes has been acknowledged by
594 * - 'idl' now contains the content matches the new conditions. */
596 ovsdb_idl_get_condition_seqno(const struct ovsdb_idl
*idl
)
598 return idl
->cond_seqno
;
601 /* Returns true if 'idl' successfully connected to the remote database and
602 * retrieved its contents (even if the connection subsequently dropped and is
603 * in the process of reconnecting). If so, then 'idl' contains an atomic
604 * snapshot of the database's contents (but it might be arbitrarily old if the
605 * connection dropped).
607 * Returns false if 'idl' has never connected or retrieved the database's
608 * contents. If so, 'idl' is empty. */
610 ovsdb_idl_has_ever_connected(const struct ovsdb_idl
*idl
)
612 return ovsdb_idl_get_seqno(idl
) != 0;
615 /* Reconfigures 'idl' so that it would reconnect to the database, if
616 * connection was dropped. */
618 ovsdb_idl_enable_reconnect(struct ovsdb_idl
*idl
)
620 jsonrpc_session_enable_reconnect(idl
->session
);
623 /* Forces 'idl' to drop its connection to the database and reconnect. In the
624 * meantime, the contents of 'idl' will not change. */
626 ovsdb_idl_force_reconnect(struct ovsdb_idl
*idl
)
628 jsonrpc_session_force_reconnect(idl
->session
);
631 /* Some IDL users should only write to write-only columns. Furthermore,
632 * writing to a column which is not write-only can cause serious performance
633 * degradations for these users. This function causes 'idl' to reject writes
634 * to columns which are not marked write only using ovsdb_idl_omit_alert(). */
636 ovsdb_idl_verify_write_only(struct ovsdb_idl
*idl
)
638 idl
->verify_write_only
= true;
641 /* Returns true if 'idl' is currently connected or trying to connect
642 * and a negative response to a schema request has not been received */
644 ovsdb_idl_is_alive(const struct ovsdb_idl
*idl
)
646 return jsonrpc_session_is_alive(idl
->session
) &&
647 idl
->state
!= IDL_S_NO_SCHEMA
;
650 /* Returns the last error reported on a connection by 'idl'. The return value
651 * is 0 only if no connection made by 'idl' has ever encountered an error and
652 * a negative response to a schema request has never been received. See
653 * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value
656 ovsdb_idl_get_last_error(const struct ovsdb_idl
*idl
)
660 err
= jsonrpc_session_get_last_error(idl
->session
);
664 } else if (idl
->state
== IDL_S_NO_SCHEMA
) {
671 /* Sets the "probe interval" for 'idl->session' to 'probe_interval', in
675 ovsdb_idl_set_probe_interval(const struct ovsdb_idl
*idl
, int probe_interval
)
677 jsonrpc_session_set_probe_interval(idl
->session
, probe_interval
);
681 find_uuid_in_array(const struct uuid
*target
,
682 const struct uuid
*array
, size_t n
)
684 for (size_t i
= 0; i
< n
; i
++) {
685 if (uuid_equals(&array
[i
], target
)) {
693 array_contains_uuid(const struct uuid
*target
,
694 const struct uuid
*array
, size_t n
)
696 return find_uuid_in_array(target
, array
, n
) != SIZE_MAX
;
700 remove_uuid_from_array(const struct uuid
*target
,
701 struct uuid
*array
, size_t *n
)
703 size_t i
= find_uuid_in_array(target
, array
, *n
);
705 array
[i
] = array
[--*n
];
713 add_row_references(const struct ovsdb_base_type
*type
,
714 const union ovsdb_atom
*atoms
, size_t n_atoms
,
715 const struct uuid
*exclude_uuid
,
716 struct uuid
**dstsp
, size_t *n_dstsp
,
717 size_t *allocated_dstsp
)
719 if (type
->type
!= OVSDB_TYPE_UUID
|| !type
->u
.uuid
.refTableName
) {
723 for (size_t i
= 0; i
< n_atoms
; i
++) {
724 const struct uuid
*uuid
= &atoms
[i
].uuid
;
725 if (!uuid_equals(uuid
, exclude_uuid
)
726 && !array_contains_uuid(uuid
, *dstsp
, *n_dstsp
)) {
727 if (*n_dstsp
>= *allocated_dstsp
) {
728 *dstsp
= x2nrealloc(*dstsp
, allocated_dstsp
,
732 (*dstsp
)[*n_dstsp
] = *uuid
;
738 /* Checks for consistency in 'idl''s graph of arcs between database rows. Each
739 * reference from one row to a different row should be reflected as a "struct
740 * ovsdb_idl_arc" between those rows.
742 * This function is slow, big-O wise, and aborts if it finds an inconsistency,
743 * thus it is only for use in test programs. */
745 ovsdb_idl_check_consistency(const struct ovsdb_idl
*idl
)
747 /* Consistency is broken while a transaction is in progress. */
754 struct uuid
*dsts
= NULL
;
755 size_t allocated_dsts
= 0;
757 for (size_t i
= 0; i
< idl
->class->n_tables
; i
++) {
758 const struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
759 const struct ovsdb_idl_table_class
*class = table
->class;
761 const struct ovsdb_idl_row
*row
;
762 HMAP_FOR_EACH (row
, hmap_node
, &table
->rows
) {
765 size_t n_columns
= shash_count(&row
->table
->columns
);
766 for (size_t j
= 0; j
< n_columns
; j
++) {
767 const struct ovsdb_type
*type
= &class->columns
[j
].type
;
768 const struct ovsdb_datum
*datum
= &row
->new[j
];
769 add_row_references(&type
->key
,
770 datum
->keys
, datum
->n
, &row
->uuid
,
771 &dsts
, &n_dsts
, &allocated_dsts
);
772 add_row_references(&type
->value
,
773 datum
->values
, datum
->n
, &row
->uuid
,
774 &dsts
, &n_dsts
, &allocated_dsts
);
777 const struct ovsdb_idl_arc
*arc
;
778 LIST_FOR_EACH (arc
, src_node
, &row
->src_arcs
) {
779 if (!remove_uuid_from_array(&arc
->dst
->uuid
,
781 VLOG_ERR("unexpected arc from %s row "UUID_FMT
" to %s "
784 UUID_ARGS(&row
->uuid
),
785 arc
->dst
->table
->class->name
,
786 UUID_ARGS(&arc
->dst
->uuid
));
790 for (size_t i
= 0; i
< n_dsts
; i
++) {
791 VLOG_ERR("%s row "UUID_FMT
" missing arc to row "UUID_FMT
,
792 table
->class->name
, UUID_ARGS(&row
->uuid
),
793 UUID_ARGS(&dsts
[i
]));
802 const struct ovsdb_idl_class
*
803 ovsdb_idl_get_class(const struct ovsdb_idl
*idl
)
808 /* Given 'column' in some table in 'class', returns the table's class. */
809 const struct ovsdb_idl_table_class
*
810 ovsdb_idl_table_class_from_column(const struct ovsdb_idl_class
*class,
811 const struct ovsdb_idl_column
*column
)
813 for (size_t i
= 0; i
< class->n_tables
; i
++) {
814 const struct ovsdb_idl_table_class
*tc
= &class->tables
[i
];
815 if (column
>= tc
->columns
&& column
< &tc
->columns
[tc
->n_columns
]) {
823 /* Given 'column' in some table in 'idl', returns the table. */
824 static struct ovsdb_idl_table
*
825 ovsdb_idl_table_from_column(struct ovsdb_idl
*idl
,
826 const struct ovsdb_idl_column
*column
)
828 const struct ovsdb_idl_table_class
*tc
=
829 ovsdb_idl_table_class_from_column(idl
->class, column
);
830 return &idl
->tables
[tc
- idl
->class->tables
];
833 static unsigned char *
834 ovsdb_idl_get_mode(struct ovsdb_idl
*idl
,
835 const struct ovsdb_idl_column
*column
)
837 ovs_assert(!idl
->change_seqno
);
839 const struct ovsdb_idl_table
*table
= ovsdb_idl_table_from_column(idl
,
841 return &table
->modes
[column
- table
->class->columns
];
845 add_ref_table(struct ovsdb_idl
*idl
, const struct ovsdb_base_type
*base
)
847 if (base
->type
== OVSDB_TYPE_UUID
&& base
->u
.uuid
.refTableName
) {
848 struct ovsdb_idl_table
*table
;
850 table
= shash_find_data(&idl
->table_by_name
,
851 base
->u
.uuid
.refTableName
);
853 table
->need_table
= true;
855 VLOG_WARN("%s IDL class missing referenced table %s",
856 idl
->class->database
, base
->u
.uuid
.refTableName
);
861 /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also
862 * ensures that any tables referenced by 'column' will be replicated, even if
863 * no columns in that table are selected for replication (see
864 * ovsdb_idl_add_table() for more information).
866 * This function is only useful if 'monitor_everything_by_default' was false in
867 * the call to ovsdb_idl_create(). This function should be called between
868 * ovsdb_idl_create() and the first call to ovsdb_idl_run().
871 ovsdb_idl_add_column(struct ovsdb_idl
*idl
,
872 const struct ovsdb_idl_column
*column
)
874 *ovsdb_idl_get_mode(idl
, column
) = OVSDB_IDL_MONITOR
| OVSDB_IDL_ALERT
;
875 add_ref_table(idl
, &column
->type
.key
);
876 add_ref_table(idl
, &column
->type
.value
);
879 /* Ensures that the table with class 'tc' will be replicated on 'idl' even if
880 * no columns are selected for replication. Just the necessary data for table
881 * references will be replicated (the UUID of the rows, for instance), any
882 * columns not selected for replication will remain unreplicated.
883 * This can be useful because it allows 'idl' to keep track of what rows in the
884 * table actually exist, which in turn allows columns that reference the table
885 * to have accurate contents. (The IDL presents the database with references to
886 * rows that do not exist removed.)
888 * This function is only useful if 'monitor_everything_by_default' was false in
889 * the call to ovsdb_idl_create(). This function should be called between
890 * ovsdb_idl_create() and the first call to ovsdb_idl_run().
893 ovsdb_idl_add_table(struct ovsdb_idl
*idl
,
894 const struct ovsdb_idl_table_class
*tc
)
898 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
899 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
901 if (table
->class == tc
) {
902 table
->need_table
= true;
910 /* A single clause within an ovsdb_idl_condition. */
911 struct ovsdb_idl_clause
{
912 struct hmap_node hmap_node
; /* In struct ovsdb_idl_condition. */
913 enum ovsdb_function function
; /* Never OVSDB_F_TRUE or OVSDB_F_FALSE. */
914 const struct ovsdb_idl_column
*column
; /* Must be nonnull. */
915 struct ovsdb_datum arg
; /* Has ovsdb_type ->column->type. */
919 ovsdb_idl_clause_hash(const struct ovsdb_idl_clause
*clause
)
921 uint32_t hash
= hash_pointer(clause
->column
, clause
->function
);
922 return ovsdb_datum_hash(&clause
->arg
, &clause
->column
->type
, hash
);
926 ovsdb_idl_clause_equals(const struct ovsdb_idl_clause
*a
,
927 const struct ovsdb_idl_clause
*b
)
929 return (a
->function
== b
->function
930 && a
->column
== b
->column
931 && ovsdb_datum_equals(&a
->arg
, &b
->arg
, &a
->column
->type
));
935 ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause
*clause
)
937 const char *function
= ovsdb_function_to_string(clause
->function
);
938 return json_array_create_3(json_string_create(clause
->column
->name
),
939 json_string_create(function
),
940 ovsdb_datum_to_json(&clause
->arg
,
941 &clause
->column
->type
));
945 ovsdb_idl_clause_destroy(struct ovsdb_idl_clause
*clause
)
948 ovsdb_datum_destroy(&clause
->arg
, &clause
->column
->type
);
953 /* ovsdb_idl_condition. */
956 ovsdb_idl_condition_init(struct ovsdb_idl_condition
*cnd
)
958 hmap_init(&cnd
->clauses
);
959 cnd
->is_true
= false;
963 ovsdb_idl_condition_destroy(struct ovsdb_idl_condition
*cond
)
966 ovsdb_idl_condition_clear(cond
);
967 hmap_destroy(&cond
->clauses
);
972 ovsdb_idl_condition_clear(struct ovsdb_idl_condition
*cond
)
974 struct ovsdb_idl_clause
*clause
, *next
;
975 HMAP_FOR_EACH_SAFE (clause
, next
, hmap_node
, &cond
->clauses
) {
976 hmap_remove(&cond
->clauses
, &clause
->hmap_node
);
977 ovsdb_idl_clause_destroy(clause
);
979 cond
->is_true
= false;
983 ovsdb_idl_condition_is_true(const struct ovsdb_idl_condition
*condition
)
985 return condition
->is_true
;
988 static struct ovsdb_idl_clause
*
989 ovsdb_idl_condition_find_clause(const struct ovsdb_idl_condition
*condition
,
990 const struct ovsdb_idl_clause
*target
,
993 struct ovsdb_idl_clause
*clause
;
994 HMAP_FOR_EACH_WITH_HASH (clause
, hmap_node
, hash
, &condition
->clauses
) {
995 if (ovsdb_idl_clause_equals(clause
, target
)) {
1003 ovsdb_idl_condition_add_clause__(struct ovsdb_idl_condition
*condition
,
1004 const struct ovsdb_idl_clause
*src
,
1007 struct ovsdb_idl_clause
*clause
= xmalloc(sizeof *clause
);
1008 clause
->function
= src
->function
;
1009 clause
->column
= src
->column
;
1010 ovsdb_datum_clone(&clause
->arg
, &src
->arg
, &src
->column
->type
);
1011 hmap_insert(&condition
->clauses
, &clause
->hmap_node
, hash
);
1014 /* Adds a clause to the condition for replicating the table with class 'tc' in
1017 * The IDL replicates only rows in a table that satisfy at least one clause in
1018 * the table's condition. The default condition for a table has a single
1019 * clause with function OVSDB_F_TRUE, so that the IDL replicates all rows in
1020 * the table. When the IDL client replaces the default condition by one of its
1021 * own, the condition can have any number of clauses. If it has no conditions,
1022 * then no rows are replicated.
1024 * Two distinct of clauses can usefully be added:
1026 * - A 'function' of OVSDB_F_TRUE. A "true" clause causes every row to be
1027 * replicated, regardless of whether other clauses exist. 'column' and
1028 * 'arg' are ignored.
1030 * - Binary 'functions' add a clause of the form "<column> <function>
1031 * <arg>", e.g. "column == 5" or "column <= 10". In this case, 'arg' must
1032 * have a type that is compatible with 'column'.
1035 ovsdb_idl_condition_add_clause(struct ovsdb_idl_condition
*condition
,
1036 enum ovsdb_function function
,
1037 const struct ovsdb_idl_column
*column
,
1038 const struct ovsdb_datum
*arg
)
1040 if (condition
->is_true
) {
1041 /* Adding a clause to an always-true condition has no effect. */
1042 } else if (function
== OVSDB_F_TRUE
) {
1043 ovsdb_idl_condition_add_clause_true(condition
);
1044 } else if (function
== OVSDB_F_FALSE
) {
1045 /* Adding a "false" clause never has any effect. */
1047 struct ovsdb_idl_clause clause
= {
1048 .function
= function
,
1052 uint32_t hash
= ovsdb_idl_clause_hash(&clause
);
1053 if (!ovsdb_idl_condition_find_clause(condition
, &clause
, hash
)) {
1054 ovsdb_idl_condition_add_clause__(condition
, &clause
, hash
);
1060 ovsdb_idl_condition_add_clause_true(struct ovsdb_idl_condition
*condition
)
1062 if (!condition
->is_true
) {
1063 ovsdb_idl_condition_clear(condition
);
1064 condition
->is_true
= true;
1069 ovsdb_idl_condition_equals(const struct ovsdb_idl_condition
*a
,
1070 const struct ovsdb_idl_condition
*b
)
1072 if (hmap_count(&a
->clauses
) != hmap_count(&b
->clauses
)) {
1075 if (a
->is_true
!= b
->is_true
) {
1079 const struct ovsdb_idl_clause
*clause
;
1080 HMAP_FOR_EACH (clause
, hmap_node
, &a
->clauses
) {
1081 if (!ovsdb_idl_condition_find_clause(b
, clause
,
1082 clause
->hmap_node
.hash
)) {
1090 ovsdb_idl_condition_clone(struct ovsdb_idl_condition
*dst
,
1091 const struct ovsdb_idl_condition
*src
)
1093 ovsdb_idl_condition_init(dst
);
1095 dst
->is_true
= src
->is_true
;
1097 const struct ovsdb_idl_clause
*clause
;
1098 HMAP_FOR_EACH (clause
, hmap_node
, &src
->clauses
) {
1099 ovsdb_idl_condition_add_clause__(dst
, clause
, clause
->hmap_node
.hash
);
1103 /* Sets the replication condition for 'tc' in 'idl' to 'condition' and
1104 * arranges to send the new condition to the database server.
1106 * Return the next conditional update sequence number. When this
1107 * value and ovsdb_idl_get_condition_seqno() matchs, the 'idl'
1108 * contains rows that match the 'condition'.
1111 ovsdb_idl_set_condition(struct ovsdb_idl
*idl
,
1112 const struct ovsdb_idl_table_class
*tc
,
1113 const struct ovsdb_idl_condition
*condition
)
1115 struct ovsdb_idl_table
*table
= ovsdb_idl_table_from_class(idl
, tc
);
1116 unsigned int seqno
= idl
->cond_seqno
;
1117 if (!ovsdb_idl_condition_equals(condition
, &table
->condition
)) {
1118 ovsdb_idl_condition_destroy(&table
->condition
);
1119 ovsdb_idl_condition_clone(&table
->condition
, condition
);
1120 idl
->cond_changed
= table
->cond_changed
= true;
1121 poll_immediate_wake();
1128 static struct json
*
1129 ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition
*cnd
)
1132 return json_array_create_empty();
1135 size_t n
= hmap_count(&cnd
->clauses
);
1137 return json_array_create_1(json_boolean_create(false));
1140 struct json
**clauses
= xmalloc(n
* sizeof *clauses
);
1141 const struct ovsdb_idl_clause
*clause
;
1143 HMAP_FOR_EACH (clause
, hmap_node
, &cnd
->clauses
) {
1144 clauses
[i
++] = ovsdb_idl_clause_to_json(clause
);
1147 return json_array_create(clauses
, n
);
1150 static struct json
*
1151 ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table
*table
)
1153 const struct ovsdb_idl_condition
*cond
= &table
->condition
;
1154 struct json
*monitor_cond_change_request
= json_object_create();
1155 struct json
*cond_json
= ovsdb_idl_condition_to_json(cond
);
1157 json_object_put(monitor_cond_change_request
, "where", cond_json
);
1159 return monitor_cond_change_request
;
1163 ovsdb_idl_send_cond_change(struct ovsdb_idl
*idl
)
1166 char uuid
[UUID_LEN
+ 1];
1167 struct json
*params
, *json_uuid
;
1168 struct jsonrpc_msg
*request
;
1170 /* When 'idl-request_id' is not NULL, there is an outstanding
1171 * conditional monitoring update request that we have not heard
1172 * from the server yet. Don't generate another request in this case. */
1173 if (!idl
->cond_changed
|| !jsonrpc_session_is_connected(idl
->session
) ||
1174 idl
->state
!= IDL_S_MONITORING_COND
|| idl
->request_id
) {
1178 struct json
*monitor_cond_change_requests
= NULL
;
1180 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1181 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1183 if (table
->cond_changed
) {
1184 struct json
*req
= ovsdb_idl_create_cond_change_req(table
);
1186 if (!monitor_cond_change_requests
) {
1187 monitor_cond_change_requests
= json_object_create();
1189 json_object_put(monitor_cond_change_requests
,
1191 json_array_create_1(req
));
1193 table
->cond_changed
= false;
1197 /* Send request if not empty. */
1198 if (monitor_cond_change_requests
) {
1199 snprintf(uuid
, sizeof uuid
, UUID_FMT
,
1200 UUID_ARGS(&idl
->uuid
));
1201 json_uuid
= json_string_create(uuid
);
1203 /* Create a new uuid */
1204 uuid_generate(&idl
->uuid
);
1205 snprintf(uuid
, sizeof uuid
, UUID_FMT
,
1206 UUID_ARGS(&idl
->uuid
));
1207 params
= json_array_create_3(json_uuid
, json_string_create(uuid
),
1208 monitor_cond_change_requests
);
1210 request
= jsonrpc_create_request("monitor_cond_change", params
,
1212 jsonrpc_session_send(idl
->session
, request
);
1214 idl
->cond_changed
= false;
1217 /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'.
1219 * This function should be called between ovsdb_idl_create() and the first call
1220 * to ovsdb_idl_run().
1223 ovsdb_idl_omit_alert(struct ovsdb_idl
*idl
,
1224 const struct ovsdb_idl_column
*column
)
1226 *ovsdb_idl_get_mode(idl
, column
) &= ~OVSDB_IDL_ALERT
;
1229 /* Sets the mode for 'column' in 'idl' to 0. See the big comment above
1230 * OVSDB_IDL_MONITOR for details.
1232 * This function should be called between ovsdb_idl_create() and the first call
1233 * to ovsdb_idl_run().
1236 ovsdb_idl_omit(struct ovsdb_idl
*idl
, const struct ovsdb_idl_column
*column
)
1238 *ovsdb_idl_get_mode(idl
, column
) = 0;
1241 /* Returns the most recent IDL change sequence number that caused a
1242 * insert, modify or delete update to the table with class 'table_class'.
1245 ovsdb_idl_table_get_seqno(const struct ovsdb_idl
*idl
,
1246 const struct ovsdb_idl_table_class
*table_class
)
1248 struct ovsdb_idl_table
*table
1249 = ovsdb_idl_table_from_class(idl
, table_class
);
1250 unsigned int max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_INSERT
];
1252 if (max_seqno
< table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
]) {
1253 max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_MODIFY
];
1255 if (max_seqno
< table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]) {
1256 max_seqno
= table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
];
1261 /* For each row that contains tracked columns, IDL stores the most
1262 * recent IDL change sequence numbers associateed with insert, modify
1263 * and delete updates to the table.
1266 ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row
*row
,
1267 enum ovsdb_idl_change change
)
1269 return row
->change_seqno
[change
];
1272 /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that
1273 * all rows whose 'column' is modified are traced. Similarly, insert
1274 * or delete of rows having 'column' are tracked. Clients are able
1275 * to retrive the tracked rows with the ovsdb_idl_track_get_*()
1278 * This function should be called between ovsdb_idl_create() and
1279 * the first call to ovsdb_idl_run(). The column to be tracked
1280 * should have OVSDB_IDL_ALERT turned on.
1283 ovsdb_idl_track_add_column(struct ovsdb_idl
*idl
,
1284 const struct ovsdb_idl_column
*column
)
1286 if (!(*ovsdb_idl_get_mode(idl
, column
) & OVSDB_IDL_ALERT
)) {
1287 ovsdb_idl_add_column(idl
, column
);
1289 *ovsdb_idl_get_mode(idl
, column
) |= OVSDB_IDL_TRACK
;
1293 ovsdb_idl_track_add_all(struct ovsdb_idl
*idl
)
1297 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1298 const struct ovsdb_idl_table_class
*tc
= &idl
->class->tables
[i
];
1300 for (j
= 0; j
< tc
->n_columns
; j
++) {
1301 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
1302 ovsdb_idl_track_add_column(idl
, column
);
1307 /* Returns true if 'table' has any tracked column. */
1309 ovsdb_idl_track_is_set(struct ovsdb_idl_table
*table
)
1313 for (i
= 0; i
< table
->class->n_columns
; i
++) {
1314 if (table
->modes
[i
] & OVSDB_IDL_TRACK
) {
1321 /* Returns the first tracked row in table with class 'table_class'
1322 * for the specified 'idl'. Returns NULL if there are no tracked rows */
1323 const struct ovsdb_idl_row
*
1324 ovsdb_idl_track_get_first(const struct ovsdb_idl
*idl
,
1325 const struct ovsdb_idl_table_class
*table_class
)
1327 struct ovsdb_idl_table
*table
1328 = ovsdb_idl_table_from_class(idl
, table_class
);
1330 if (!ovs_list_is_empty(&table
->track_list
)) {
1331 return CONTAINER_OF(ovs_list_front(&table
->track_list
), struct ovsdb_idl_row
, track_node
);
1336 /* Returns the next tracked row in table after the specified 'row'
1337 * (in no particular order). Returns NULL if there are no tracked rows */
1338 const struct ovsdb_idl_row
*
1339 ovsdb_idl_track_get_next(const struct ovsdb_idl_row
*row
)
1341 if (row
->track_node
.next
!= &row
->table
->track_list
) {
1342 return CONTAINER_OF(row
->track_node
.next
, struct ovsdb_idl_row
, track_node
);
1348 /* Returns true if a tracked 'column' in 'row' was updated by IDL, false
1349 * otherwise. The tracking data is cleared by ovsdb_idl_track_clear()
1351 * Function returns false if 'column' is not tracked (see
1352 * ovsdb_idl_track_add_column()).
1355 ovsdb_idl_track_is_updated(const struct ovsdb_idl_row
*row
,
1356 const struct ovsdb_idl_column
*column
)
1358 const struct ovsdb_idl_table_class
*class;
1361 class = row
->table
->class;
1362 column_idx
= column
- class->columns
;
1364 if (row
->updated
&& bitmap_is_set(row
->updated
, column_idx
)) {
1371 /* Flushes the tracked rows. Client calls this function after calling
1372 * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*()
1373 * functions. This is usually done at the end of the client's processing
1374 * loop when it is ready to do ovsdb_idl_run() again.
1377 ovsdb_idl_track_clear(const struct ovsdb_idl
*idl
)
1381 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1382 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1384 if (!ovs_list_is_empty(&table
->track_list
)) {
1385 struct ovsdb_idl_row
*row
, *next
;
1387 LIST_FOR_EACH_SAFE(row
, next
, track_node
, &table
->track_list
) {
1390 row
->updated
= NULL
;
1392 ovs_list_remove(&row
->track_node
);
1393 ovs_list_init(&row
->track_node
);
1394 if (ovsdb_idl_row_is_orphan(row
)) {
1395 ovsdb_idl_row_clear_old(row
);
1405 ovsdb_idl_send_schema_request(struct ovsdb_idl
*idl
)
1407 struct jsonrpc_msg
*msg
;
1409 json_destroy(idl
->request_id
);
1410 msg
= jsonrpc_create_request(
1412 json_array_create_1(json_string_create(idl
->class->database
)),
1414 jsonrpc_session_send(idl
->session
, msg
);
1418 log_error(struct ovsdb_error
*error
)
1420 char *s
= ovsdb_error_to_string(error
);
1421 VLOG_WARN("error parsing database schema: %s", s
);
1423 ovsdb_error_destroy(error
);
1426 /* Frees 'schema', which is in the format returned by parse_schema(). */
1428 free_schema(struct shash
*schema
)
1431 struct shash_node
*node
, *next
;
1433 SHASH_FOR_EACH_SAFE (node
, next
, schema
) {
1434 struct sset
*sset
= node
->data
;
1437 shash_delete(schema
, node
);
1439 shash_destroy(schema
);
1444 /* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC
1445 * 7047, to obtain the names of its rows and columns. If successful, returns
1446 * an shash whose keys are table names and whose values are ssets, where each
1447 * sset contains the names of its table's columns. On failure (due to a parse
1448 * error), returns NULL.
1450 * It would also be possible to use the general-purpose OVSDB schema parser in
1451 * ovsdb-server, but that's overkill, possibly too strict for the current use
1452 * case, and would require restructuring ovsdb-server to separate the schema
1453 * code from the rest. */
1454 static struct shash
*
1455 parse_schema(const struct json
*schema_json
)
1457 struct ovsdb_parser parser
;
1458 const struct json
*tables_json
;
1459 struct ovsdb_error
*error
;
1460 struct shash_node
*node
;
1461 struct shash
*schema
;
1463 ovsdb_parser_init(&parser
, schema_json
, "database schema");
1464 tables_json
= ovsdb_parser_member(&parser
, "tables", OP_OBJECT
);
1465 error
= ovsdb_parser_destroy(&parser
);
1471 schema
= xmalloc(sizeof *schema
);
1473 SHASH_FOR_EACH (node
, json_object(tables_json
)) {
1474 const char *table_name
= node
->name
;
1475 const struct json
*json
= node
->data
;
1476 const struct json
*columns_json
;
1478 ovsdb_parser_init(&parser
, json
, "table schema for table %s",
1480 columns_json
= ovsdb_parser_member(&parser
, "columns", OP_OBJECT
);
1481 error
= ovsdb_parser_destroy(&parser
);
1484 free_schema(schema
);
1488 struct sset
*columns
= xmalloc(sizeof *columns
);
1491 struct shash_node
*node2
;
1492 SHASH_FOR_EACH (node2
, json_object(columns_json
)) {
1493 const char *column_name
= node2
->name
;
1494 sset_add(columns
, column_name
);
1496 shash_add(schema
, table_name
, columns
);
1502 ovsdb_idl_send_monitor_request__(struct ovsdb_idl
*idl
,
1505 struct shash
*schema
;
1506 struct json
*monitor_requests
;
1507 struct jsonrpc_msg
*msg
;
1508 char uuid
[UUID_LEN
+ 1];
1511 schema
= parse_schema(idl
->schema
);
1512 monitor_requests
= json_object_create();
1513 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
1514 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
1515 const struct ovsdb_idl_table_class
*tc
= table
->class;
1516 struct json
*monitor_request
, *columns
, *where
;
1517 const struct sset
*table_schema
;
1520 table_schema
= (schema
1521 ? shash_find_data(schema
, table
->class->name
)
1524 columns
= table
->need_table
? json_array_create_empty() : NULL
;
1525 for (j
= 0; j
< tc
->n_columns
; j
++) {
1526 const struct ovsdb_idl_column
*column
= &tc
->columns
[j
];
1527 if (table
->modes
[j
] & OVSDB_IDL_MONITOR
) {
1529 && !sset_contains(table_schema
, column
->name
)) {
1530 VLOG_WARN("%s table in %s database lacks %s column "
1531 "(database needs upgrade?)",
1532 table
->class->name
, idl
->class->database
,
1537 columns
= json_array_create_empty();
1539 json_array_add(columns
, json_string_create(column
->name
));
1544 if (schema
&& !table_schema
) {
1545 VLOG_WARN("%s database lacks %s table "
1546 "(database needs upgrade?)",
1547 idl
->class->database
, table
->class->name
);
1548 json_destroy(columns
);
1552 monitor_request
= json_object_create();
1553 json_object_put(monitor_request
, "columns", columns
);
1554 if (!strcmp(method
, "monitor_cond")
1555 && !ovsdb_idl_condition_is_true(&table
->condition
)) {
1556 where
= ovsdb_idl_condition_to_json(&table
->condition
);
1557 json_object_put(monitor_request
, "where", where
);
1558 table
->cond_changed
= false;
1560 json_object_put(monitor_requests
, tc
->name
, monitor_request
);
1563 free_schema(schema
);
1565 json_destroy(idl
->request_id
);
1567 snprintf(uuid
, sizeof uuid
, UUID_FMT
, UUID_ARGS(&idl
->uuid
));
1568 msg
= jsonrpc_create_request(
1570 json_array_create_3(json_string_create(idl
->class->database
),
1571 json_string_create(uuid
), monitor_requests
),
1573 jsonrpc_session_send(idl
->session
, msg
);
1574 idl
->cond_changed
= false;
1578 ovsdb_idl_send_monitor_request(struct ovsdb_idl
*idl
)
1580 ovsdb_idl_send_monitor_request__(idl
, "monitor");
1584 log_parse_update_error(struct ovsdb_error
*error
)
1586 if (!VLOG_DROP_WARN(&syntax_rl
)) {
1587 char *s
= ovsdb_error_to_string(error
);
1588 VLOG_WARN_RL(&syntax_rl
, "%s", s
);
1591 ovsdb_error_destroy(error
);
1595 ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl
*idl
)
1597 ovsdb_idl_send_monitor_request__(idl
, "monitor_cond");
1601 ovsdb_idl_parse_update(struct ovsdb_idl
*idl
, const struct json
*table_updates
,
1602 enum ovsdb_update_version version
)
1604 struct ovsdb_error
*error
= ovsdb_idl_parse_update__(idl
, table_updates
,
1607 log_parse_update_error(error
);
1611 static struct ovsdb_error
*
1612 ovsdb_idl_parse_update__(struct ovsdb_idl
*idl
,
1613 const struct json
*table_updates
,
1614 enum ovsdb_update_version version
)
1616 const struct shash_node
*tables_node
;
1617 const char *table_updates_name
= table_updates_names
[version
];
1618 const char *table_update_name
= table_update_names
[version
];
1619 const char *row_update_name
= row_update_names
[version
];
1621 if (table_updates
->type
!= JSON_OBJECT
) {
1622 return ovsdb_syntax_error(table_updates
, NULL
,
1623 "<%s> is not an object",
1624 table_updates_name
);
1627 SHASH_FOR_EACH (tables_node
, json_object(table_updates
)) {
1628 const struct json
*table_update
= tables_node
->data
;
1629 const struct shash_node
*table_node
;
1630 struct ovsdb_idl_table
*table
;
1632 table
= shash_find_data(&idl
->table_by_name
, tables_node
->name
);
1634 return ovsdb_syntax_error(
1635 table_updates
, NULL
,
1636 "<%s> includes unknown table \"%s\"",
1641 if (table_update
->type
!= JSON_OBJECT
) {
1642 return ovsdb_syntax_error(table_update
, NULL
,
1643 "<%s> for table \"%s\" is "
1646 table
->class->name
);
1648 SHASH_FOR_EACH (table_node
, json_object(table_update
)) {
1649 const struct json
*row_update
= table_node
->data
;
1650 const struct json
*old_json
, *new_json
;
1653 if (!uuid_from_string(&uuid
, table_node
->name
)) {
1654 return ovsdb_syntax_error(table_update
, NULL
,
1655 "<%s> for table \"%s\" "
1656 "contains bad UUID "
1657 "\"%s\" as member name",
1662 if (row_update
->type
!= JSON_OBJECT
) {
1663 return ovsdb_syntax_error(row_update
, NULL
,
1664 "<%s> for table \"%s\" "
1665 "contains <%s> for %s that "
1675 old_json
= shash_find_data(json_object(row_update
), "old");
1676 new_json
= shash_find_data(json_object(row_update
), "new");
1677 if (old_json
&& old_json
->type
!= JSON_OBJECT
) {
1678 return ovsdb_syntax_error(old_json
, NULL
,
1679 "\"old\" <row> is not object");
1680 } else if (new_json
&& new_json
->type
!= JSON_OBJECT
) {
1681 return ovsdb_syntax_error(new_json
, NULL
,
1682 "\"new\" <row> is not object");
1683 } else if ((old_json
!= NULL
) + (new_json
!= NULL
)
1684 != shash_count(json_object(row_update
))) {
1685 return ovsdb_syntax_error(row_update
, NULL
,
1686 "<row-update> contains "
1687 "unexpected member");
1688 } else if (!old_json
&& !new_json
) {
1689 return ovsdb_syntax_error(row_update
, NULL
,
1690 "<row-update> missing \"old\" "
1691 "and \"new\" members");
1694 if (ovsdb_idl_process_update(table
, &uuid
, old_json
,
1696 idl
->change_seqno
++;
1700 case OVSDB_UPDATE2
: {
1701 const char *ops
[] = {"modify", "insert", "delete", "initial"};
1702 const char *operation
;
1703 const struct json
*row
;
1706 for (i
= 0; i
< ARRAY_SIZE(ops
); i
++) {
1708 row
= shash_find_data(json_object(row_update
), operation
);
1711 if (ovsdb_idl_process_update2(table
, &uuid
, operation
,
1713 idl
->change_seqno
++;
1719 /* row_update2 should contain one of the objects */
1720 if (i
== ARRAY_SIZE(ops
)) {
1721 return ovsdb_syntax_error(row_update
, NULL
,
1722 "<row_update2> includes unknown "
1737 static struct ovsdb_idl_row
*
1738 ovsdb_idl_get_row(struct ovsdb_idl_table
*table
, const struct uuid
*uuid
)
1740 struct ovsdb_idl_row
*row
;
1742 HMAP_FOR_EACH_WITH_HASH (row
, hmap_node
, uuid_hash(uuid
), &table
->rows
) {
1743 if (uuid_equals(&row
->uuid
, uuid
)) {
1750 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1753 ovsdb_idl_process_update(struct ovsdb_idl_table
*table
,
1754 const struct uuid
*uuid
, const struct json
*old
,
1755 const struct json
*new)
1757 struct ovsdb_idl_row
*row
;
1759 row
= ovsdb_idl_get_row(table
, uuid
);
1762 if (row
&& !ovsdb_idl_row_is_orphan(row
)) {
1763 /* XXX perhaps we should check the 'old' values? */
1764 ovsdb_idl_delete_row(row
);
1766 VLOG_WARN_RL(&semantic_rl
, "cannot delete missing row "UUID_FMT
" "
1768 UUID_ARGS(uuid
), table
->class->name
);
1774 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), new);
1775 } else if (ovsdb_idl_row_is_orphan(row
)) {
1776 ovsdb_idl_insert_row(row
, new);
1778 VLOG_WARN_RL(&semantic_rl
, "cannot add existing row "UUID_FMT
" to "
1779 "table %s", UUID_ARGS(uuid
), table
->class->name
);
1780 return ovsdb_idl_modify_row(row
, new);
1785 /* XXX perhaps we should check the 'old' values? */
1786 if (!ovsdb_idl_row_is_orphan(row
)) {
1787 return ovsdb_idl_modify_row(row
, new);
1789 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing but "
1790 "referenced row "UUID_FMT
" in table %s",
1791 UUID_ARGS(uuid
), table
->class->name
);
1792 ovsdb_idl_insert_row(row
, new);
1795 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing row "UUID_FMT
" "
1796 "in table %s", UUID_ARGS(uuid
), table
->class->name
);
1797 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), new);
1804 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1807 ovsdb_idl_process_update2(struct ovsdb_idl_table
*table
,
1808 const struct uuid
*uuid
,
1809 const char *operation
,
1810 const struct json
*json_row
)
1812 struct ovsdb_idl_row
*row
;
1814 row
= ovsdb_idl_get_row(table
, uuid
);
1815 if (!strcmp(operation
, "delete")) {
1817 if (row
&& !ovsdb_idl_row_is_orphan(row
)) {
1818 ovsdb_idl_delete_row(row
);
1820 VLOG_WARN_RL(&semantic_rl
, "cannot delete missing row "UUID_FMT
" "
1822 UUID_ARGS(uuid
), table
->class->name
);
1825 } else if (!strcmp(operation
, "insert") || !strcmp(operation
, "initial")) {
1828 ovsdb_idl_insert_row(ovsdb_idl_row_create(table
, uuid
), json_row
);
1829 } else if (ovsdb_idl_row_is_orphan(row
)) {
1830 ovsdb_idl_insert_row(row
, json_row
);
1832 VLOG_WARN_RL(&semantic_rl
, "cannot add existing row "UUID_FMT
" to "
1833 "table %s", UUID_ARGS(uuid
), table
->class->name
);
1834 ovsdb_idl_delete_row(row
);
1835 ovsdb_idl_insert_row(row
, json_row
);
1837 } else if (!strcmp(operation
, "modify")) {
1840 if (!ovsdb_idl_row_is_orphan(row
)) {
1841 return ovsdb_idl_modify_row_by_diff(row
, json_row
);
1843 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing but "
1844 "referenced row "UUID_FMT
" in table %s",
1845 UUID_ARGS(uuid
), table
->class->name
);
1849 VLOG_WARN_RL(&semantic_rl
, "cannot modify missing row "UUID_FMT
" "
1850 "in table %s", UUID_ARGS(uuid
), table
->class->name
);
1854 VLOG_WARN_RL(&semantic_rl
, "unknown operation %s to "
1855 "table %s", operation
, table
->class->name
);
1862 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
1865 * Change 'row' either with the content of 'row_json' or by apply 'diff'.
1866 * Caller needs to provide either valid 'row_json' or 'diff', but not
1869 ovsdb_idl_row_change__(struct ovsdb_idl_row
*row
, const struct json
*row_json
,
1870 const struct json
*diff_json
,
1871 enum ovsdb_idl_change change
)
1873 struct ovsdb_idl_table
*table
= row
->table
;
1874 const struct ovsdb_idl_table_class
*class = table
->class;
1875 struct shash_node
*node
;
1876 bool changed
= false;
1877 bool apply_diff
= diff_json
!= NULL
;
1878 const struct json
*json
= apply_diff
? diff_json
: row_json
;
1880 SHASH_FOR_EACH (node
, json_object(json
)) {
1881 const char *column_name
= node
->name
;
1882 const struct ovsdb_idl_column
*column
;
1883 struct ovsdb_datum datum
;
1884 struct ovsdb_error
*error
;
1885 unsigned int column_idx
;
1886 struct ovsdb_datum
*old
;
1888 column
= shash_find_data(&table
->columns
, column_name
);
1890 VLOG_WARN_RL(&syntax_rl
, "unknown column %s updating row "UUID_FMT
,
1891 column_name
, UUID_ARGS(&row
->uuid
));
1895 column_idx
= column
- table
->class->columns
;
1896 old
= &row
->old
[column_idx
];
1900 struct ovsdb_datum diff
;
1902 ovs_assert(!row_json
);
1903 error
= ovsdb_transient_datum_from_json(&diff
, &column
->type
,
1906 error
= ovsdb_datum_apply_diff(&datum
, old
, &diff
,
1908 ovsdb_datum_destroy(&diff
, &column
->type
);
1911 ovs_assert(!diff_json
);
1912 error
= ovsdb_datum_from_json(&datum
, &column
->type
, node
->data
,
1917 if (!ovsdb_datum_equals(old
, &datum
, &column
->type
)) {
1918 ovsdb_datum_swap(old
, &datum
);
1919 if (table
->modes
[column_idx
] & OVSDB_IDL_ALERT
) {
1921 row
->change_seqno
[change
]
1922 = row
->table
->change_seqno
[change
]
1923 = row
->table
->idl
->change_seqno
+ 1;
1924 if (table
->modes
[column_idx
] & OVSDB_IDL_TRACK
) {
1925 if (!ovs_list_is_empty(&row
->track_node
)) {
1926 ovs_list_remove(&row
->track_node
);
1928 ovs_list_push_back(&row
->table
->track_list
,
1930 if (!row
->updated
) {
1931 row
->updated
= bitmap_allocate(class->n_columns
);
1933 bitmap_set1(row
->updated
, column_idx
);
1937 /* Didn't really change but the OVSDB monitor protocol always
1938 * includes every value in a row. */
1941 ovsdb_datum_destroy(&datum
, &column
->type
);
1943 char *s
= ovsdb_error_to_string(error
);
1944 VLOG_WARN_RL(&syntax_rl
, "error parsing column %s in row "UUID_FMT
1945 " in table %s: %s", column_name
,
1946 UUID_ARGS(&row
->uuid
), table
->class->name
, s
);
1948 ovsdb_error_destroy(error
);
1955 ovsdb_idl_row_update(struct ovsdb_idl_row
*row
, const struct json
*row_json
,
1956 enum ovsdb_idl_change change
)
1958 return ovsdb_idl_row_change__(row
, row_json
, NULL
, change
);
1962 ovsdb_idl_row_apply_diff(struct ovsdb_idl_row
*row
,
1963 const struct json
*diff_json
,
1964 enum ovsdb_idl_change change
)
1966 return ovsdb_idl_row_change__(row
, NULL
, diff_json
, change
);
1969 /* When a row A refers to row B through a column with a "refTable" constraint,
1970 * but row B does not exist, row B is called an "orphan row". Orphan rows
1971 * should not persist, because the database enforces referential integrity, but
1972 * they can appear transiently as changes from the database are received (the
1973 * database doesn't try to topologically sort them and circular references mean
1974 * it isn't always possible anyhow).
1976 * This function returns true if 'row' is an orphan row, otherwise false.
1979 ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row
*row
)
1981 return !row
->old
&& !row
->new;
1984 /* Returns true if 'row' is conceptually part of the database as modified by
1985 * the current transaction (if any), false otherwise.
1987 * This function will return true if 'row' is not an orphan (see the comment on
1988 * ovsdb_idl_row_is_orphan()) and:
1990 * - 'row' exists in the database and has not been deleted within the
1991 * current transaction (if any).
1993 * - 'row' was inserted within the current transaction and has not been
1994 * deleted. (In the latter case you should not have passed 'row' in at
1995 * all, because ovsdb_idl_txn_delete() freed it.)
1997 * This function will return false if 'row' is an orphan or if 'row' was
1998 * deleted within the current transaction.
2001 ovsdb_idl_row_exists(const struct ovsdb_idl_row
*row
)
2003 return row
->new != NULL
;
2007 ovsdb_idl_row_parse(struct ovsdb_idl_row
*row
)
2009 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2012 for (i
= 0; i
< class->n_columns
; i
++) {
2013 const struct ovsdb_idl_column
*c
= &class->columns
[i
];
2014 (c
->parse
)(row
, &row
->old
[i
]);
2019 ovsdb_idl_row_unparse(struct ovsdb_idl_row
*row
)
2021 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2024 for (i
= 0; i
< class->n_columns
; i
++) {
2025 const struct ovsdb_idl_column
*c
= &class->columns
[i
];
2031 ovsdb_idl_row_clear_old(struct ovsdb_idl_row
*row
)
2033 ovs_assert(row
->old
== row
->new);
2034 if (!ovsdb_idl_row_is_orphan(row
)) {
2035 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2038 for (i
= 0; i
< class->n_columns
; i
++) {
2039 ovsdb_datum_destroy(&row
->old
[i
], &class->columns
[i
].type
);
2042 row
->old
= row
->new = NULL
;
2047 ovsdb_idl_row_clear_new(struct ovsdb_idl_row
*row
)
2049 if (row
->old
!= row
->new) {
2051 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2055 BITMAP_FOR_EACH_1 (i
, class->n_columns
, row
->written
) {
2056 ovsdb_datum_destroy(&row
->new[i
], &class->columns
[i
].type
);
2061 row
->written
= NULL
;
2063 row
->new = row
->old
;
2068 ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row
*row
, bool destroy_dsts
)
2070 struct ovsdb_idl_arc
*arc
, *next
;
2072 /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows
2073 * that this causes to be unreferenced, if tracking is not enabled.
2074 * If tracking is enabled, orphaned nodes are removed from hmap but not
2077 LIST_FOR_EACH_SAFE (arc
, next
, src_node
, &row
->src_arcs
) {
2078 ovs_list_remove(&arc
->dst_node
);
2080 && ovsdb_idl_row_is_orphan(arc
->dst
)
2081 && ovs_list_is_empty(&arc
->dst
->dst_arcs
)) {
2082 ovsdb_idl_row_destroy(arc
->dst
);
2086 ovs_list_init(&row
->src_arcs
);
2089 /* Force nodes that reference 'row' to reparse. */
2091 ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row
*row
)
2093 struct ovsdb_idl_arc
*arc
, *next
;
2095 /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy
2096 * 'arc', so we need to use the "safe" variant of list traversal. However,
2097 * calling an ovsdb_idl_column's 'parse' function will add an arc
2098 * equivalent to 'arc' to row->arcs. That could be a problem for
2099 * traversal, but it adds it at the beginning of the list to prevent us
2100 * from stumbling upon it again.
2102 * (If duplicate arcs were possible then we would need to make sure that
2103 * 'next' didn't also point into 'arc''s destination, but we forbid
2104 * duplicate arcs.) */
2105 LIST_FOR_EACH_SAFE (arc
, next
, dst_node
, &row
->dst_arcs
) {
2106 struct ovsdb_idl_row
*ref
= arc
->src
;
2108 ovsdb_idl_row_unparse(ref
);
2109 ovsdb_idl_row_clear_arcs(ref
, false);
2110 ovsdb_idl_row_parse(ref
);
2114 static struct ovsdb_idl_row
*
2115 ovsdb_idl_row_create__(const struct ovsdb_idl_table_class
*class)
2117 struct ovsdb_idl_row
*row
= xzalloc(class->allocation_size
);
2118 class->row_init(row
);
2119 ovs_list_init(&row
->src_arcs
);
2120 ovs_list_init(&row
->dst_arcs
);
2121 hmap_node_nullify(&row
->txn_node
);
2122 ovs_list_init(&row
->track_node
);
2126 static struct ovsdb_idl_row
*
2127 ovsdb_idl_row_create(struct ovsdb_idl_table
*table
, const struct uuid
*uuid
)
2129 struct ovsdb_idl_row
*row
= ovsdb_idl_row_create__(table
->class);
2130 hmap_insert(&table
->rows
, &row
->hmap_node
, uuid_hash(uuid
));
2133 row
->map_op_written
= NULL
;
2134 row
->map_op_lists
= NULL
;
2135 row
->set_op_written
= NULL
;
2136 row
->set_op_lists
= NULL
;
2141 ovsdb_idl_row_destroy(struct ovsdb_idl_row
*row
)
2144 ovsdb_idl_row_clear_old(row
);
2145 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
2146 ovsdb_idl_destroy_all_map_op_lists(row
);
2147 ovsdb_idl_destroy_all_set_op_lists(row
);
2148 if (ovsdb_idl_track_is_set(row
->table
)) {
2149 row
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]
2150 = row
->table
->change_seqno
[OVSDB_IDL_CHANGE_DELETE
]
2151 = row
->table
->idl
->change_seqno
+ 1;
2153 if (!ovs_list_is_empty(&row
->track_node
)) {
2154 ovs_list_remove(&row
->track_node
);
2156 ovs_list_push_back(&row
->table
->track_list
, &row
->track_node
);
2161 ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row
*row
)
2163 if (row
->map_op_written
) {
2164 /* Clear Map Operation Lists */
2165 size_t idx
, n_columns
;
2166 const struct ovsdb_idl_column
*columns
;
2167 const struct ovsdb_type
*type
;
2168 n_columns
= row
->table
->class->n_columns
;
2169 columns
= row
->table
->class->columns
;
2170 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->map_op_written
) {
2171 type
= &columns
[idx
].type
;
2172 map_op_list_destroy(row
->map_op_lists
[idx
], type
);
2174 free(row
->map_op_lists
);
2175 bitmap_free(row
->map_op_written
);
2176 row
->map_op_lists
= NULL
;
2177 row
->map_op_written
= NULL
;
2182 ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row
*row
)
2184 if (row
->set_op_written
) {
2185 /* Clear Set Operation Lists */
2186 size_t idx
, n_columns
;
2187 const struct ovsdb_idl_column
*columns
;
2188 const struct ovsdb_type
*type
;
2189 n_columns
= row
->table
->class->n_columns
;
2190 columns
= row
->table
->class->columns
;
2191 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->set_op_written
) {
2192 type
= &columns
[idx
].type
;
2193 set_op_list_destroy(row
->set_op_lists
[idx
], type
);
2195 free(row
->set_op_lists
);
2196 bitmap_free(row
->set_op_written
);
2197 row
->set_op_lists
= NULL
;
2198 row
->set_op_written
= NULL
;
2203 ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl
*idl
)
2207 for (i
= 0; i
< idl
->class->n_tables
; i
++) {
2208 struct ovsdb_idl_table
*table
= &idl
->tables
[i
];
2210 if (!ovs_list_is_empty(&table
->track_list
)) {
2211 struct ovsdb_idl_row
*row
, *next
;
2213 LIST_FOR_EACH_SAFE(row
, next
, track_node
, &table
->track_list
) {
2214 if (!ovsdb_idl_track_is_set(row
->table
)) {
2215 ovs_list_remove(&row
->track_node
);
2224 ovsdb_idl_insert_row(struct ovsdb_idl_row
*row
, const struct json
*row_json
)
2226 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2229 ovs_assert(!row
->old
&& !row
->new);
2230 row
->old
= row
->new = xmalloc(class->n_columns
* sizeof *row
->old
);
2231 for (i
= 0; i
< class->n_columns
; i
++) {
2232 ovsdb_datum_init_default(&row
->old
[i
], &class->columns
[i
].type
);
2234 ovsdb_idl_row_update(row
, row_json
, OVSDB_IDL_CHANGE_INSERT
);
2235 ovsdb_idl_row_parse(row
);
2237 ovsdb_idl_row_reparse_backrefs(row
);
2241 ovsdb_idl_delete_row(struct ovsdb_idl_row
*row
)
2243 ovsdb_idl_row_unparse(row
);
2244 ovsdb_idl_row_clear_arcs(row
, true);
2245 ovsdb_idl_row_clear_old(row
);
2246 if (ovs_list_is_empty(&row
->dst_arcs
)) {
2247 ovsdb_idl_row_destroy(row
);
2249 ovsdb_idl_row_reparse_backrefs(row
);
2253 /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false
2256 ovsdb_idl_modify_row(struct ovsdb_idl_row
*row
, const struct json
*row_json
)
2260 ovsdb_idl_row_unparse(row
);
2261 ovsdb_idl_row_clear_arcs(row
, true);
2262 changed
= ovsdb_idl_row_update(row
, row_json
, OVSDB_IDL_CHANGE_MODIFY
);
2263 ovsdb_idl_row_parse(row
);
2269 ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row
*row
,
2270 const struct json
*diff_json
)
2274 ovsdb_idl_row_unparse(row
);
2275 ovsdb_idl_row_clear_arcs(row
, true);
2276 changed
= ovsdb_idl_row_apply_diff(row
, diff_json
,
2277 OVSDB_IDL_CHANGE_MODIFY
);
2278 ovsdb_idl_row_parse(row
);
2284 may_add_arc(const struct ovsdb_idl_row
*src
, const struct ovsdb_idl_row
*dst
)
2286 const struct ovsdb_idl_arc
*arc
;
2293 /* No duplicate arcs.
2295 * We only need to test whether the first arc in dst->dst_arcs originates
2296 * at 'src', since we add all of the arcs from a given source in a clump
2297 * (in a single call to ovsdb_idl_row_parse()) and new arcs are always
2298 * added at the front of the dst_arcs list. */
2299 if (ovs_list_is_empty(&dst
->dst_arcs
)) {
2302 arc
= CONTAINER_OF(dst
->dst_arcs
.next
, struct ovsdb_idl_arc
, dst_node
);
2303 return arc
->src
!= src
;
2306 static struct ovsdb_idl_table
*
2307 ovsdb_idl_table_from_class(const struct ovsdb_idl
*idl
,
2308 const struct ovsdb_idl_table_class
*table_class
)
2310 return &idl
->tables
[table_class
- idl
->class->tables
];
2313 /* Called by ovsdb-idlc generated code. */
2314 struct ovsdb_idl_row
*
2315 ovsdb_idl_get_row_arc(struct ovsdb_idl_row
*src
,
2316 const struct ovsdb_idl_table_class
*dst_table_class
,
2317 const struct uuid
*dst_uuid
)
2319 struct ovsdb_idl
*idl
= src
->table
->idl
;
2320 struct ovsdb_idl_table
*dst_table
;
2321 struct ovsdb_idl_arc
*arc
;
2322 struct ovsdb_idl_row
*dst
;
2324 dst_table
= ovsdb_idl_table_from_class(idl
, dst_table_class
);
2325 dst
= ovsdb_idl_get_row(dst_table
, dst_uuid
);
2327 /* We're being called from ovsdb_idl_txn_write(). We must not update
2328 * any arcs, because the transaction will be backed out at commit or
2329 * abort time and we don't want our graph screwed up.
2331 * Just return the destination row, if there is one and it has not been
2333 if (dst
&& (hmap_node_is_null(&dst
->txn_node
) || dst
->new)) {
2338 /* We're being called from some other context. Update the graph. */
2340 dst
= ovsdb_idl_row_create(dst_table
, dst_uuid
);
2343 /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */
2344 if (may_add_arc(src
, dst
)) {
2345 /* The arc *must* be added at the front of the dst_arcs list. See
2346 * ovsdb_idl_row_reparse_backrefs() for details. */
2347 arc
= xmalloc(sizeof *arc
);
2348 ovs_list_push_front(&src
->src_arcs
, &arc
->src_node
);
2349 ovs_list_push_front(&dst
->dst_arcs
, &arc
->dst_node
);
2354 return !ovsdb_idl_row_is_orphan(dst
) ? dst
: NULL
;
2358 /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a
2359 * pointer to the row if there is one, otherwise a null pointer. */
2360 const struct ovsdb_idl_row
*
2361 ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl
*idl
,
2362 const struct ovsdb_idl_table_class
*tc
,
2363 const struct uuid
*uuid
)
2365 return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl
, tc
), uuid
);
2368 static struct ovsdb_idl_row
*
2369 next_real_row(struct ovsdb_idl_table
*table
, struct hmap_node
*node
)
2371 for (; node
; node
= hmap_next(&table
->rows
, node
)) {
2372 struct ovsdb_idl_row
*row
;
2374 row
= CONTAINER_OF(node
, struct ovsdb_idl_row
, hmap_node
);
2375 if (ovsdb_idl_row_exists(row
)) {
2382 /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that
2385 * Database tables are internally maintained as hash tables, so adding or
2386 * removing rows while traversing the same table can cause some rows to be
2387 * visited twice or not at apply. */
2388 const struct ovsdb_idl_row
*
2389 ovsdb_idl_first_row(const struct ovsdb_idl
*idl
,
2390 const struct ovsdb_idl_table_class
*table_class
)
2392 struct ovsdb_idl_table
*table
2393 = ovsdb_idl_table_from_class(idl
, table_class
);
2394 return next_real_row(table
, hmap_first(&table
->rows
));
2397 /* Returns a row following 'row' within its table, or a null pointer if 'row'
2398 * is the last row in its table. */
2399 const struct ovsdb_idl_row
*
2400 ovsdb_idl_next_row(const struct ovsdb_idl_row
*row
)
2402 struct ovsdb_idl_table
*table
= row
->table
;
2404 return next_real_row(table
, hmap_next(&table
->rows
, &row
->hmap_node
));
2407 /* Reads and returns the value of 'column' within 'row'. If an ongoing
2408 * transaction has changed 'column''s value, the modified value is returned.
2410 * The caller must not modify or free the returned value.
2412 * Various kinds of changes can invalidate the returned value: writing to the
2413 * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row'
2414 * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction
2415 * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the
2416 * returned value is needed for a long time, it is best to make a copy of it
2417 * with ovsdb_datum_clone(). */
2418 const struct ovsdb_datum
*
2419 ovsdb_idl_read(const struct ovsdb_idl_row
*row
,
2420 const struct ovsdb_idl_column
*column
)
2422 const struct ovsdb_idl_table_class
*class;
2425 ovs_assert(!ovsdb_idl_row_is_synthetic(row
));
2427 class = row
->table
->class;
2428 column_idx
= column
- class->columns
;
2430 ovs_assert(row
->new != NULL
);
2431 ovs_assert(column_idx
< class->n_columns
);
2433 if (row
->written
&& bitmap_is_set(row
->written
, column_idx
)) {
2434 return &row
->new[column_idx
];
2435 } else if (row
->old
) {
2436 return &row
->old
[column_idx
];
2438 return ovsdb_datum_default(&column
->type
);
2442 /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key
2443 * type 'key_type' and value type 'value_type'. (Scalar and set types will
2444 * have a value type of OVSDB_TYPE_VOID.)
2446 * This is useful in code that "knows" that a particular column has a given
2447 * type, so that it will abort if someone changes the column's type without
2448 * updating the code that uses it. */
2449 const struct ovsdb_datum
*
2450 ovsdb_idl_get(const struct ovsdb_idl_row
*row
,
2451 const struct ovsdb_idl_column
*column
,
2452 enum ovsdb_atomic_type key_type OVS_UNUSED
,
2453 enum ovsdb_atomic_type value_type OVS_UNUSED
)
2455 ovs_assert(column
->type
.key
.type
== key_type
);
2456 ovs_assert(column
->type
.value
.type
== value_type
);
2458 return ovsdb_idl_read(row
, column
);
2461 /* Returns true if the field represented by 'column' in 'row' may be modified,
2462 * false if it is immutable.
2464 * Normally, whether a field is mutable is controlled by its column's schema.
2465 * However, an immutable column can be set to any initial value at the time of
2466 * insertion, so if 'row' is a new row (one that is being added as part of the
2467 * current transaction, supposing that a transaction is in progress) then even
2468 * its "immutable" fields are actually mutable. */
2470 ovsdb_idl_is_mutable(const struct ovsdb_idl_row
*row
,
2471 const struct ovsdb_idl_column
*column
)
2473 return column
->mutable || (row
->new && !row
->old
);
2476 /* Returns false if 'row' was obtained from the IDL, true if it was initialized
2477 * to all-zero-bits by some other entity. If 'row' was set up some other way
2478 * then the return value is indeterminate. */
2480 ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row
*row
)
2482 return row
->table
== NULL
;
2487 static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn
*txn
,
2488 enum ovsdb_idl_txn_status
);
2490 /* Returns a string representation of 'status'. The caller must not modify or
2491 * free the returned string.
2493 * The return value is probably useful only for debug log messages and unit
2496 ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status
)
2499 case TXN_UNCOMMITTED
:
2500 return "uncommitted";
2503 case TXN_INCOMPLETE
:
2504 return "incomplete";
2511 case TXN_NOT_LOCKED
:
2512 return "not locked";
2519 /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single
2520 * active transaction at a time. See the large comment in ovsdb-idl.h for
2521 * general information on transactions. */
2522 struct ovsdb_idl_txn
*
2523 ovsdb_idl_txn_create(struct ovsdb_idl
*idl
)
2525 struct ovsdb_idl_txn
*txn
;
2527 ovs_assert(!idl
->txn
);
2528 idl
->txn
= txn
= xmalloc(sizeof *txn
);
2529 txn
->request_id
= NULL
;
2531 hmap_init(&txn
->txn_rows
);
2532 txn
->status
= TXN_UNCOMMITTED
;
2534 txn
->dry_run
= false;
2535 ds_init(&txn
->comment
);
2537 txn
->inc_table
= NULL
;
2538 txn
->inc_column
= NULL
;
2540 hmap_init(&txn
->inserted_rows
);
2545 /* Appends 's', which is treated as a printf()-type format string, to the
2546 * comments that will be passed to the OVSDB server when 'txn' is committed.
2547 * (The comment will be committed to the OVSDB log, which "ovsdb-tool
2548 * show-log" can print in a relatively human-readable form.) */
2550 ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn
*txn
, const char *s
, ...)
2554 if (txn
->comment
.length
) {
2555 ds_put_char(&txn
->comment
, '\n');
2559 ds_put_format_valist(&txn
->comment
, s
, args
);
2563 /* Marks 'txn' as a transaction that will not actually modify the database. In
2564 * almost every way, the transaction is treated like other transactions. It
2565 * must be committed or aborted like other transactions, it will be sent to the
2566 * database server like other transactions, and so on. The only difference is
2567 * that the operations sent to the database server will include, as the last
2568 * step, an "abort" operation, so that any changes made by the transaction will
2569 * not actually take effect. */
2571 ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn
*txn
)
2573 txn
->dry_run
= true;
2576 /* Causes 'txn', when committed, to increment the value of 'column' within
2577 * 'row' by 1. 'column' must have an integer type. After 'txn' commits
2578 * successfully, the client may retrieve the final (incremented) value of
2579 * 'column' with ovsdb_idl_txn_get_increment_new_value().
2581 * If at time of commit the transaction is otherwise empty, that is, it doesn't
2582 * change the database, then 'force' is important. If 'force' is false in this
2583 * case, the IDL suppresses the increment and skips a round trip to the
2584 * database server. If 'force' is true, the IDL will still increment the
2587 * The client could accomplish something similar with ovsdb_idl_read(),
2588 * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc
2589 * generated wrappers for these functions. However, ovsdb_idl_txn_increment()
2590 * will never (by itself) fail because of a verify error.
2592 * The intended use is for incrementing the "next_cfg" column in the
2593 * Open_vSwitch table. */
2595 ovsdb_idl_txn_increment(struct ovsdb_idl_txn
*txn
,
2596 const struct ovsdb_idl_row
*row
,
2597 const struct ovsdb_idl_column
*column
,
2600 ovs_assert(!txn
->inc_table
);
2601 ovs_assert(column
->type
.key
.type
== OVSDB_TYPE_INTEGER
);
2602 ovs_assert(column
->type
.value
.type
== OVSDB_TYPE_VOID
);
2604 txn
->inc_table
= row
->table
->class->name
;
2605 txn
->inc_column
= column
->name
;
2606 txn
->inc_row
= row
->uuid
;
2607 txn
->inc_force
= force
;
2610 /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit()
2611 * has been called for 'txn' but the commit is still incomplete (that is, the
2612 * last call returned TXN_INCOMPLETE) then the transaction may or may not still
2613 * end up committing at the database server, but the client will not be able to
2614 * get any further status information back. */
2616 ovsdb_idl_txn_destroy(struct ovsdb_idl_txn
*txn
)
2618 struct ovsdb_idl_txn_insert
*insert
, *next
;
2620 json_destroy(txn
->request_id
);
2621 if (txn
->status
== TXN_INCOMPLETE
) {
2622 hmap_remove(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
);
2624 ovsdb_idl_txn_abort(txn
);
2625 ds_destroy(&txn
->comment
);
2627 HMAP_FOR_EACH_SAFE (insert
, next
, hmap_node
, &txn
->inserted_rows
) {
2630 hmap_destroy(&txn
->inserted_rows
);
2634 /* Causes poll_block() to wake up if 'txn' has completed committing. */
2636 ovsdb_idl_txn_wait(const struct ovsdb_idl_txn
*txn
)
2638 if (txn
->status
!= TXN_UNCOMMITTED
&& txn
->status
!= TXN_INCOMPLETE
) {
2639 poll_immediate_wake();
2643 static struct json
*
2644 where_uuid_equals(const struct uuid
*uuid
)
2647 json_array_create_1(
2648 json_array_create_3(
2649 json_string_create("_uuid"),
2650 json_string_create("=="),
2651 json_array_create_2(
2652 json_string_create("uuid"),
2653 json_string_create_nocopy(
2654 xasprintf(UUID_FMT
, UUID_ARGS(uuid
))))));
2658 uuid_name_from_uuid(const struct uuid
*uuid
)
2663 name
= xasprintf("row"UUID_FMT
, UUID_ARGS(uuid
));
2664 for (p
= name
; *p
!= '\0'; p
++) {
2673 static const struct ovsdb_idl_row
*
2674 ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn
*txn
, const struct uuid
*uuid
)
2676 const struct ovsdb_idl_row
*row
;
2678 HMAP_FOR_EACH_WITH_HASH (row
, txn_node
, uuid_hash(uuid
), &txn
->txn_rows
) {
2679 if (uuid_equals(&row
->uuid
, uuid
)) {
2686 /* XXX there must be a cleaner way to do this */
2687 static struct json
*
2688 substitute_uuids(struct json
*json
, const struct ovsdb_idl_txn
*txn
)
2690 if (json
->type
== JSON_ARRAY
) {
2694 if (json
->u
.array
.n
== 2
2695 && json
->u
.array
.elems
[0]->type
== JSON_STRING
2696 && json
->u
.array
.elems
[1]->type
== JSON_STRING
2697 && !strcmp(json
->u
.array
.elems
[0]->u
.string
, "uuid")
2698 && uuid_from_string(&uuid
, json
->u
.array
.elems
[1]->u
.string
)) {
2699 const struct ovsdb_idl_row
*row
;
2701 row
= ovsdb_idl_txn_get_row(txn
, &uuid
);
2702 if (row
&& !row
->old
&& row
->new) {
2705 return json_array_create_2(
2706 json_string_create("named-uuid"),
2707 json_string_create_nocopy(uuid_name_from_uuid(&uuid
)));
2711 for (i
= 0; i
< json
->u
.array
.n
; i
++) {
2712 json
->u
.array
.elems
[i
] = substitute_uuids(json
->u
.array
.elems
[i
],
2715 } else if (json
->type
== JSON_OBJECT
) {
2716 struct shash_node
*node
;
2718 SHASH_FOR_EACH (node
, json_object(json
)) {
2719 node
->data
= substitute_uuids(node
->data
, txn
);
2726 ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn
*txn
)
2728 struct ovsdb_idl_row
*row
, *next
;
2730 /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an
2731 * ovsdb_idl_column's 'parse' function, which will call
2732 * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a
2733 * transaction and fail to update the graph. */
2734 txn
->idl
->txn
= NULL
;
2736 HMAP_FOR_EACH_SAFE (row
, next
, txn_node
, &txn
->txn_rows
) {
2737 ovsdb_idl_destroy_all_map_op_lists(row
);
2738 ovsdb_idl_destroy_all_set_op_lists(row
);
2741 ovsdb_idl_row_unparse(row
);
2742 ovsdb_idl_row_clear_arcs(row
, false);
2743 ovsdb_idl_row_parse(row
);
2746 ovsdb_idl_row_unparse(row
);
2748 ovsdb_idl_row_clear_new(row
);
2751 row
->prereqs
= NULL
;
2754 row
->written
= NULL
;
2756 hmap_remove(&txn
->txn_rows
, &row
->txn_node
);
2757 hmap_node_nullify(&row
->txn_node
);
2759 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
2763 hmap_destroy(&txn
->txn_rows
);
2764 hmap_init(&txn
->txn_rows
);
2768 ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row
*row
,
2769 struct json
*mutations
)
2771 const struct ovsdb_idl_table_class
*class = row
->table
->class;
2773 bool any_mutations
= false;
2775 if (row
->map_op_written
) {
2776 BITMAP_FOR_EACH_1(idx
, class->n_columns
, row
->map_op_written
) {
2777 struct map_op_list
*map_op_list
;
2778 const struct ovsdb_idl_column
*column
;
2779 const struct ovsdb_datum
*old_datum
;
2780 enum ovsdb_atomic_type key_type
, value_type
;
2781 struct json
*mutation
, *map
, *col_name
, *mutator
;
2782 struct json
*del_set
, *ins_map
;
2783 bool any_del
, any_ins
;
2785 map_op_list
= row
->map_op_lists
[idx
];
2786 column
= &class->columns
[idx
];
2787 key_type
= column
->type
.key
.type
;
2788 value_type
= column
->type
.value
.type
;
2790 /* Get the value to be changed */
2791 if (row
->new && row
->written
&& bitmap_is_set(row
->written
,idx
)) {
2792 old_datum
= &row
->new[idx
];
2793 } else if (row
->old
!= NULL
) {
2794 old_datum
= &row
->old
[idx
];
2796 old_datum
= ovsdb_datum_default(&column
->type
);
2799 del_set
= json_array_create_empty();
2800 ins_map
= json_array_create_empty();
2804 for (struct map_op
*map_op
= map_op_list_first(map_op_list
); map_op
;
2805 map_op
= map_op_list_next(map_op_list
, map_op
)) {
2807 if (map_op_type(map_op
) == MAP_OP_UPDATE
) {
2808 /* Find out if value really changed. */
2809 struct ovsdb_datum
*new_datum
;
2811 new_datum
= map_op_datum(map_op
);
2812 pos
= ovsdb_datum_find_key(old_datum
,
2813 &new_datum
->keys
[0],
2815 if (ovsdb_atom_equals(&new_datum
->values
[0],
2816 &old_datum
->values
[pos
],
2818 /* No change in value. Move on to next update. */
2821 } else if (map_op_type(map_op
) == MAP_OP_DELETE
){
2822 /* Verify that there is a key to delete. */
2824 pos
= ovsdb_datum_find_key(old_datum
,
2825 &map_op_datum(map_op
)->keys
[0],
2827 if (pos
== UINT_MAX
) {
2828 /* No key to delete. Move on to next update. */
2829 VLOG_WARN("Trying to delete a key that doesn't "
2830 "exist in the map.");
2835 if (map_op_type(map_op
) == MAP_OP_INSERT
) {
2836 map
= json_array_create_2(
2837 ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2839 ovsdb_atom_to_json(&map_op_datum(map_op
)->values
[0],
2841 json_array_add(ins_map
, map
);
2843 } else { /* MAP_OP_UPDATE or MAP_OP_DELETE */
2844 map
= ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2846 json_array_add(del_set
, map
);
2850 /* Generate an additional insert mutate for updates. */
2851 if (map_op_type(map_op
) == MAP_OP_UPDATE
) {
2852 map
= json_array_create_2(
2853 ovsdb_atom_to_json(&map_op_datum(map_op
)->keys
[0],
2855 ovsdb_atom_to_json(&map_op_datum(map_op
)->values
[0],
2857 json_array_add(ins_map
, map
);
2863 col_name
= json_string_create(column
->name
);
2864 mutator
= json_string_create("delete");
2865 map
= json_array_create_2(json_string_create("set"), del_set
);
2866 mutation
= json_array_create_3(col_name
, mutator
, map
);
2867 json_array_add(mutations
, mutation
);
2868 any_mutations
= true;
2870 json_destroy(del_set
);
2873 col_name
= json_string_create(column
->name
);
2874 mutator
= json_string_create("insert");
2875 map
= json_array_create_2(json_string_create("map"), ins_map
);
2876 mutation
= json_array_create_3(col_name
, mutator
, map
);
2877 json_array_add(mutations
, mutation
);
2878 any_mutations
= true;
2880 json_destroy(ins_map
);
2884 if (row
->set_op_written
) {
2885 BITMAP_FOR_EACH_1(idx
, class->n_columns
, row
->set_op_written
) {
2886 struct set_op_list
*set_op_list
;
2887 const struct ovsdb_idl_column
*column
;
2888 const struct ovsdb_datum
*old_datum
;
2889 enum ovsdb_atomic_type key_type
;
2890 struct json
*mutation
, *set
, *col_name
, *mutator
;
2891 struct json
*del_set
, *ins_set
;
2892 bool any_del
, any_ins
;
2894 set_op_list
= row
->set_op_lists
[idx
];
2895 column
= &class->columns
[idx
];
2896 key_type
= column
->type
.key
.type
;
2898 /* Get the value to be changed */
2899 if (row
->new && row
->written
&& bitmap_is_set(row
->written
,idx
)) {
2900 old_datum
= &row
->new[idx
];
2901 } else if (row
->old
!= NULL
) {
2902 old_datum
= &row
->old
[idx
];
2904 old_datum
= ovsdb_datum_default(&column
->type
);
2907 del_set
= json_array_create_empty();
2908 ins_set
= json_array_create_empty();
2912 for (struct set_op
*set_op
= set_op_list_first(set_op_list
); set_op
;
2913 set_op
= set_op_list_next(set_op_list
, set_op
)) {
2914 if (set_op_type(set_op
) == SET_OP_INSERT
) {
2915 set
= ovsdb_atom_to_json(&set_op_datum(set_op
)->keys
[0],
2917 json_array_add(ins_set
, set
);
2919 } else { /* SETP_OP_DELETE */
2920 /* Verify that there is a key to delete. */
2922 pos
= ovsdb_datum_find_key(old_datum
,
2923 &set_op_datum(set_op
)->keys
[0],
2925 if (pos
== UINT_MAX
) {
2926 /* No key to delete. Move on to next update. */
2927 VLOG_WARN("Trying to delete a key that doesn't "
2928 "exist in the set.");
2931 set
= ovsdb_atom_to_json(&set_op_datum(set_op
)->keys
[0],
2933 json_array_add(del_set
, set
);
2938 col_name
= json_string_create(column
->name
);
2939 mutator
= json_string_create("delete");
2940 set
= json_array_create_2(json_string_create("set"), del_set
);
2941 mutation
= json_array_create_3(col_name
, mutator
, set
);
2942 json_array_add(mutations
, mutation
);
2943 any_mutations
= true;
2945 json_destroy(del_set
);
2948 col_name
= json_string_create(column
->name
);
2949 mutator
= json_string_create("insert");
2950 set
= json_array_create_2(json_string_create("set"), ins_set
);
2951 mutation
= json_array_create_3(col_name
, mutator
, set
);
2952 json_array_add(mutations
, mutation
);
2953 any_mutations
= true;
2955 json_destroy(ins_set
);
2959 return any_mutations
;
2962 /* Attempts to commit 'txn'. Returns the status of the commit operation, one
2963 * of the following TXN_* constants:
2967 * The transaction is in progress, but not yet complete. The caller
2968 * should call again later, after calling ovsdb_idl_run() to let the IDL
2969 * do OVSDB protocol processing.
2973 * The transaction is complete. (It didn't actually change the database,
2974 * so the IDL didn't send any request to the database server.)
2978 * The caller previously called ovsdb_idl_txn_abort().
2982 * The transaction was successful. The update made by the transaction
2983 * (and possibly other changes made by other database clients) should
2984 * already be visible in the IDL.
2988 * The transaction failed for some transient reason, e.g. because a
2989 * "verify" operation reported an inconsistency or due to a network
2990 * problem. The caller should wait for a change to the database, then
2991 * compose a new transaction, and commit the new transaction.
2993 * Use the return value of ovsdb_idl_get_seqno() to wait for a change in
2994 * the database. It is important to use its return value *before* the
2995 * initial call to ovsdb_idl_txn_commit() as the baseline for this
2996 * purpose, because the change that one should wait for can happen after
2997 * the initial call but before the call that returns TXN_TRY_AGAIN, and
2998 * using some other baseline value in that situation could cause an
2999 * indefinite wait if the database rarely changes.
3003 * The transaction failed because the IDL has been configured to require
3004 * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or
3005 * has already lost it.
3007 * Committing a transaction rolls back all of the changes that it made to the
3008 * IDL's copy of the database. If the transaction commits successfully, then
3009 * the database server will send an update and, thus, the IDL will be updated
3010 * with the committed changes. */
3011 enum ovsdb_idl_txn_status
3012 ovsdb_idl_txn_commit(struct ovsdb_idl_txn
*txn
)
3014 struct ovsdb_idl_row
*row
;
3015 struct json
*operations
;
3018 if (txn
!= txn
->idl
->txn
) {
3022 /* If we need a lock but don't have it, give up quickly. */
3023 if (txn
->idl
->lock_name
&& !ovsdb_idl_has_lock(txn
->idl
)) {
3024 txn
->status
= TXN_NOT_LOCKED
;
3025 goto disassemble_out
;
3028 operations
= json_array_create_1(
3029 json_string_create(txn
->idl
->class->database
));
3031 /* Assert that we have the required lock (avoiding a race). */
3032 if (txn
->idl
->lock_name
) {
3033 struct json
*op
= json_object_create();
3034 json_array_add(operations
, op
);
3035 json_object_put_string(op
, "op", "assert");
3036 json_object_put_string(op
, "lock", txn
->idl
->lock_name
);
3039 /* Add prerequisites and declarations of new rows. */
3040 HMAP_FOR_EACH (row
, txn_node
, &txn
->txn_rows
) {
3041 /* XXX check that deleted rows exist even if no prereqs? */
3043 const struct ovsdb_idl_table_class
*class = row
->table
->class;
3044 size_t n_columns
= class->n_columns
;
3045 struct json
*op
, *columns
, *row_json
;
3048 op
= json_object_create();
3049 json_array_add(operations
, op
);
3050 json_object_put_string(op
, "op", "wait");
3051 json_object_put_string(op
, "table", class->name
);
3052 json_object_put(op
, "timeout", json_integer_create(0));
3053 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3054 json_object_put_string(op
, "until", "==");
3055 columns
= json_array_create_empty();
3056 json_object_put(op
, "columns", columns
);
3057 row_json
= json_object_create();
3058 json_object_put(op
, "rows", json_array_create_1(row_json
));
3060 BITMAP_FOR_EACH_1 (idx
, n_columns
, row
->prereqs
) {
3061 const struct ovsdb_idl_column
*column
= &class->columns
[idx
];
3062 json_array_add(columns
, json_string_create(column
->name
));
3063 json_object_put(row_json
, column
->name
,
3064 ovsdb_datum_to_json(&row
->old
[idx
],
3071 any_updates
= false;
3072 HMAP_FOR_EACH (row
, txn_node
, &txn
->txn_rows
) {
3073 const struct ovsdb_idl_table_class
*class = row
->table
->class;
3076 if (class->is_root
) {
3077 struct json
*op
= json_object_create();
3078 json_object_put_string(op
, "op", "delete");
3079 json_object_put_string(op
, "table", class->name
);
3080 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3081 json_array_add(operations
, op
);
3084 /* Let ovsdb-server decide whether to really delete it. */
3086 } else if (row
->old
!= row
->new) {
3087 struct json
*row_json
;
3091 op
= json_object_create();
3092 json_object_put_string(op
, "op", row
->old
? "update" : "insert");
3093 json_object_put_string(op
, "table", class->name
);
3095 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3097 struct ovsdb_idl_txn_insert
*insert
;
3101 json_object_put(op
, "uuid-name",
3102 json_string_create_nocopy(
3103 uuid_name_from_uuid(&row
->uuid
)));
3105 insert
= xmalloc(sizeof *insert
);
3106 insert
->dummy
= row
->uuid
;
3107 insert
->op_index
= operations
->u
.array
.n
- 1;
3108 uuid_zero(&insert
->real
);
3109 hmap_insert(&txn
->inserted_rows
, &insert
->hmap_node
,
3110 uuid_hash(&insert
->dummy
));
3112 row_json
= json_object_create();
3113 json_object_put(op
, "row", row_json
);
3116 BITMAP_FOR_EACH_1 (idx
, class->n_columns
, row
->written
) {
3117 const struct ovsdb_idl_column
*column
=
3118 &class->columns
[idx
];
3121 || !ovsdb_datum_is_default(&row
->new[idx
],
3123 json_object_put(row_json
, column
->name
,
3125 ovsdb_datum_to_json(&row
->new[idx
],
3129 /* If anything really changed, consider it an update.
3130 * We can't suppress not-really-changed values earlier
3131 * or transactions would become nonatomic (see the big
3132 * comment inside ovsdb_idl_txn_write()). */
3133 if (!any_updates
&& row
->old
&&
3134 !ovsdb_datum_equals(&row
->old
[idx
], &row
->new[idx
],
3142 if (!row
->old
|| !shash_is_empty(json_object(row_json
))) {
3143 json_array_add(operations
, op
);
3149 /* Add mutate operation, for partial map or partial set updates. */
3150 if (row
->map_op_written
|| row
->set_op_written
) {
3151 struct json
*op
, *mutations
;
3154 op
= json_object_create();
3155 json_object_put_string(op
, "op", "mutate");
3156 json_object_put_string(op
, "table", class->name
);
3157 json_object_put(op
, "where", where_uuid_equals(&row
->uuid
));
3158 mutations
= json_array_create_empty();
3159 any_mutations
= ovsdb_idl_txn_extract_mutations(row
, mutations
);
3160 json_object_put(op
, "mutations", mutations
);
3162 if (any_mutations
) {
3163 op
= substitute_uuids(op
, txn
);
3164 json_array_add(operations
, op
);
3172 /* Add increment. */
3173 if (txn
->inc_table
&& (any_updates
|| txn
->inc_force
)) {
3175 txn
->inc_index
= operations
->u
.array
.n
- 1;
3177 struct json
*op
= json_object_create();
3178 json_object_put_string(op
, "op", "mutate");
3179 json_object_put_string(op
, "table", txn
->inc_table
);
3180 json_object_put(op
, "where",
3181 substitute_uuids(where_uuid_equals(&txn
->inc_row
),
3183 json_object_put(op
, "mutations",
3184 json_array_create_1(
3185 json_array_create_3(
3186 json_string_create(txn
->inc_column
),
3187 json_string_create("+="),
3188 json_integer_create(1))));
3189 json_array_add(operations
, op
);
3191 op
= json_object_create();
3192 json_object_put_string(op
, "op", "select");
3193 json_object_put_string(op
, "table", txn
->inc_table
);
3194 json_object_put(op
, "where",
3195 substitute_uuids(where_uuid_equals(&txn
->inc_row
),
3197 json_object_put(op
, "columns",
3198 json_array_create_1(json_string_create(
3200 json_array_add(operations
, op
);
3203 if (txn
->comment
.length
) {
3204 struct json
*op
= json_object_create();
3205 json_object_put_string(op
, "op", "comment");
3206 json_object_put_string(op
, "comment", ds_cstr(&txn
->comment
));
3207 json_array_add(operations
, op
);
3211 struct json
*op
= json_object_create();
3212 json_object_put_string(op
, "op", "abort");
3213 json_array_add(operations
, op
);
3217 txn
->status
= TXN_UNCHANGED
;
3218 json_destroy(operations
);
3219 } else if (!jsonrpc_session_send(
3221 jsonrpc_create_request(
3222 "transact", operations
, &txn
->request_id
))) {
3223 hmap_insert(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
,
3224 json_hash(txn
->request_id
, 0));
3225 txn
->status
= TXN_INCOMPLETE
;
3227 txn
->status
= TXN_TRY_AGAIN
;
3231 ovsdb_idl_txn_disassemble(txn
);
3233 switch (txn
->status
) {
3234 case TXN_UNCOMMITTED
: COVERAGE_INC(txn_uncommitted
); break;
3235 case TXN_UNCHANGED
: COVERAGE_INC(txn_unchanged
); break;
3236 case TXN_INCOMPLETE
: COVERAGE_INC(txn_incomplete
); break;
3237 case TXN_ABORTED
: COVERAGE_INC(txn_aborted
); break;
3238 case TXN_SUCCESS
: COVERAGE_INC(txn_success
); break;
3239 case TXN_TRY_AGAIN
: COVERAGE_INC(txn_try_again
); break;
3240 case TXN_NOT_LOCKED
: COVERAGE_INC(txn_not_locked
); break;
3241 case TXN_ERROR
: COVERAGE_INC(txn_error
); break;
3247 /* Attempts to commit 'txn', blocking until the commit either succeeds or
3248 * fails. Returns the final commit status, which may be any TXN_* value other
3249 * than TXN_INCOMPLETE.
3251 * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the
3252 * return value of ovsdb_idl_get_seqno() to change. */
3253 enum ovsdb_idl_txn_status
3254 ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn
*txn
)
3256 enum ovsdb_idl_txn_status status
;
3259 while ((status
= ovsdb_idl_txn_commit(txn
)) == TXN_INCOMPLETE
) {
3260 ovsdb_idl_run(txn
->idl
);
3261 ovsdb_idl_wait(txn
->idl
);
3262 ovsdb_idl_txn_wait(txn
);
3268 /* Returns the final (incremented) value of the column in 'txn' that was set to
3269 * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed
3272 ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn
*txn
)
3274 ovs_assert(txn
->status
== TXN_SUCCESS
);
3275 return txn
->inc_new_value
;
3278 /* Aborts 'txn' without sending it to the database server. This is effective
3279 * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'.
3280 * Otherwise, it has no effect.
3282 * Aborting a transaction doesn't free its memory. Use
3283 * ovsdb_idl_txn_destroy() to do that. */
3285 ovsdb_idl_txn_abort(struct ovsdb_idl_txn
*txn
)
3287 ovsdb_idl_txn_disassemble(txn
);
3288 if (txn
->status
== TXN_UNCOMMITTED
|| txn
->status
== TXN_INCOMPLETE
) {
3289 txn
->status
= TXN_ABORTED
;
3293 /* Returns a string that reports the error status for 'txn'. The caller must
3294 * not modify or free the returned string. A call to ovsdb_idl_txn_destroy()
3295 * for 'txn' may free the returned string.
3297 * The return value is ordinarily one of the strings that
3298 * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed
3299 * due to an error reported by the database server, the return value is that
3302 ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn
*txn
)
3304 if (txn
->status
!= TXN_ERROR
) {
3305 return ovsdb_idl_txn_status_to_string(txn
->status
);
3306 } else if (txn
->error
) {
3309 return "no error details available";
3314 ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn
*txn
,
3315 const struct json
*json
)
3317 if (txn
->error
== NULL
) {
3318 txn
->error
= json_to_string(json
, JSSF_SORT
);
3322 /* For transaction 'txn' that completed successfully, finds and returns the
3323 * permanent UUID that the database assigned to a newly inserted row, given the
3324 * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row.
3326 * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or
3327 * if it was assigned by that function and then deleted by
3328 * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted
3329 * and then deleted within a single transaction are never sent to the database
3330 * server, so it never assigns them a permanent UUID.) */
3332 ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn
*txn
,
3333 const struct uuid
*uuid
)
3335 const struct ovsdb_idl_txn_insert
*insert
;
3337 ovs_assert(txn
->status
== TXN_SUCCESS
|| txn
->status
== TXN_UNCHANGED
);
3338 HMAP_FOR_EACH_IN_BUCKET (insert
, hmap_node
,
3339 uuid_hash(uuid
), &txn
->inserted_rows
) {
3340 if (uuid_equals(uuid
, &insert
->dummy
)) {
3341 return &insert
->real
;
3348 ovsdb_idl_txn_complete(struct ovsdb_idl_txn
*txn
,
3349 enum ovsdb_idl_txn_status status
)
3351 txn
->status
= status
;
3352 hmap_remove(&txn
->idl
->outstanding_txns
, &txn
->hmap_node
);
3356 ovsdb_idl_txn_write__(const struct ovsdb_idl_row
*row_
,
3357 const struct ovsdb_idl_column
*column
,
3358 struct ovsdb_datum
*datum
, bool owns_datum
)
3360 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3361 const struct ovsdb_idl_table_class
*class;
3365 if (ovsdb_idl_row_is_synthetic(row
)) {
3369 class = row
->table
->class;
3370 column_idx
= column
- class->columns
;
3371 write_only
= row
->table
->modes
[column_idx
] == OVSDB_IDL_MONITOR
;
3373 ovs_assert(row
->new != NULL
);
3374 ovs_assert(column_idx
< class->n_columns
);
3375 ovs_assert(row
->old
== NULL
||
3376 row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
);
3378 if (row
->table
->idl
->verify_write_only
&& !write_only
) {
3379 VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
3380 " explicitly configured not to.", class->name
, column
->name
);
3384 /* If this is a write-only column and the datum being written is the same
3385 * as the one already there, just skip the update entirely. This is worth
3386 * optimizing because we have a lot of columns that get periodically
3387 * refreshed into the database but don't actually change that often.
3389 * We don't do this for read/write columns because that would break
3390 * atomicity of transactions--some other client might have written a
3391 * different value in that column since we read it. (But if a whole
3392 * transaction only does writes of existing values, without making any real
3393 * changes, we will drop the whole transaction later in
3394 * ovsdb_idl_txn_commit().) */
3395 if (write_only
&& ovsdb_datum_equals(ovsdb_idl_read(row
, column
),
3396 datum
, &column
->type
)) {
3400 if (hmap_node_is_null(&row
->txn_node
)) {
3401 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3402 uuid_hash(&row
->uuid
));
3404 if (row
->old
== row
->new) {
3405 row
->new = xmalloc(class->n_columns
* sizeof *row
->new);
3407 if (!row
->written
) {
3408 row
->written
= bitmap_allocate(class->n_columns
);
3410 if (bitmap_is_set(row
->written
, column_idx
)) {
3411 ovsdb_datum_destroy(&row
->new[column_idx
], &column
->type
);
3413 bitmap_set1(row
->written
, column_idx
);
3416 row
->new[column_idx
] = *datum
;
3418 ovsdb_datum_clone(&row
->new[column_idx
], datum
, &column
->type
);
3420 (column
->unparse
)(row
);
3421 (column
->parse
)(row
, &row
->new[column_idx
]);
3426 ovsdb_datum_destroy(datum
, &column
->type
);
3430 /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_'
3431 * itself and the structs derived from it (e.g. the "struct ovsrec_*", for
3434 * 'datum' must have the correct type for its column, but it needs not be
3435 * sorted or unique because this function will take care of that. The IDL does
3436 * not check that it meets schema constraints, but ovsdb-server will do so at
3437 * commit time so it had better be correct.
3439 * A transaction must be in progress. Replication of 'column' must not have
3440 * been disabled (by calling ovsdb_idl_omit()).
3442 * Usually this function is used indirectly through one of the "set" functions
3443 * generated by ovsdb-idlc.
3445 * Takes ownership of what 'datum' points to (and in some cases destroys that
3446 * data before returning) but makes a copy of 'datum' itself. (Commonly
3447 * 'datum' is on the caller's stack.) */
3449 ovsdb_idl_txn_write(const struct ovsdb_idl_row
*row
,
3450 const struct ovsdb_idl_column
*column
,
3451 struct ovsdb_datum
*datum
)
3453 ovsdb_datum_sort_unique(datum
,
3454 column
->type
.key
.type
, column
->type
.value
.type
);
3455 ovsdb_idl_txn_write__(row
, column
, datum
, true);
3458 /* Similar to ovsdb_idl_txn_write(), except:
3460 * - The caller retains ownership of 'datum' and what it points to.
3462 * - The caller must ensure that 'datum' is sorted and unique (e.g. via
3463 * ovsdb_datum_sort_unique().) */
3465 ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row
*row
,
3466 const struct ovsdb_idl_column
*column
,
3467 const struct ovsdb_datum
*datum
)
3469 ovsdb_idl_txn_write__(row
, column
,
3470 CONST_CAST(struct ovsdb_datum
*, datum
), false);
3473 /* Causes the original contents of 'column' in 'row_' to be verified as a
3474 * prerequisite to completing the transaction. That is, if 'column' in 'row_'
3475 * changed (or if 'row_' was deleted) between the time that the IDL originally
3476 * read its contents and the time that the transaction commits, then the
3477 * transaction aborts and ovsdb_idl_txn_commit() returns TXN_TRY_AGAIN.
3479 * The intention is that, to ensure that no transaction commits based on dirty
3480 * reads, an application should call ovsdb_idl_txn_verify() on each data item
3481 * read as part of a read-modify-write operation.
3483 * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current
3484 * value of 'column' is already known:
3486 * - If 'row_' is a row created by the current transaction (returned by
3487 * ovsdb_idl_txn_insert()).
3489 * - If 'column' has already been modified (with ovsdb_idl_txn_write())
3490 * within the current transaction.
3492 * Because of the latter property, always call ovsdb_idl_txn_verify() *before*
3493 * ovsdb_idl_txn_write() for a given read-modify-write.
3495 * A transaction must be in progress.
3497 * Usually this function is used indirectly through one of the "verify"
3498 * functions generated by ovsdb-idlc. */
3500 ovsdb_idl_txn_verify(const struct ovsdb_idl_row
*row_
,
3501 const struct ovsdb_idl_column
*column
)
3503 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3504 const struct ovsdb_idl_table_class
*class;
3507 if (ovsdb_idl_row_is_synthetic(row
)) {
3511 class = row
->table
->class;
3512 column_idx
= column
- class->columns
;
3514 ovs_assert(row
->new != NULL
);
3515 ovs_assert(row
->old
== NULL
||
3516 row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
);
3518 || (row
->written
&& bitmap_is_set(row
->written
, column_idx
))) {
3522 if (hmap_node_is_null(&row
->txn_node
)) {
3523 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3524 uuid_hash(&row
->uuid
));
3526 if (!row
->prereqs
) {
3527 row
->prereqs
= bitmap_allocate(class->n_columns
);
3529 bitmap_set1(row
->prereqs
, column_idx
);
3532 /* Deletes 'row_' from its table. May free 'row_', so it must not be
3533 * accessed afterward.
3535 * A transaction must be in progress.
3537 * Usually this function is used indirectly through one of the "delete"
3538 * functions generated by ovsdb-idlc. */
3540 ovsdb_idl_txn_delete(const struct ovsdb_idl_row
*row_
)
3542 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
3544 if (ovsdb_idl_row_is_synthetic(row
)) {
3548 ovs_assert(row
->new != NULL
);
3550 ovsdb_idl_row_unparse(row
);
3551 ovsdb_idl_row_clear_new(row
);
3552 ovs_assert(!row
->prereqs
);
3553 hmap_remove(&row
->table
->rows
, &row
->hmap_node
);
3554 hmap_remove(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
);
3558 if (hmap_node_is_null(&row
->txn_node
)) {
3559 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
3560 uuid_hash(&row
->uuid
));
3562 ovsdb_idl_row_clear_new(row
);
3566 /* Inserts and returns a new row in the table with the specified 'class' in the
3567 * database with open transaction 'txn'.
3569 * The new row is assigned a provisional UUID. If 'uuid' is null then one is
3570 * randomly generated; otherwise 'uuid' should specify a randomly generated
3571 * UUID not otherwise in use. ovsdb-server will assign a different UUID when
3572 * 'txn' is committed, but the IDL will replace any uses of the provisional
3573 * UUID in the data to be to be committed by the UUID assigned by
3576 * Usually this function is used indirectly through one of the "insert"
3577 * functions generated by ovsdb-idlc. */
3578 const struct ovsdb_idl_row
*
3579 ovsdb_idl_txn_insert(struct ovsdb_idl_txn
*txn
,
3580 const struct ovsdb_idl_table_class
*class,
3581 const struct uuid
*uuid
)
3583 struct ovsdb_idl_row
*row
= ovsdb_idl_row_create__(class);
3586 ovs_assert(!ovsdb_idl_txn_get_row(txn
, uuid
));
3589 uuid_generate(&row
->uuid
);
3592 row
->table
= ovsdb_idl_table_from_class(txn
->idl
, class);
3593 row
->new = xmalloc(class->n_columns
* sizeof *row
->new);
3594 hmap_insert(&row
->table
->rows
, &row
->hmap_node
, uuid_hash(&row
->uuid
));
3595 hmap_insert(&txn
->txn_rows
, &row
->txn_node
, uuid_hash(&row
->uuid
));
3600 ovsdb_idl_txn_abort_all(struct ovsdb_idl
*idl
)
3602 struct ovsdb_idl_txn
*txn
;
3604 HMAP_FOR_EACH (txn
, hmap_node
, &idl
->outstanding_txns
) {
3605 ovsdb_idl_txn_complete(txn
, TXN_TRY_AGAIN
);
3609 static struct ovsdb_idl_txn
*
3610 ovsdb_idl_txn_find(struct ovsdb_idl
*idl
, const struct json
*id
)
3612 struct ovsdb_idl_txn
*txn
;
3614 HMAP_FOR_EACH_WITH_HASH (txn
, hmap_node
,
3615 json_hash(id
, 0), &idl
->outstanding_txns
) {
3616 if (json_equal(id
, txn
->request_id
)) {
3624 check_json_type(const struct json
*json
, enum json_type type
, const char *name
)
3627 VLOG_WARN_RL(&syntax_rl
, "%s is missing", name
);
3629 } else if (json
->type
!= type
) {
3630 VLOG_WARN_RL(&syntax_rl
, "%s is %s instead of %s",
3631 name
, json_type_to_string(json
->type
),
3632 json_type_to_string(type
));
3640 ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn
*txn
,
3641 const struct json_array
*results
)
3643 struct json
*count
, *rows
, *row
, *column
;
3644 struct shash
*mutate
, *select
;
3646 if (txn
->inc_index
+ 2 > results
->n
) {
3647 VLOG_WARN_RL(&syntax_rl
, "reply does not contain enough operations "
3648 "for increment (has %"PRIuSIZE
", needs %u)",
3649 results
->n
, txn
->inc_index
+ 2);
3653 /* We know that this is a JSON object because the loop in
3654 * ovsdb_idl_txn_process_reply() checked. */
3655 mutate
= json_object(results
->elems
[txn
->inc_index
]);
3656 count
= shash_find_data(mutate
, "count");
3657 if (!check_json_type(count
, JSON_INTEGER
, "\"mutate\" reply \"count\"")) {
3660 if (count
->u
.integer
!= 1) {
3661 VLOG_WARN_RL(&syntax_rl
,
3662 "\"mutate\" reply \"count\" is %lld instead of 1",
3667 select
= json_object(results
->elems
[txn
->inc_index
+ 1]);
3668 rows
= shash_find_data(select
, "rows");
3669 if (!check_json_type(rows
, JSON_ARRAY
, "\"select\" reply \"rows\"")) {
3672 if (rows
->u
.array
.n
!= 1) {
3673 VLOG_WARN_RL(&syntax_rl
, "\"select\" reply \"rows\" has %"PRIuSIZE
" elements "
3678 row
= rows
->u
.array
.elems
[0];
3679 if (!check_json_type(row
, JSON_OBJECT
, "\"select\" reply row")) {
3682 column
= shash_find_data(json_object(row
), txn
->inc_column
);
3683 if (!check_json_type(column
, JSON_INTEGER
,
3684 "\"select\" reply inc column")) {
3687 txn
->inc_new_value
= column
->u
.integer
;
3692 ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert
*insert
,
3693 const struct json_array
*results
)
3695 static const struct ovsdb_base_type uuid_type
= OVSDB_BASE_UUID_INIT
;
3696 struct ovsdb_error
*error
;
3697 struct json
*json_uuid
;
3698 union ovsdb_atom uuid
;
3699 struct shash
*reply
;
3701 if (insert
->op_index
>= results
->n
) {
3702 VLOG_WARN_RL(&syntax_rl
, "reply does not contain enough operations "
3703 "for insert (has %"PRIuSIZE
", needs %u)",
3704 results
->n
, insert
->op_index
);
3708 /* We know that this is a JSON object because the loop in
3709 * ovsdb_idl_txn_process_reply() checked. */
3710 reply
= json_object(results
->elems
[insert
->op_index
]);
3711 json_uuid
= shash_find_data(reply
, "uuid");
3712 if (!check_json_type(json_uuid
, JSON_ARRAY
, "\"insert\" reply \"uuid\"")) {
3716 error
= ovsdb_atom_from_json(&uuid
, &uuid_type
, json_uuid
, NULL
);
3718 char *s
= ovsdb_error_to_string(error
);
3719 VLOG_WARN_RL(&syntax_rl
, "\"insert\" reply \"uuid\" is not a JSON "
3722 ovsdb_error_destroy(error
);
3726 insert
->real
= uuid
.uuid
;
3732 ovsdb_idl_txn_process_reply(struct ovsdb_idl
*idl
,
3733 const struct jsonrpc_msg
*msg
)
3735 struct ovsdb_idl_txn
*txn
;
3736 enum ovsdb_idl_txn_status status
;
3738 txn
= ovsdb_idl_txn_find(idl
, msg
->id
);
3743 if (msg
->type
== JSONRPC_ERROR
) {
3745 } else if (msg
->result
->type
!= JSON_ARRAY
) {
3746 VLOG_WARN_RL(&syntax_rl
, "reply to \"transact\" is not JSON array");
3749 struct json_array
*ops
= &msg
->result
->u
.array
;
3750 int hard_errors
= 0;
3751 int soft_errors
= 0;
3752 int lock_errors
= 0;
3755 for (i
= 0; i
< ops
->n
; i
++) {
3756 struct json
*op
= ops
->elems
[i
];
3758 if (op
->type
== JSON_NULL
) {
3759 /* This isn't an error in itself but indicates that some prior
3760 * operation failed, so make sure that we know about it. */
3762 } else if (op
->type
== JSON_OBJECT
) {
3765 error
= shash_find_data(json_object(op
), "error");
3767 if (error
->type
== JSON_STRING
) {
3768 if (!strcmp(error
->u
.string
, "timed out")) {
3770 } else if (!strcmp(error
->u
.string
, "not owner")) {
3772 } else if (!strcmp(error
->u
.string
, "not allowed")) {
3774 ovsdb_idl_txn_set_error_json(txn
, op
);
3775 } else if (strcmp(error
->u
.string
, "aborted")) {
3777 ovsdb_idl_txn_set_error_json(txn
, op
);
3778 VLOG_WARN_RL(&other_rl
,
3779 "transaction error: %s", txn
->error
);
3783 ovsdb_idl_txn_set_error_json(txn
, op
);
3784 VLOG_WARN_RL(&syntax_rl
,
3785 "\"error\" in reply is not JSON string");
3790 ovsdb_idl_txn_set_error_json(txn
, op
);
3791 VLOG_WARN_RL(&syntax_rl
,
3792 "operation reply is not JSON null or object");
3796 if (!soft_errors
&& !hard_errors
&& !lock_errors
) {
3797 struct ovsdb_idl_txn_insert
*insert
;
3799 if (txn
->inc_table
&& !ovsdb_idl_txn_process_inc_reply(txn
, ops
)) {
3803 HMAP_FOR_EACH (insert
, hmap_node
, &txn
->inserted_rows
) {
3804 if (!ovsdb_idl_txn_process_insert_reply(insert
, ops
)) {
3810 status
= (hard_errors
? TXN_ERROR
3811 : lock_errors
? TXN_NOT_LOCKED
3812 : soft_errors
? TXN_TRY_AGAIN
3816 ovsdb_idl_txn_complete(txn
, status
);
3820 /* Returns the transaction currently active for 'row''s IDL. A transaction
3821 * must currently be active. */
3822 struct ovsdb_idl_txn
*
3823 ovsdb_idl_txn_get(const struct ovsdb_idl_row
*row
)
3825 struct ovsdb_idl_txn
*txn
= row
->table
->idl
->txn
;
3826 ovs_assert(txn
!= NULL
);
3830 /* Returns the IDL on which 'txn' acts. */
3832 ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn
*txn
)
3837 /* Blocks until 'idl' successfully connects to the remote database and
3838 * retrieves its contents. */
3840 ovsdb_idl_get_initial_snapshot(struct ovsdb_idl
*idl
)
3844 if (ovsdb_idl_has_ever_connected(idl
)) {
3847 ovsdb_idl_wait(idl
);
3852 /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from
3853 * the database server and to avoid modifying the database when the lock cannot
3854 * be acquired (that is, when another client has the same lock).
3856 * If 'lock_name' is NULL, drops the locking requirement and releases the
3859 ovsdb_idl_set_lock(struct ovsdb_idl
*idl
, const char *lock_name
)
3861 ovs_assert(!idl
->txn
);
3862 ovs_assert(hmap_is_empty(&idl
->outstanding_txns
));
3864 if (idl
->lock_name
&& (!lock_name
|| strcmp(lock_name
, idl
->lock_name
))) {
3865 /* Release previous lock. */
3866 ovsdb_idl_send_unlock_request(idl
);
3867 free(idl
->lock_name
);
3868 idl
->lock_name
= NULL
;
3869 idl
->is_lock_contended
= false;
3872 if (lock_name
&& !idl
->lock_name
) {
3873 /* Acquire new lock. */
3874 idl
->lock_name
= xstrdup(lock_name
);
3875 ovsdb_idl_send_lock_request(idl
);
3879 /* Returns true if 'idl' is configured to obtain a lock and owns that lock.
3881 * Locking and unlocking happens asynchronously from the database client's
3882 * point of view, so the information is only useful for optimization (e.g. if
3883 * the client doesn't have the lock then there's no point in trying to write to
3886 ovsdb_idl_has_lock(const struct ovsdb_idl
*idl
)
3888 return idl
->has_lock
;
3891 /* Returns true if 'idl' is configured to obtain a lock but the database server
3892 * has indicated that some other client already owns the requested lock. */
3894 ovsdb_idl_is_lock_contended(const struct ovsdb_idl
*idl
)
3896 return idl
->is_lock_contended
;
3900 ovsdb_idl_update_has_lock(struct ovsdb_idl
*idl
, bool new_has_lock
)
3902 if (new_has_lock
&& !idl
->has_lock
) {
3903 if (idl
->state
== IDL_S_MONITORING
||
3904 idl
->state
== IDL_S_MONITORING_COND
) {
3905 idl
->change_seqno
++;
3907 /* We're setting up a session, so don't signal that the database
3908 * changed. Finalizing the session will increment change_seqno
3911 idl
->is_lock_contended
= false;
3913 idl
->has_lock
= new_has_lock
;
3917 ovsdb_idl_send_lock_request__(struct ovsdb_idl
*idl
, const char *method
,
3920 ovsdb_idl_update_has_lock(idl
, false);
3922 json_destroy(idl
->lock_request_id
);
3923 idl
->lock_request_id
= NULL
;
3925 if (jsonrpc_session_is_connected(idl
->session
)) {
3926 struct json
*params
;
3928 params
= json_array_create_1(json_string_create(idl
->lock_name
));
3929 jsonrpc_session_send(idl
->session
,
3930 jsonrpc_create_request(method
, params
, idp
));
3935 ovsdb_idl_send_lock_request(struct ovsdb_idl
*idl
)
3937 ovsdb_idl_send_lock_request__(idl
, "lock", &idl
->lock_request_id
);
3941 ovsdb_idl_send_unlock_request(struct ovsdb_idl
*idl
)
3943 ovsdb_idl_send_lock_request__(idl
, "unlock", NULL
);
3947 ovsdb_idl_parse_lock_reply(struct ovsdb_idl
*idl
, const struct json
*result
)
3951 json_destroy(idl
->lock_request_id
);
3952 idl
->lock_request_id
= NULL
;
3954 if (result
->type
== JSON_OBJECT
) {
3955 const struct json
*locked
;
3957 locked
= shash_find_data(json_object(result
), "locked");
3958 got_lock
= locked
&& locked
->type
== JSON_TRUE
;
3963 ovsdb_idl_update_has_lock(idl
, got_lock
);
3965 idl
->is_lock_contended
= true;
3970 ovsdb_idl_parse_lock_notify(struct ovsdb_idl
*idl
,
3971 const struct json
*params
,
3975 && params
->type
== JSON_ARRAY
3976 && json_array(params
)->n
> 0
3977 && json_array(params
)->elems
[0]->type
== JSON_STRING
) {
3978 const char *lock_name
= json_string(json_array(params
)->elems
[0]);
3980 if (!strcmp(idl
->lock_name
, lock_name
)) {
3981 ovsdb_idl_update_has_lock(idl
, new_has_lock
);
3982 if (!new_has_lock
) {
3983 idl
->is_lock_contended
= true;
3989 /* Inserts a new Map Operation into current transaction. */
3991 ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row
*row
,
3992 const struct ovsdb_idl_column
*column
,
3993 struct ovsdb_datum
*datum
,
3994 enum map_op_type op_type
)
3996 const struct ovsdb_idl_table_class
*class;
3998 struct map_op
*map_op
;
4000 class = row
->table
->class;
4001 column_idx
= column
- class->columns
;
4003 /* Check if a map operation list exists for this column. */
4004 if (!row
->map_op_written
) {
4005 row
->map_op_written
= bitmap_allocate(class->n_columns
);
4006 row
->map_op_lists
= xzalloc(class->n_columns
*
4007 sizeof *row
->map_op_lists
);
4009 if (!row
->map_op_lists
[column_idx
]) {
4010 row
->map_op_lists
[column_idx
] = map_op_list_create();
4013 /* Add a map operation to the corresponding list. */
4014 map_op
= map_op_create(datum
, op_type
);
4015 bitmap_set1(row
->map_op_written
, column_idx
);
4016 map_op_list_add(row
->map_op_lists
[column_idx
], map_op
, &column
->type
);
4018 /* Add this row to transaction's list of rows. */
4019 if (hmap_node_is_null(&row
->txn_node
)) {
4020 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
4021 uuid_hash(&row
->uuid
));
4025 /* Inserts a new Set Operation into current transaction. */
4027 ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row
*row
,
4028 const struct ovsdb_idl_column
*column
,
4029 struct ovsdb_datum
*datum
,
4030 enum set_op_type op_type
)
4032 const struct ovsdb_idl_table_class
*class;
4034 struct set_op
*set_op
;
4036 class = row
->table
->class;
4037 column_idx
= column
- class->columns
;
4039 /* Check if a set operation list exists for this column. */
4040 if (!row
->set_op_written
) {
4041 row
->set_op_written
= bitmap_allocate(class->n_columns
);
4042 row
->set_op_lists
= xzalloc(class->n_columns
*
4043 sizeof *row
->set_op_lists
);
4045 if (!row
->set_op_lists
[column_idx
]) {
4046 row
->set_op_lists
[column_idx
] = set_op_list_create();
4049 /* Add a set operation to the corresponding list. */
4050 set_op
= set_op_create(datum
, op_type
);
4051 bitmap_set1(row
->set_op_written
, column_idx
);
4052 set_op_list_add(row
->set_op_lists
[column_idx
], set_op
, &column
->type
);
4054 /* Add this row to the transactions's list of rows. */
4055 if (hmap_node_is_null(&row
->txn_node
)) {
4056 hmap_insert(&row
->table
->idl
->txn
->txn_rows
, &row
->txn_node
,
4057 uuid_hash(&row
->uuid
));
4062 is_valid_partial_update(const struct ovsdb_idl_row
*row
,
4063 const struct ovsdb_idl_column
*column
,
4064 struct ovsdb_datum
*datum
)
4066 /* Verify that this column is being monitored. */
4067 unsigned int column_idx
= column
- row
->table
->class->columns
;
4068 if (!(row
->table
->modes
[column_idx
] & OVSDB_IDL_MONITOR
)) {
4069 VLOG_WARN("cannot partially update non-monitored column");
4073 /* Verify that the update affects a single element. */
4074 if (datum
->n
!= 1) {
4075 VLOG_WARN("invalid datum for partial update");
4082 /* Inserts the value described in 'datum' into the map in 'column' in
4083 * 'row_'. If the value doesn't already exist in 'column' then it's value
4084 * is added. The value in 'datum' must be of the same type as the values
4085 * in 'column'. This function takes ownership of 'datum'.
4087 * Usually this function is used indirectly through one of the "update"
4088 * functions generated by vswitch-idl. */
4090 ovsdb_idl_txn_write_partial_set(const struct ovsdb_idl_row
*row_
,
4091 const struct ovsdb_idl_column
*column
,
4092 struct ovsdb_datum
*datum
)
4094 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4095 enum set_op_type op_type
;
4097 if (!is_valid_partial_update(row
, column
, datum
)) {
4098 ovsdb_datum_destroy(datum
, &column
->type
);
4103 op_type
= SET_OP_INSERT
;
4105 ovsdb_idl_txn_add_set_op(row
, column
, datum
, op_type
);
4108 /* Deletes the value specified in 'datum' from the set in 'column' in 'row_'.
4109 * The value in 'datum' must be of the same type as the keys in 'column'.
4110 * This function takes ownership of 'datum'.
4112 * Usually this function is used indirectly through one of the "update"
4113 * functions generated by vswitch-idl. */
4115 ovsdb_idl_txn_delete_partial_set(const struct ovsdb_idl_row
*row_
,
4116 const struct ovsdb_idl_column
*column
,
4117 struct ovsdb_datum
*datum
)
4119 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4121 if (!is_valid_partial_update(row
, column
, datum
)) {
4122 struct ovsdb_type type_
= column
->type
;
4123 type_
.value
.type
= OVSDB_TYPE_VOID
;
4124 ovsdb_datum_destroy(datum
, &type_
);
4128 ovsdb_idl_txn_add_set_op(row
, column
, datum
, SET_OP_DELETE
);
4131 /* Inserts the key-value specified in 'datum' into the map in 'column' in
4132 * 'row_'. If the key already exist in 'column', then it's value is updated
4133 * with the value in 'datum'. The key-value in 'datum' must be of the same type
4134 * as the keys-values in 'column'. This function takes ownership of 'datum'.
4136 * Usually this function is used indirectly through one of the "update"
4137 * functions generated by vswitch-idl. */
4139 ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row
*row_
,
4140 const struct ovsdb_idl_column
*column
,
4141 struct ovsdb_datum
*datum
)
4143 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4144 enum ovsdb_atomic_type key_type
;
4145 enum map_op_type op_type
;
4147 const struct ovsdb_datum
*old_datum
;
4149 if (!is_valid_partial_update(row
, column
, datum
)) {
4150 ovsdb_datum_destroy(datum
, &column
->type
);
4155 /* Find out if this is an insert or an update. */
4156 key_type
= column
->type
.key
.type
;
4157 old_datum
= ovsdb_idl_read(row
, column
);
4158 pos
= ovsdb_datum_find_key(old_datum
, &datum
->keys
[0], key_type
);
4159 op_type
= pos
== UINT_MAX
? MAP_OP_INSERT
: MAP_OP_UPDATE
;
4161 ovsdb_idl_txn_add_map_op(row
, column
, datum
, op_type
);
4164 /* Deletes the key specified in 'datum' from the map in 'column' in 'row_'.
4165 * The key in 'datum' must be of the same type as the keys in 'column'.
4166 * The value in 'datum' must be NULL. This function takes ownership of
4169 * Usually this function is used indirectly through one of the "update"
4170 * functions generated by vswitch-idl. */
4172 ovsdb_idl_txn_delete_partial_map(const struct ovsdb_idl_row
*row_
,
4173 const struct ovsdb_idl_column
*column
,
4174 struct ovsdb_datum
*datum
)
4176 struct ovsdb_idl_row
*row
= CONST_CAST(struct ovsdb_idl_row
*, row_
);
4178 if (!is_valid_partial_update(row
, column
, datum
)) {
4179 struct ovsdb_type type_
= column
->type
;
4180 type_
.value
.type
= OVSDB_TYPE_VOID
;
4181 ovsdb_datum_destroy(datum
, &type_
);
4185 ovsdb_idl_txn_add_map_op(row
, column
, datum
, MAP_OP_DELETE
);
4189 ovsdb_idl_loop_destroy(struct ovsdb_idl_loop
*loop
)
4192 ovsdb_idl_destroy(loop
->idl
);
4196 struct ovsdb_idl_txn
*
4197 ovsdb_idl_loop_run(struct ovsdb_idl_loop
*loop
)
4199 ovsdb_idl_run(loop
->idl
);
4200 loop
->open_txn
= (loop
->committing_txn
4201 || ovsdb_idl_get_seqno(loop
->idl
) == loop
->skip_seqno
4203 : ovsdb_idl_txn_create(loop
->idl
));
4204 return loop
->open_txn
;
4207 /* Attempts to commit the current transaction, if one is open, and sets up the
4208 * poll loop to wake up when some more work might be needed.
4210 * If a transaction was open, in this or a previous iteration of the main loop,
4211 * and had not before finished committing (successfully or unsuccessfully), the
4212 * return value is one of:
4214 * 1: The transaction committed successfully (or it did not change anything in
4216 * 0: The transaction failed.
4217 * -1: The commit is still in progress.
4219 * Thus, the return value is -1 if the transaction is in progress and otherwise
4220 * true for success, false for failure.
4222 * (In the corner case where the IDL sends a transaction to the database and
4223 * the database commits it, and the connection between the IDL and the database
4224 * drops before the IDL receives the message confirming the commit, this
4225 * function can return 0 even though the transaction succeeded.)
4228 ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop
*loop
)
4230 if (loop
->open_txn
) {
4231 loop
->committing_txn
= loop
->open_txn
;
4232 loop
->open_txn
= NULL
;
4234 loop
->precommit_seqno
= ovsdb_idl_get_seqno(loop
->idl
);
4237 struct ovsdb_idl_txn
*txn
= loop
->committing_txn
;
4240 enum ovsdb_idl_txn_status status
= ovsdb_idl_txn_commit(txn
);
4241 if (status
!= TXN_INCOMPLETE
) {
4244 /* We want to re-evaluate the database when it's changed from
4245 * the contents that it had when we started the commit. (That
4246 * might have already happened.) */
4247 loop
->skip_seqno
= loop
->precommit_seqno
;
4248 if (ovsdb_idl_get_seqno(loop
->idl
) != loop
->skip_seqno
) {
4249 poll_immediate_wake();
4255 /* Possibly some work on the database was deferred because no
4256 * further transaction could proceed. Wake up again. */
4258 loop
->cur_cfg
= loop
->next_cfg
;
4259 poll_immediate_wake();
4264 loop
->cur_cfg
= loop
->next_cfg
;
4268 case TXN_NOT_LOCKED
:
4273 case TXN_UNCOMMITTED
:
4274 case TXN_INCOMPLETE
:
4278 ovsdb_idl_txn_destroy(txn
);
4279 loop
->committing_txn
= NULL
;
4284 /* Not a meaningful return value: no transaction was in progress. */
4288 ovsdb_idl_wait(loop
->idl
);