]>
Commit | Line | Data |
---|---|---|
a0b02897 | 1 | /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc. |
93fe0264 | 2 | * Copyright (C) 2016 Hewlett Packard Enterprise Development LP |
c3bb4bd7 BP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | ||
19 | #include "ovsdb-idl.h" | |
20 | ||
475281c0 | 21 | #include <errno.h> |
b54e22e9 | 22 | #include <inttypes.h> |
c3bb4bd7 BP |
23 | #include <limits.h> |
24 | #include <stdlib.h> | |
25 | ||
475281c0 | 26 | #include "bitmap.h" |
60032110 | 27 | #include "coverage.h" |
0164e367 | 28 | #include "hash.h" |
3e8a2ad1 | 29 | #include "openvswitch/dynamic-string.h" |
b302749b | 30 | #include "fatal-signal.h" |
ee89ea7b | 31 | #include "openvswitch/json.h" |
c3bb4bd7 | 32 | #include "jsonrpc.h" |
d18e52e3 BP |
33 | #include "ovsdb/ovsdb.h" |
34 | #include "ovsdb/table.h" | |
c3bb4bd7 BP |
35 | #include "ovsdb-data.h" |
36 | #include "ovsdb-error.h" | |
37 | #include "ovsdb-idl-provider.h" | |
d18e52e3 | 38 | #include "ovsdb-parser.h" |
fd016ae3 | 39 | #include "openvswitch/poll-loop.h" |
ee89ea7b | 40 | #include "openvswitch/shash.h" |
93fe0264 | 41 | #include "skiplist.h" |
d18e52e3 | 42 | #include "sset.h" |
c3bb4bd7 | 43 | #include "util.h" |
93fe0264 | 44 | #include "uuid.h" |
e6211adc | 45 | #include "openvswitch/vlog.h" |
c3bb4bd7 | 46 | |
d98e6007 | 47 | VLOG_DEFINE_THIS_MODULE(ovsdb_idl); |
5136ce49 | 48 | |
60032110 RW |
49 | COVERAGE_DEFINE(txn_uncommitted); |
50 | COVERAGE_DEFINE(txn_unchanged); | |
51 | COVERAGE_DEFINE(txn_incomplete); | |
52 | COVERAGE_DEFINE(txn_aborted); | |
53 | COVERAGE_DEFINE(txn_success); | |
54 | COVERAGE_DEFINE(txn_try_again); | |
55 | COVERAGE_DEFINE(txn_not_locked); | |
56 | COVERAGE_DEFINE(txn_error); | |
57 | ||
c3bb4bd7 BP |
58 | /* An arc from one idl_row to another. When row A contains a UUID that |
59 | * references row B, this is represented by an arc from A (the source) to B | |
60 | * (the destination). | |
61 | * | |
62 | * Arcs from a row to itself are omitted, that is, src and dst are always | |
63 | * different. | |
64 | * | |
65 | * Arcs are never duplicated, that is, even if there are multiple references | |
66 | * from A to B, there is only a single arc from A to B. | |
67 | * | |
68 | * Arcs are directed: an arc from A to B is the converse of an an arc from B to | |
69 | * A. Both an arc and its converse may both be present, if each row refers | |
70 | * to the other circularly. | |
71 | * | |
72 | * The source and destination row may be in the same table or in different | |
73 | * tables. | |
74 | */ | |
75 | struct ovsdb_idl_arc { | |
ca6ba700 TG |
76 | struct ovs_list src_node; /* In src->src_arcs list. */ |
77 | struct ovs_list dst_node; /* In dst->dst_arcs list. */ | |
c3bb4bd7 BP |
78 | struct ovsdb_idl_row *src; /* Source row. */ |
79 | struct ovsdb_idl_row *dst; /* Destination row. */ | |
80 | }; | |
81 | ||
f5a12b82 BP |
82 | /* Connection state machine. |
83 | * | |
84 | * When a JSON-RPC session connects, the IDL sends a "get_schema" request and | |
85 | * transitions to IDL_S_SCHEMA_REQUESTED. If the session drops and reconnects, | |
86 | * the IDL starts over again in the same way. */ | |
d18e52e3 | 87 | enum ovsdb_idl_state { |
f5a12b82 BP |
88 | /* Waits for "get_schema" reply, then sends a "monitor_cond" request whose |
89 | * details are informed by the schema and transitions to | |
90 | * IDL_S_MONITOR_COND_REQUESTED. */ | |
d18e52e3 | 91 | IDL_S_SCHEMA_REQUESTED, |
f5a12b82 BP |
92 | |
93 | /* Waits for "monitor_cond" reply: | |
94 | * | |
95 | * - If the reply indicates success, replaces the IDL contents by the | |
96 | * data carried in the reply and transitions to IDL_S_MONITORING_COND. | |
97 | * | |
98 | * - If the reply indicates failure because the database is too old to | |
99 | * support monitor_cond, sends a "monitor" request and transitions to | |
100 | * IDl_S_MONITOR_REQUESTED. */ | |
c383f3bf | 101 | IDL_S_MONITOR_COND_REQUESTED, |
f5a12b82 BP |
102 | |
103 | /* Waits for "monitor" reply, then replaces the IDL contents by the data | |
104 | * carried in the reply and transitions to IDL_S_MONITORING. */ | |
105 | IDL_S_MONITOR_REQUESTED, | |
106 | ||
107 | /* Terminal states that process "update2" (IDL_S_MONITORING_COND) or | |
108 | * "update" (IDL_S_MONITORING) notifications. */ | |
c383f3bf | 109 | IDL_S_MONITORING_COND, |
f5a12b82 BP |
110 | IDL_S_MONITORING, |
111 | ||
112 | /* Terminal error state that indicates that nothing useful can be done. | |
113 | * The most likely reason is that the database server doesn't have the | |
114 | * desired database. We maintain the session with the database server | |
115 | * anyway. If it starts serving the database that we want, then it will | |
116 | * kill the session and we will automatically reconnect and try again. */ | |
6d882210 | 117 | IDL_S_NO_SCHEMA |
d18e52e3 BP |
118 | }; |
119 | ||
c3bb4bd7 | 120 | struct ovsdb_idl { |
f5a12b82 | 121 | /* Data. */ |
3eb14233 | 122 | const struct ovsdb_idl_class *class_; |
c1ac745c | 123 | struct shash table_by_name; /* Contains "struct ovsdb_idl_table *"s.*/ |
3eb14233 | 124 | struct ovsdb_idl_table *tables; /* Array of ->class_->n_tables elements. */ |
c3bb4bd7 | 125 | unsigned int change_seqno; |
475281c0 | 126 | |
f5a12b82 BP |
127 | /* Session state. |
128 | * | |
129 | *'state_seqno' is a snapshot of the session's sequence number as returned | |
130 | * jsonrpc_session_get_seqno(session), so if it differs from the value that | |
131 | * function currently returns then the session has reconnected and the | |
132 | * state machine must restart. */ | |
133 | struct jsonrpc_session *session; /* Connection to the server. */ | |
134 | enum ovsdb_idl_state state; /* Current session state. */ | |
135 | unsigned int state_seqno; /* See above. */ | |
136 | struct json *request_id; /* JSON ID for request awaiting reply. */ | |
137 | struct json *schema; /* Temporary copy of database schema. */ | |
138 | struct uuid uuid; /* Identifier for monitor. */ | |
d18e52e3 | 139 | |
06b6d651 BP |
140 | /* Database locking. */ |
141 | char *lock_name; /* Name of lock we need, NULL if none. */ | |
142 | bool has_lock; /* Has db server told us we have the lock? */ | |
143 | bool is_lock_contended; /* Has db server told us we can't get lock? */ | |
144 | struct json *lock_request_id; /* JSON-RPC ID of in-flight lock request. */ | |
145 | ||
475281c0 BP |
146 | /* Transaction support. */ |
147 | struct ovsdb_idl_txn *txn; | |
148 | struct hmap outstanding_txns; | |
f5a12b82 | 149 | bool verify_write_only; |
46437c52 AZ |
150 | |
151 | /* Conditional monitoring. */ | |
16ebb90e | 152 | bool cond_changed; |
46437c52 AZ |
153 | unsigned int cond_seqno; /* Keep track of condition clauses changes |
154 | over a single conditional monitoring session. | |
155 | Reverts to zero when idl session | |
156 | reconnects. */ | |
475281c0 BP |
157 | }; |
158 | ||
159 | struct ovsdb_idl_txn { | |
160 | struct hmap_node hmap_node; | |
161 | struct json *request_id; | |
162 | struct ovsdb_idl *idl; | |
163 | struct hmap txn_rows; | |
164 | enum ovsdb_idl_txn_status status; | |
91e310a5 | 165 | char *error; |
577aebdf | 166 | bool dry_run; |
d171b584 | 167 | struct ds comment; |
b54e22e9 BP |
168 | |
169 | /* Increments. */ | |
94fbe1aa BP |
170 | const char *inc_table; |
171 | const char *inc_column; | |
172 | struct uuid inc_row; | |
de32cec7 | 173 | bool inc_force; |
b54e22e9 BP |
174 | unsigned int inc_index; |
175 | int64_t inc_new_value; | |
69490970 BP |
176 | |
177 | /* Inserted rows. */ | |
d95d1510 | 178 | struct hmap inserted_rows; /* Contains "struct ovsdb_idl_txn_insert"s. */ |
69490970 BP |
179 | }; |
180 | ||
181 | struct ovsdb_idl_txn_insert { | |
182 | struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */ | |
183 | struct uuid dummy; /* Dummy UUID used locally. */ | |
184 | int op_index; /* Index into transaction's operation array. */ | |
185 | struct uuid real; /* Real UUID used by database server. */ | |
c3bb4bd7 BP |
186 | }; |
187 | ||
db2b5757 AZ |
188 | enum ovsdb_update_version { |
189 | OVSDB_UPDATE, /* RFC 7047 "update" method. */ | |
190 | OVSDB_UPDATE2 /* "update2" Extension to RFC 7047. | |
191 | See ovsdb-server(1) for more information. */ | |
192 | }; | |
193 | ||
194 | /* Name arrays indexed by 'enum ovsdb_update_version'. */ | |
9bf924c8 AZ |
195 | static const char *table_updates_names[] = {"table_updates", "table_updates2"}; |
196 | static const char *table_update_names[] = {"table_update", "table_update2"}; | |
197 | static const char *row_update_names[] = {"row_update", "row_update2"}; | |
db2b5757 | 198 | |
c3bb4bd7 BP |
199 | static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5); |
200 | static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
d6db7b3c | 201 | static struct vlog_rate_limit other_rl = VLOG_RATE_LIMIT_INIT(1, 5); |
c3bb4bd7 BP |
202 | |
203 | static void ovsdb_idl_clear(struct ovsdb_idl *); | |
d18e52e3 | 204 | static void ovsdb_idl_send_schema_request(struct ovsdb_idl *); |
db2b5757 | 205 | static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *); |
c383f3bf | 206 | static void ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl *); |
db2b5757 AZ |
207 | static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *, |
208 | enum ovsdb_update_version); | |
c3bb4bd7 | 209 | static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *, |
db2b5757 AZ |
210 | const struct json *, |
211 | enum ovsdb_update_version); | |
c547535a | 212 | static bool ovsdb_idl_process_update(struct ovsdb_idl_table *, |
c3bb4bd7 BP |
213 | const struct uuid *, |
214 | const struct json *old, | |
215 | const struct json *new); | |
db2b5757 AZ |
216 | static bool ovsdb_idl_process_update2(struct ovsdb_idl_table *, |
217 | const struct uuid *, | |
218 | const char *operation, | |
219 | const struct json *row); | |
c3bb4bd7 BP |
220 | static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *); |
221 | static void ovsdb_idl_delete_row(struct ovsdb_idl_row *); | |
c547535a | 222 | static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *); |
db2b5757 AZ |
223 | static bool ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *, |
224 | const struct json *); | |
c3bb4bd7 BP |
225 | |
226 | static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); | |
475281c0 BP |
227 | static struct ovsdb_idl_row *ovsdb_idl_row_create__( |
228 | const struct ovsdb_idl_table_class *); | |
c3bb4bd7 BP |
229 | static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *, |
230 | const struct uuid *); | |
231 | static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *); | |
932104f4 | 232 | static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *); |
f199df26 | 233 | static void ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *); |
f1ab6e06 | 234 | static void ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *); |
c3bb4bd7 | 235 | |
979821c0 BP |
236 | static void ovsdb_idl_row_parse(struct ovsdb_idl_row *); |
237 | static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *); | |
475281c0 BP |
238 | static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *); |
239 | static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *); | |
932104f4 | 240 | static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *, bool destroy_dsts); |
475281c0 BP |
241 | |
242 | static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *); | |
243 | static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *, | |
244 | const struct jsonrpc_msg *msg); | |
f199df26 EA |
245 | static bool ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *, |
246 | struct json *); | |
247 | static void ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *, | |
248 | const struct ovsdb_idl_column *, | |
249 | struct ovsdb_datum *, | |
250 | enum map_op_type); | |
f1ab6e06 RM |
251 | static void ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *, |
252 | const struct ovsdb_idl_column *, | |
253 | struct ovsdb_datum *, | |
254 | enum set_op_type); | |
c3bb4bd7 | 255 | |
06b6d651 BP |
256 | static void ovsdb_idl_send_lock_request(struct ovsdb_idl *); |
257 | static void ovsdb_idl_send_unlock_request(struct ovsdb_idl *); | |
258 | static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *, | |
259 | const struct json *); | |
260 | static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *, | |
261 | const struct json *params, | |
262 | bool new_has_lock); | |
932104f4 SA |
263 | static struct ovsdb_idl_table * |
264 | ovsdb_idl_table_from_class(const struct ovsdb_idl *, | |
265 | const struct ovsdb_idl_table_class *); | |
266 | static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table); | |
16ebb90e | 267 | static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl); |
06b6d651 | 268 | |
93fe0264 LR |
269 | static struct ovsdb_idl_index *ovsdb_idl_create_index_(const struct |
270 | ovsdb_idl_table *table, | |
271 | size_t allocated_cols); | |
272 | static void | |
273 | ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table); | |
274 | static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *); | |
275 | static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *); | |
276 | ||
2ce42c88 BP |
277 | /* Creates and returns a connection to database 'remote', which should be in a |
278 | * form acceptable to jsonrpc_session_open(). The connection will maintain an | |
279 | * in-memory replica of the remote database whose schema is described by | |
280 | * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically | |
ef73f86c BP |
281 | * by ovsdb-idlc.) |
282 | * | |
fba6bd1d BP |
283 | * Passes 'retry' to jsonrpc_session_open(). See that function for |
284 | * documentation. | |
285 | * | |
ef73f86c BP |
286 | * If 'monitor_everything_by_default' is true, then everything in the remote |
287 | * database will be replicated by default. ovsdb_idl_omit() and | |
288 | * ovsdb_idl_omit_alert() may be used to selectively drop some columns from | |
289 | * monitoring. | |
290 | * | |
291 | * If 'monitor_everything_by_default' is false, then no columns or tables will | |
292 | * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() | |
293 | * must be used to choose some columns or tables to replicate. | |
294 | */ | |
c3bb4bd7 | 295 | struct ovsdb_idl * |
ef73f86c | 296 | ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, |
fba6bd1d | 297 | bool monitor_everything_by_default, bool retry) |
c3bb4bd7 BP |
298 | { |
299 | struct ovsdb_idl *idl; | |
ef73f86c | 300 | uint8_t default_mode; |
c3bb4bd7 BP |
301 | size_t i; |
302 | ||
ef73f86c BP |
303 | default_mode = (monitor_everything_by_default |
304 | ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT | |
305 | : 0); | |
306 | ||
c3bb4bd7 | 307 | idl = xzalloc(sizeof *idl); |
3eb14233 | 308 | idl->class_ = class; |
fba6bd1d | 309 | idl->session = jsonrpc_session_open(remote, retry); |
115f1e4d BP |
310 | shash_init(&idl->table_by_name); |
311 | idl->tables = xmalloc(class->n_tables * sizeof *idl->tables); | |
c3bb4bd7 BP |
312 | for (i = 0; i < class->n_tables; i++) { |
313 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; | |
115f1e4d | 314 | struct ovsdb_idl_table *table = &idl->tables[i]; |
c3bb4bd7 BP |
315 | size_t j; |
316 | ||
efdd9088 | 317 | shash_add_assert(&idl->table_by_name, tc->name, table); |
3eb14233 | 318 | table->class_ = tc; |
c547535a | 319 | table->modes = xmalloc(tc->n_columns); |
ef73f86c BP |
320 | memset(table->modes, default_mode, tc->n_columns); |
321 | table->need_table = false; | |
c3bb4bd7 | 322 | shash_init(&table->columns); |
93fe0264 | 323 | shash_init(&table->indexes); |
c3bb4bd7 BP |
324 | for (j = 0; j < tc->n_columns; j++) { |
325 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
326 | ||
efdd9088 | 327 | shash_add_assert(&table->columns, column->name, column); |
c3bb4bd7 BP |
328 | } |
329 | hmap_init(&table->rows); | |
417e7e66 | 330 | ovs_list_init(&table->track_list); |
932104f4 SA |
331 | table->change_seqno[OVSDB_IDL_CHANGE_INSERT] |
332 | = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] | |
333 | = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; | |
c3bb4bd7 | 334 | table->idl = idl; |
bf26aac5 | 335 | ovsdb_idl_condition_init(&table->condition); |
0164e367 | 336 | ovsdb_idl_condition_add_clause_true(&table->condition); |
16ebb90e | 337 | table->cond_changed = false; |
c3bb4bd7 | 338 | } |
d18e52e3 | 339 | |
16ebb90e | 340 | idl->cond_changed = false; |
46437c52 | 341 | idl->cond_seqno = 0; |
d18e52e3 BP |
342 | idl->state_seqno = UINT_MAX; |
343 | idl->request_id = NULL; | |
db2b5757 | 344 | idl->schema = NULL; |
d18e52e3 | 345 | |
475281c0 | 346 | hmap_init(&idl->outstanding_txns); |
7152b6fa | 347 | uuid_generate(&idl->uuid); |
c3bb4bd7 BP |
348 | |
349 | return idl; | |
350 | } | |
351 | ||
1b62572d RM |
352 | /* Changes the remote and creates a new session. */ |
353 | void | |
354 | ovsdb_idl_set_remote(struct ovsdb_idl *idl, const char *remote, | |
355 | bool retry) | |
356 | { | |
357 | if (idl) { | |
358 | ovs_assert(!idl->txn); | |
634f6c29 | 359 | jsonrpc_session_close(idl->session); |
1b62572d RM |
360 | idl->session = jsonrpc_session_open(remote, retry); |
361 | idl->state_seqno = UINT_MAX; | |
362 | } | |
363 | } | |
364 | ||
2ce42c88 | 365 | /* Destroys 'idl' and all of the data structures that it manages. */ |
c3bb4bd7 BP |
366 | void |
367 | ovsdb_idl_destroy(struct ovsdb_idl *idl) | |
368 | { | |
369 | if (idl) { | |
115f1e4d | 370 | size_t i; |
c3bb4bd7 | 371 | |
cb22974d | 372 | ovs_assert(!idl->txn); |
c3bb4bd7 BP |
373 | ovsdb_idl_clear(idl); |
374 | jsonrpc_session_close(idl->session); | |
375 | ||
3eb14233 | 376 | for (i = 0; i < idl->class_->n_tables; i++) { |
115f1e4d | 377 | struct ovsdb_idl_table *table = &idl->tables[i]; |
d927cd6e | 378 | ovsdb_idl_condition_destroy(&table->condition); |
93fe0264 | 379 | ovsdb_idl_destroy_indexes(table); |
c3bb4bd7 BP |
380 | shash_destroy(&table->columns); |
381 | hmap_destroy(&table->rows); | |
c547535a | 382 | free(table->modes); |
c3bb4bd7 | 383 | } |
115f1e4d BP |
384 | shash_destroy(&idl->table_by_name); |
385 | free(idl->tables); | |
d18e52e3 | 386 | json_destroy(idl->request_id); |
06b6d651 BP |
387 | free(idl->lock_name); |
388 | json_destroy(idl->lock_request_id); | |
db2b5757 | 389 | json_destroy(idl->schema); |
da157f1e | 390 | hmap_destroy(&idl->outstanding_txns); |
c3bb4bd7 BP |
391 | free(idl); |
392 | } | |
393 | } | |
394 | ||
395 | static void | |
396 | ovsdb_idl_clear(struct ovsdb_idl *idl) | |
397 | { | |
c3bb4bd7 | 398 | bool changed = false; |
115f1e4d | 399 | size_t i; |
c3bb4bd7 | 400 | |
3eb14233 | 401 | for (i = 0; i < idl->class_->n_tables; i++) { |
115f1e4d | 402 | struct ovsdb_idl_table *table = &idl->tables[i]; |
c3bb4bd7 BP |
403 | struct ovsdb_idl_row *row, *next_row; |
404 | ||
5351980b | 405 | table->cond_changed = false; |
c3bb4bd7 BP |
406 | if (hmap_is_empty(&table->rows)) { |
407 | continue; | |
408 | } | |
409 | ||
410 | changed = true; | |
4e8e4213 | 411 | HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) { |
c3bb4bd7 BP |
412 | struct ovsdb_idl_arc *arc, *next_arc; |
413 | ||
414 | if (!ovsdb_idl_row_is_orphan(row)) { | |
93fe0264 | 415 | ovsdb_idl_remove_from_indexes(row); |
979821c0 | 416 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 417 | } |
4e8e4213 | 418 | LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) { |
c3bb4bd7 BP |
419 | free(arc); |
420 | } | |
421 | /* No need to do anything with dst_arcs: some node has those arcs | |
422 | * as forward arcs and will destroy them itself. */ | |
423 | ||
417e7e66 BW |
424 | if (!ovs_list_is_empty(&row->track_node)) { |
425 | ovs_list_remove(&row->track_node); | |
932104f4 SA |
426 | } |
427 | ||
0196cc15 | 428 | ovsdb_idl_row_destroy(row); |
c3bb4bd7 BP |
429 | } |
430 | } | |
431 | ||
5351980b | 432 | idl->cond_changed = false; |
46437c52 | 433 | idl->cond_seqno = 0; |
932104f4 SA |
434 | ovsdb_idl_track_clear(idl); |
435 | ||
c3bb4bd7 BP |
436 | if (changed) { |
437 | idl->change_seqno++; | |
438 | } | |
439 | } | |
440 | ||
854a94d9 BP |
441 | /* Processes a batch of messages from the database server on 'idl'. This may |
442 | * cause the IDL's contents to change. The client may check for that with | |
443 | * ovsdb_idl_get_seqno(). */ | |
444 | void | |
c3bb4bd7 BP |
445 | ovsdb_idl_run(struct ovsdb_idl *idl) |
446 | { | |
447 | int i; | |
448 | ||
cb22974d | 449 | ovs_assert(!idl->txn); |
16ebb90e LS |
450 | |
451 | ovsdb_idl_send_cond_change(idl); | |
452 | ||
c3bb4bd7 BP |
453 | jsonrpc_session_run(idl->session); |
454 | for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) { | |
828cd4c7 | 455 | struct jsonrpc_msg *msg; |
c3bb4bd7 BP |
456 | unsigned int seqno; |
457 | ||
458 | seqno = jsonrpc_session_get_seqno(idl->session); | |
d18e52e3 BP |
459 | if (idl->state_seqno != seqno) { |
460 | idl->state_seqno = seqno; | |
461 | json_destroy(idl->request_id); | |
462 | idl->request_id = NULL; | |
475281c0 | 463 | ovsdb_idl_txn_abort_all(idl); |
d18e52e3 BP |
464 | |
465 | ovsdb_idl_send_schema_request(idl); | |
466 | idl->state = IDL_S_SCHEMA_REQUESTED; | |
06b6d651 BP |
467 | if (idl->lock_name) { |
468 | ovsdb_idl_send_lock_request(idl); | |
469 | } | |
c3bb4bd7 BP |
470 | } |
471 | ||
472 | msg = jsonrpc_session_recv(idl->session); | |
473 | if (!msg) { | |
474 | break; | |
475 | } | |
476 | ||
4931f33a | 477 | if (msg->type == JSONRPC_NOTIFY |
db2b5757 | 478 | && !strcmp(msg->method, "update2") |
d95d1510 BP |
479 | && msg->params->type == JSON_ARRAY |
480 | && msg->params->u.array.n == 2 | |
7152b6fa | 481 | && msg->params->u.array.elems[0]->type == JSON_STRING) { |
06b6d651 | 482 | /* Database contents changed. */ |
db2b5757 AZ |
483 | ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1], |
484 | OVSDB_UPDATE2); | |
c3bb4bd7 | 485 | } else if (msg->type == JSONRPC_REPLY |
d18e52e3 BP |
486 | && idl->request_id |
487 | && json_equal(idl->request_id, msg->id)) { | |
db2b5757 AZ |
488 | json_destroy(idl->request_id); |
489 | idl->request_id = NULL; | |
490 | ||
d18e52e3 BP |
491 | switch (idl->state) { |
492 | case IDL_S_SCHEMA_REQUESTED: | |
493 | /* Reply to our "get_schema" request. */ | |
db2b5757 | 494 | idl->schema = json_clone(msg->result); |
c383f3bf LS |
495 | ovsdb_idl_send_monitor_cond_request(idl); |
496 | idl->state = IDL_S_MONITOR_COND_REQUESTED; | |
d18e52e3 BP |
497 | break; |
498 | ||
499 | case IDL_S_MONITOR_REQUESTED: | |
c383f3bf LS |
500 | case IDL_S_MONITOR_COND_REQUESTED: |
501 | /* Reply to our "monitor" or "monitor_cond" request. */ | |
d18e52e3 | 502 | idl->change_seqno++; |
d18e52e3 | 503 | ovsdb_idl_clear(idl); |
db2b5757 AZ |
504 | if (idl->state == IDL_S_MONITOR_REQUESTED) { |
505 | idl->state = IDL_S_MONITORING; | |
506 | ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE); | |
c383f3bf LS |
507 | } else { /* IDL_S_MONITOR_COND_REQUESTED. */ |
508 | idl->state = IDL_S_MONITORING_COND; | |
db2b5757 AZ |
509 | ovsdb_idl_parse_update(idl, msg->result, OVSDB_UPDATE2); |
510 | } | |
511 | ||
512 | /* Schema is not useful after monitor request is accepted | |
513 | * by the server. */ | |
514 | json_destroy(idl->schema); | |
515 | idl->schema = NULL; | |
d18e52e3 BP |
516 | break; |
517 | ||
c383f3bf | 518 | case IDL_S_MONITORING_COND: |
114ba2eb AZ |
519 | /* Conditional monitor clauses were updated. Send out |
520 | * the next condition changes, in any, immediately. */ | |
521 | ovsdb_idl_send_cond_change(idl); | |
46437c52 | 522 | idl->cond_seqno++; |
114ba2eb AZ |
523 | break; |
524 | ||
525 | case IDL_S_MONITORING: | |
6d882210 | 526 | case IDL_S_NO_SCHEMA: |
d18e52e3 BP |
527 | default: |
528 | OVS_NOT_REACHED(); | |
529 | } | |
db2b5757 | 530 | } else if (msg->type == JSONRPC_NOTIFY |
7152b6fa LS |
531 | && !strcmp(msg->method, "update") |
532 | && msg->params->type == JSON_ARRAY | |
533 | && msg->params->u.array.n == 2 | |
534 | && msg->params->u.array.elems[0]->type == JSON_STRING) { | |
db2b5757 AZ |
535 | /* Database contents changed. */ |
536 | ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1], | |
537 | OVSDB_UPDATE); | |
06b6d651 BP |
538 | } else if (msg->type == JSONRPC_REPLY |
539 | && idl->lock_request_id | |
540 | && json_equal(idl->lock_request_id, msg->id)) { | |
541 | /* Reply to our "lock" request. */ | |
542 | ovsdb_idl_parse_lock_reply(idl, msg->result); | |
543 | } else if (msg->type == JSONRPC_NOTIFY | |
544 | && !strcmp(msg->method, "locked")) { | |
545 | /* We got our lock. */ | |
546 | ovsdb_idl_parse_lock_notify(idl, msg->params, true); | |
547 | } else if (msg->type == JSONRPC_NOTIFY | |
548 | && !strcmp(msg->method, "stolen")) { | |
549 | /* Someone else stole our lock. */ | |
550 | ovsdb_idl_parse_lock_notify(idl, msg->params, false); | |
db2b5757 | 551 | } else if (msg->type == JSONRPC_ERROR |
c383f3bf | 552 | && idl->state == IDL_S_MONITOR_COND_REQUESTED |
db2b5757 AZ |
553 | && idl->request_id |
554 | && json_equal(idl->request_id, msg->id)) { | |
555 | if (msg->error && !strcmp(json_string(msg->error), | |
556 | "unknown method")) { | |
557 | /* Fall back to using "monitor" method. */ | |
558 | json_destroy(idl->request_id); | |
559 | idl->request_id = NULL; | |
560 | ovsdb_idl_send_monitor_request(idl); | |
561 | idl->state = IDL_S_MONITOR_REQUESTED; | |
562 | } | |
114ba2eb AZ |
563 | } else if (msg->type == JSONRPC_ERROR |
564 | && idl->state == IDL_S_MONITORING_COND | |
565 | && idl->request_id | |
566 | && json_equal(idl->request_id, msg->id)) { | |
567 | json_destroy(idl->request_id); | |
568 | idl->request_id = NULL; | |
569 | VLOG_ERR("%s: conditional monitor update failed", | |
570 | jsonrpc_session_get_name(idl->session)); | |
571 | idl->state = IDL_S_NO_SCHEMA; | |
8e7baed0 LR |
572 | } else if (msg->type == JSONRPC_ERROR |
573 | && idl->state == IDL_S_SCHEMA_REQUESTED | |
574 | && idl->request_id | |
575 | && json_equal(idl->request_id, msg->id)) { | |
114ba2eb AZ |
576 | json_destroy(idl->request_id); |
577 | idl->request_id = NULL; | |
578 | VLOG_ERR("%s: requested schema not found", | |
579 | jsonrpc_session_get_name(idl->session)); | |
580 | idl->state = IDL_S_NO_SCHEMA; | |
475281c0 BP |
581 | } else if ((msg->type == JSONRPC_ERROR |
582 | || msg->type == JSONRPC_REPLY) | |
583 | && ovsdb_idl_txn_process_reply(idl, msg)) { | |
584 | /* ovsdb_idl_txn_process_reply() did everything needful. */ | |
c3bb4bd7 | 585 | } else { |
c5a80c70 BP |
586 | /* This can happen if ovsdb_idl_txn_destroy() is called to destroy |
587 | * a transaction before we receive the reply, so keep the log level | |
588 | * low. */ | |
589 | VLOG_DBG("%s: received unexpected %s message", | |
590 | jsonrpc_session_get_name(idl->session), | |
591 | jsonrpc_msg_type_to_string(msg->type)); | |
c3bb4bd7 | 592 | } |
c3bb4bd7 BP |
593 | jsonrpc_msg_destroy(msg); |
594 | } | |
932104f4 | 595 | ovsdb_idl_row_destroy_postprocess(idl); |
c3bb4bd7 BP |
596 | } |
597 | ||
2ce42c88 BP |
598 | /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to |
599 | * do or when activity occurs on a transaction on 'idl'. */ | |
c3bb4bd7 BP |
600 | void |
601 | ovsdb_idl_wait(struct ovsdb_idl *idl) | |
602 | { | |
603 | jsonrpc_session_wait(idl->session); | |
604 | jsonrpc_session_recv_wait(idl->session); | |
605 | } | |
606 | ||
2f926787 BP |
607 | /* Returns a "sequence number" that represents the state of 'idl'. When |
608 | * ovsdb_idl_run() changes the database, the sequence number changes. The | |
609 | * initial fetch of the entire contents of the remote database is considered to | |
610 | * be one kind of change. Successfully acquiring a lock, if one has been | |
611 | * configured with ovsdb_idl_set_lock(), is also considered to be a change. | |
612 | * | |
613 | * As long as the sequence number does not change, the client may continue to | |
614 | * use any data structures it obtains from 'idl'. But when it changes, the | |
615 | * client must not access any of these data structures again, because they | |
616 | * could have freed or reused for other purposes. | |
617 | * | |
618 | * The sequence number can occasionally change even if the database does not. | |
619 | * This happens if the connection to the database drops and reconnects, which | |
620 | * causes the database contents to be reloaded even if they didn't change. (It | |
621 | * could also happen if the database server sends out a "change" that reflects | |
622 | * what the IDL already thought was in the database. The database server is | |
623 | * not supposed to do that, but bugs could in theory cause it to do so.) */ | |
c3bb4bd7 BP |
624 | unsigned int |
625 | ovsdb_idl_get_seqno(const struct ovsdb_idl *idl) | |
626 | { | |
627 | return idl->change_seqno; | |
628 | } | |
629 | ||
46437c52 AZ |
630 | /* Returns a "sequence number" that represents the number of conditional |
631 | * monitoring updates successfully received by the OVSDB server of an IDL | |
632 | * connection. | |
633 | * | |
634 | * ovsdb_idl_set_condition() sets a new condition that is different from | |
635 | * the current condtion, the next expected "sequence number" is returned. | |
636 | * | |
637 | * Whenever ovsdb_idl_get_cond_seqno() returns a value that matches | |
638 | * the return value of ovsdb_idl_set_condition(), The client is | |
639 | * assured that: | |
640 | * - The ovsdb_idl_set_condition() changes has been acknowledged by | |
641 | * the OVSDB sever. | |
642 | * | |
643 | * - 'idl' now contains the content matches the new conditions. */ | |
644 | unsigned int | |
645 | ovsdb_idl_get_condition_seqno(const struct ovsdb_idl *idl) | |
646 | { | |
647 | return idl->cond_seqno; | |
648 | } | |
649 | ||
2ce42c88 BP |
650 | /* Returns true if 'idl' successfully connected to the remote database and |
651 | * retrieved its contents (even if the connection subsequently dropped and is | |
652 | * in the process of reconnecting). If so, then 'idl' contains an atomic | |
653 | * snapshot of the database's contents (but it might be arbitrarily old if the | |
654 | * connection dropped). | |
655 | * | |
656 | * Returns false if 'idl' has never connected or retrieved the database's | |
657 | * contents. If so, 'idl' is empty. */ | |
f3d64521 JP |
658 | bool |
659 | ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl) | |
660 | { | |
661 | return ovsdb_idl_get_seqno(idl) != 0; | |
662 | } | |
663 | ||
705d7a39 AA |
664 | /* Reconfigures 'idl' so that it would reconnect to the database, if |
665 | * connection was dropped. */ | |
666 | void | |
667 | ovsdb_idl_enable_reconnect(struct ovsdb_idl *idl) | |
668 | { | |
669 | jsonrpc_session_enable_reconnect(idl->session); | |
670 | } | |
671 | ||
2ce42c88 BP |
672 | /* Forces 'idl' to drop its connection to the database and reconnect. In the |
673 | * meantime, the contents of 'idl' will not change. */ | |
c3bb4bd7 BP |
674 | void |
675 | ovsdb_idl_force_reconnect(struct ovsdb_idl *idl) | |
676 | { | |
677 | jsonrpc_session_force_reconnect(idl->session); | |
678 | } | |
8cdec725 EJ |
679 | |
680 | /* Some IDL users should only write to write-only columns. Furthermore, | |
681 | * writing to a column which is not write-only can cause serious performance | |
682 | * degradations for these users. This function causes 'idl' to reject writes | |
683 | * to columns which are not marked write only using ovsdb_idl_omit_alert(). */ | |
684 | void | |
685 | ovsdb_idl_verify_write_only(struct ovsdb_idl *idl) | |
686 | { | |
687 | idl->verify_write_only = true; | |
688 | } | |
fba6bd1d | 689 | |
6d882210 LR |
690 | /* Returns true if 'idl' is currently connected or trying to connect |
691 | * and a negative response to a schema request has not been received */ | |
fba6bd1d BP |
692 | bool |
693 | ovsdb_idl_is_alive(const struct ovsdb_idl *idl) | |
694 | { | |
6d882210 LR |
695 | return jsonrpc_session_is_alive(idl->session) && |
696 | idl->state != IDL_S_NO_SCHEMA; | |
fba6bd1d BP |
697 | } |
698 | ||
8d668224 | 699 | /* Returns the last error reported on a connection by 'idl'. The return value |
6d882210 LR |
700 | * is 0 only if no connection made by 'idl' has ever encountered an error and |
701 | * a negative response to a schema request has never been received. See | |
702 | * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value | |
703 | * interpretation. */ | |
fba6bd1d BP |
704 | int |
705 | ovsdb_idl_get_last_error(const struct ovsdb_idl *idl) | |
706 | { | |
6d882210 LR |
707 | int err; |
708 | ||
709 | err = jsonrpc_session_get_last_error(idl->session); | |
710 | ||
711 | if (err) { | |
712 | return err; | |
713 | } else if (idl->state == IDL_S_NO_SCHEMA) { | |
714 | return ENOENT; | |
715 | } else { | |
716 | return 0; | |
717 | } | |
fba6bd1d | 718 | } |
a4927e36 HL |
719 | |
720 | /* Sets the "probe interval" for 'idl->session' to 'probe_interval', in | |
721 | * milliseconds. | |
722 | */ | |
723 | void | |
724 | ovsdb_idl_set_probe_interval(const struct ovsdb_idl *idl, int probe_interval) | |
725 | { | |
726 | jsonrpc_session_set_probe_interval(idl->session, probe_interval); | |
727 | } | |
11990a52 BP |
728 | |
729 | static size_t | |
730 | find_uuid_in_array(const struct uuid *target, | |
731 | const struct uuid *array, size_t n) | |
732 | { | |
733 | for (size_t i = 0; i < n; i++) { | |
734 | if (uuid_equals(&array[i], target)) { | |
735 | return i; | |
736 | } | |
737 | } | |
738 | return SIZE_MAX; | |
739 | } | |
740 | ||
741 | static size_t | |
742 | array_contains_uuid(const struct uuid *target, | |
743 | const struct uuid *array, size_t n) | |
744 | { | |
745 | return find_uuid_in_array(target, array, n) != SIZE_MAX; | |
746 | } | |
747 | ||
748 | static bool | |
749 | remove_uuid_from_array(const struct uuid *target, | |
750 | struct uuid *array, size_t *n) | |
751 | { | |
752 | size_t i = find_uuid_in_array(target, array, *n); | |
753 | if (i != SIZE_MAX) { | |
754 | array[i] = array[--*n]; | |
755 | return true; | |
756 | } else { | |
757 | return false; | |
758 | } | |
759 | } | |
760 | ||
761 | static void | |
762 | add_row_references(const struct ovsdb_base_type *type, | |
763 | const union ovsdb_atom *atoms, size_t n_atoms, | |
764 | const struct uuid *exclude_uuid, | |
765 | struct uuid **dstsp, size_t *n_dstsp, | |
766 | size_t *allocated_dstsp) | |
767 | { | |
768 | if (type->type != OVSDB_TYPE_UUID || !type->u.uuid.refTableName) { | |
769 | return; | |
770 | } | |
771 | ||
772 | for (size_t i = 0; i < n_atoms; i++) { | |
773 | const struct uuid *uuid = &atoms[i].uuid; | |
774 | if (!uuid_equals(uuid, exclude_uuid) | |
775 | && !array_contains_uuid(uuid, *dstsp, *n_dstsp)) { | |
776 | if (*n_dstsp >= *allocated_dstsp) { | |
777 | *dstsp = x2nrealloc(*dstsp, allocated_dstsp, | |
778 | sizeof **dstsp); | |
779 | ||
780 | } | |
781 | (*dstsp)[*n_dstsp] = *uuid; | |
782 | ++*n_dstsp; | |
783 | } | |
784 | } | |
785 | } | |
786 | ||
787 | /* Checks for consistency in 'idl''s graph of arcs between database rows. Each | |
788 | * reference from one row to a different row should be reflected as a "struct | |
789 | * ovsdb_idl_arc" between those rows. | |
790 | * | |
791 | * This function is slow, big-O wise, and aborts if it finds an inconsistency, | |
792 | * thus it is only for use in test programs. */ | |
793 | void | |
794 | ovsdb_idl_check_consistency(const struct ovsdb_idl *idl) | |
795 | { | |
796 | /* Consistency is broken while a transaction is in progress. */ | |
797 | if (!idl->txn) { | |
798 | return; | |
799 | } | |
800 | ||
801 | bool ok = true; | |
802 | ||
803 | struct uuid *dsts = NULL; | |
804 | size_t allocated_dsts = 0; | |
805 | ||
3eb14233 | 806 | for (size_t i = 0; i < idl->class_->n_tables; i++) { |
11990a52 | 807 | const struct ovsdb_idl_table *table = &idl->tables[i]; |
3eb14233 | 808 | const struct ovsdb_idl_table_class *class = table->class_; |
11990a52 BP |
809 | |
810 | const struct ovsdb_idl_row *row; | |
811 | HMAP_FOR_EACH (row, hmap_node, &table->rows) { | |
812 | size_t n_dsts = 0; | |
fa50ab0b | 813 | if (row->new_datum) { |
11990a52 BP |
814 | size_t n_columns = shash_count(&row->table->columns); |
815 | for (size_t j = 0; j < n_columns; j++) { | |
816 | const struct ovsdb_type *type = &class->columns[j].type; | |
fa50ab0b | 817 | const struct ovsdb_datum *datum = &row->new_datum[j]; |
11990a52 BP |
818 | add_row_references(&type->key, |
819 | datum->keys, datum->n, &row->uuid, | |
820 | &dsts, &n_dsts, &allocated_dsts); | |
821 | add_row_references(&type->value, | |
822 | datum->values, datum->n, &row->uuid, | |
823 | &dsts, &n_dsts, &allocated_dsts); | |
824 | } | |
825 | } | |
826 | const struct ovsdb_idl_arc *arc; | |
827 | LIST_FOR_EACH (arc, src_node, &row->src_arcs) { | |
828 | if (!remove_uuid_from_array(&arc->dst->uuid, | |
829 | dsts, &n_dsts)) { | |
830 | VLOG_ERR("unexpected arc from %s row "UUID_FMT" to %s " | |
831 | "row "UUID_FMT, | |
3eb14233 | 832 | table->class_->name, |
11990a52 | 833 | UUID_ARGS(&row->uuid), |
3eb14233 | 834 | arc->dst->table->class_->name, |
11990a52 BP |
835 | UUID_ARGS(&arc->dst->uuid)); |
836 | ok = false; | |
837 | } | |
838 | } | |
71f21279 | 839 | for (size_t j = 0; j < n_dsts; j++) { |
11990a52 | 840 | VLOG_ERR("%s row "UUID_FMT" missing arc to row "UUID_FMT, |
3eb14233 | 841 | table->class_->name, UUID_ARGS(&row->uuid), |
71f21279 | 842 | UUID_ARGS(&dsts[j])); |
11990a52 BP |
843 | ok = false; |
844 | } | |
845 | } | |
846 | } | |
847 | free(dsts); | |
848 | ovs_assert(ok); | |
849 | } | |
ef73f86c | 850 | \f |
a0b02897 BP |
851 | const struct ovsdb_idl_class * |
852 | ovsdb_idl_get_class(const struct ovsdb_idl *idl) | |
c547535a | 853 | { |
3eb14233 | 854 | return idl->class_; |
a0b02897 | 855 | } |
c547535a | 856 | |
a0b02897 BP |
857 | /* Given 'column' in some table in 'class', returns the table's class. */ |
858 | const struct ovsdb_idl_table_class * | |
859 | ovsdb_idl_table_class_from_column(const struct ovsdb_idl_class *class, | |
860 | const struct ovsdb_idl_column *column) | |
861 | { | |
862 | for (size_t i = 0; i < class->n_tables; i++) { | |
863 | const struct ovsdb_idl_table_class *tc = &class->tables[i]; | |
c547535a | 864 | if (column >= tc->columns && column < &tc->columns[tc->n_columns]) { |
a0b02897 | 865 | return tc; |
c547535a BP |
866 | } |
867 | } | |
868 | ||
428b2edd | 869 | OVS_NOT_REACHED(); |
c547535a BP |
870 | } |
871 | ||
a0b02897 BP |
872 | /* Given 'column' in some table in 'idl', returns the table. */ |
873 | static struct ovsdb_idl_table * | |
874 | ovsdb_idl_table_from_column(struct ovsdb_idl *idl, | |
875 | const struct ovsdb_idl_column *column) | |
876 | { | |
877 | const struct ovsdb_idl_table_class *tc = | |
3eb14233 JS |
878 | ovsdb_idl_table_class_from_column(idl->class_, column); |
879 | return &idl->tables[tc - idl->class_->tables]; | |
a0b02897 BP |
880 | } |
881 | ||
882 | static unsigned char * | |
883 | ovsdb_idl_get_mode(struct ovsdb_idl *idl, | |
884 | const struct ovsdb_idl_column *column) | |
885 | { | |
886 | ovs_assert(!idl->change_seqno); | |
887 | ||
888 | const struct ovsdb_idl_table *table = ovsdb_idl_table_from_column(idl, | |
889 | column); | |
3eb14233 | 890 | return &table->modes[column - table->class_->columns]; |
a0b02897 BP |
891 | } |
892 | ||
ef73f86c BP |
893 | static void |
894 | add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base) | |
895 | { | |
896 | if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { | |
897 | struct ovsdb_idl_table *table; | |
898 | ||
899 | table = shash_find_data(&idl->table_by_name, | |
900 | base->u.uuid.refTableName); | |
901 | if (table) { | |
902 | table->need_table = true; | |
903 | } else { | |
904 | VLOG_WARN("%s IDL class missing referenced table %s", | |
3eb14233 | 905 | idl->class_->database, base->u.uuid.refTableName); |
ef73f86c BP |
906 | } |
907 | } | |
908 | } | |
909 | ||
910 | /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also | |
911 | * ensures that any tables referenced by 'column' will be replicated, even if | |
912 | * no columns in that table are selected for replication (see | |
913 | * ovsdb_idl_add_table() for more information). | |
c547535a | 914 | * |
ef73f86c BP |
915 | * This function is only useful if 'monitor_everything_by_default' was false in |
916 | * the call to ovsdb_idl_create(). This function should be called between | |
917 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
918 | */ | |
919 | void | |
920 | ovsdb_idl_add_column(struct ovsdb_idl *idl, | |
921 | const struct ovsdb_idl_column *column) | |
922 | { | |
923 | *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT; | |
924 | add_ref_table(idl, &column->type.key); | |
925 | add_ref_table(idl, &column->type.value); | |
926 | } | |
927 | ||
928 | /* Ensures that the table with class 'tc' will be replicated on 'idl' even if | |
cd26c1dc AE |
929 | * no columns are selected for replication. Just the necessary data for table |
930 | * references will be replicated (the UUID of the rows, for instance), any | |
931 | * columns not selected for replication will remain unreplicated. | |
932 | * This can be useful because it allows 'idl' to keep track of what rows in the | |
933 | * table actually exist, which in turn allows columns that reference the table | |
934 | * to have accurate contents. (The IDL presents the database with references to | |
935 | * rows that do not exist removed.) | |
c547535a | 936 | * |
ef73f86c BP |
937 | * This function is only useful if 'monitor_everything_by_default' was false in |
938 | * the call to ovsdb_idl_create(). This function should be called between | |
939 | * ovsdb_idl_create() and the first call to ovsdb_idl_run(). | |
940 | */ | |
941 | void | |
942 | ovsdb_idl_add_table(struct ovsdb_idl *idl, | |
943 | const struct ovsdb_idl_table_class *tc) | |
944 | { | |
945 | size_t i; | |
946 | ||
3eb14233 | 947 | for (i = 0; i < idl->class_->n_tables; i++) { |
ef73f86c BP |
948 | struct ovsdb_idl_table *table = &idl->tables[i]; |
949 | ||
3eb14233 | 950 | if (table->class_ == tc) { |
ef73f86c BP |
951 | table->need_table = true; |
952 | return; | |
953 | } | |
954 | } | |
955 | ||
428b2edd | 956 | OVS_NOT_REACHED(); |
ef73f86c | 957 | } |
239fa5bb | 958 | \f |
0164e367 | 959 | /* A single clause within an ovsdb_idl_condition. */ |
239fa5bb | 960 | struct ovsdb_idl_clause { |
0164e367 BP |
961 | struct hmap_node hmap_node; /* In struct ovsdb_idl_condition. */ |
962 | enum ovsdb_function function; /* Never OVSDB_F_TRUE or OVSDB_F_FALSE. */ | |
963 | const struct ovsdb_idl_column *column; /* Must be nonnull. */ | |
964 | struct ovsdb_datum arg; /* Has ovsdb_type ->column->type. */ | |
239fa5bb | 965 | }; |
ef73f86c | 966 | |
0164e367 BP |
967 | static uint32_t |
968 | ovsdb_idl_clause_hash(const struct ovsdb_idl_clause *clause) | |
969 | { | |
970 | uint32_t hash = hash_pointer(clause->column, clause->function); | |
971 | return ovsdb_datum_hash(&clause->arg, &clause->column->type, hash); | |
972 | } | |
973 | ||
974 | static int | |
975 | ovsdb_idl_clause_equals(const struct ovsdb_idl_clause *a, | |
976 | const struct ovsdb_idl_clause *b) | |
977 | { | |
978 | return (a->function == b->function | |
979 | && a->column == b->column | |
980 | && ovsdb_datum_equals(&a->arg, &b->arg, &a->column->type)); | |
981 | } | |
982 | ||
16ebb90e LS |
983 | static struct json * |
984 | ovsdb_idl_clause_to_json(const struct ovsdb_idl_clause *clause) | |
985 | { | |
0164e367 BP |
986 | const char *function = ovsdb_function_to_string(clause->function); |
987 | return json_array_create_3(json_string_create(clause->column->name), | |
988 | json_string_create(function), | |
989 | ovsdb_datum_to_json(&clause->arg, | |
990 | &clause->column->type)); | |
16ebb90e LS |
991 | } |
992 | ||
993 | static void | |
0164e367 | 994 | ovsdb_idl_clause_destroy(struct ovsdb_idl_clause *clause) |
16ebb90e | 995 | { |
0164e367 | 996 | if (clause) { |
16ebb90e | 997 | ovsdb_datum_destroy(&clause->arg, &clause->column->type); |
0164e367 | 998 | free(clause); |
16ebb90e | 999 | } |
0164e367 BP |
1000 | } |
1001 | \f | |
1002 | /* ovsdb_idl_condition. */ | |
16ebb90e | 1003 | |
0164e367 BP |
1004 | void |
1005 | ovsdb_idl_condition_init(struct ovsdb_idl_condition *cnd) | |
1006 | { | |
1007 | hmap_init(&cnd->clauses); | |
1008 | cnd->is_true = false; | |
16ebb90e LS |
1009 | } |
1010 | ||
1011 | void | |
0164e367 | 1012 | ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cond) |
16ebb90e | 1013 | { |
0164e367 BP |
1014 | if (cond) { |
1015 | ovsdb_idl_condition_clear(cond); | |
1016 | hmap_destroy(&cond->clauses); | |
1017 | } | |
1018 | } | |
16ebb90e | 1019 | |
0164e367 BP |
1020 | void |
1021 | ovsdb_idl_condition_clear(struct ovsdb_idl_condition *cond) | |
1022 | { | |
1023 | struct ovsdb_idl_clause *clause, *next; | |
1024 | HMAP_FOR_EACH_SAFE (clause, next, hmap_node, &cond->clauses) { | |
1025 | hmap_remove(&cond->clauses, &clause->hmap_node); | |
1026 | ovsdb_idl_clause_destroy(clause); | |
16ebb90e | 1027 | } |
0164e367 | 1028 | cond->is_true = false; |
16ebb90e LS |
1029 | } |
1030 | ||
0164e367 BP |
1031 | bool |
1032 | ovsdb_idl_condition_is_true(const struct ovsdb_idl_condition *condition) | |
16ebb90e | 1033 | { |
0164e367 | 1034 | return condition->is_true; |
16ebb90e LS |
1035 | } |
1036 | ||
239fa5bb | 1037 | static struct ovsdb_idl_clause * |
0164e367 BP |
1038 | ovsdb_idl_condition_find_clause(const struct ovsdb_idl_condition *condition, |
1039 | const struct ovsdb_idl_clause *target, | |
1040 | uint32_t hash) | |
16ebb90e | 1041 | { |
0164e367 BP |
1042 | struct ovsdb_idl_clause *clause; |
1043 | HMAP_FOR_EACH_WITH_HASH (clause, hmap_node, hash, &condition->clauses) { | |
1044 | if (ovsdb_idl_clause_equals(clause, target)) { | |
1045 | return clause; | |
16ebb90e LS |
1046 | } |
1047 | } | |
239fa5bb BP |
1048 | return NULL; |
1049 | } | |
1050 | ||
0164e367 BP |
1051 | static void |
1052 | ovsdb_idl_condition_add_clause__(struct ovsdb_idl_condition *condition, | |
1053 | const struct ovsdb_idl_clause *src, | |
1054 | uint32_t hash) | |
1055 | { | |
1056 | struct ovsdb_idl_clause *clause = xmalloc(sizeof *clause); | |
1057 | clause->function = src->function; | |
1058 | clause->column = src->column; | |
1059 | ovsdb_datum_clone(&clause->arg, &src->arg, &src->column->type); | |
1060 | hmap_insert(&condition->clauses, &clause->hmap_node, hash); | |
1061 | } | |
1062 | ||
239fa5bb BP |
1063 | /* Adds a clause to the condition for replicating the table with class 'tc' in |
1064 | * 'idl'. | |
1065 | * | |
0164e367 BP |
1066 | * The IDL replicates only rows in a table that satisfy at least one clause in |
1067 | * the table's condition. The default condition for a table has a single | |
1068 | * clause with function OVSDB_F_TRUE, so that the IDL replicates all rows in | |
1069 | * the table. When the IDL client replaces the default condition by one of its | |
1070 | * own, the condition can have any number of clauses. If it has no conditions, | |
1071 | * then no rows are replicated. | |
239fa5bb | 1072 | * |
0164e367 | 1073 | * Two distinct of clauses can usefully be added: |
239fa5bb | 1074 | * |
0164e367 BP |
1075 | * - A 'function' of OVSDB_F_TRUE. A "true" clause causes every row to be |
1076 | * replicated, regardless of whether other clauses exist. 'column' and | |
1077 | * 'arg' are ignored. | |
239fa5bb | 1078 | * |
0164e367 BP |
1079 | * - Binary 'functions' add a clause of the form "<column> <function> |
1080 | * <arg>", e.g. "column == 5" or "column <= 10". In this case, 'arg' must | |
1081 | * have a type that is compatible with 'column'. | |
239fa5bb BP |
1082 | */ |
1083 | void | |
0164e367 | 1084 | ovsdb_idl_condition_add_clause(struct ovsdb_idl_condition *condition, |
239fa5bb BP |
1085 | enum ovsdb_function function, |
1086 | const struct ovsdb_idl_column *column, | |
1087 | const struct ovsdb_datum *arg) | |
1088 | { | |
0164e367 BP |
1089 | if (condition->is_true) { |
1090 | /* Adding a clause to an always-true condition has no effect. */ | |
1091 | } else if (function == OVSDB_F_TRUE) { | |
1092 | ovsdb_idl_condition_add_clause_true(condition); | |
1093 | } else if (function == OVSDB_F_FALSE) { | |
1094 | /* Adding a "false" clause never has any effect. */ | |
1095 | } else { | |
1096 | struct ovsdb_idl_clause clause = { | |
1097 | .function = function, | |
1098 | .column = column, | |
1099 | .arg = *arg, | |
1100 | }; | |
1101 | uint32_t hash = ovsdb_idl_clause_hash(&clause); | |
1102 | if (!ovsdb_idl_condition_find_clause(condition, &clause, hash)) { | |
1103 | ovsdb_idl_condition_add_clause__(condition, &clause, hash); | |
1104 | } | |
1105 | } | |
1106 | } | |
239fa5bb | 1107 | |
0164e367 BP |
1108 | void |
1109 | ovsdb_idl_condition_add_clause_true(struct ovsdb_idl_condition *condition) | |
1110 | { | |
1111 | if (!condition->is_true) { | |
1112 | ovsdb_idl_condition_clear(condition); | |
1113 | condition->is_true = true; | |
1114 | } | |
1115 | } | |
1116 | ||
1117 | static bool | |
1118 | ovsdb_idl_condition_equals(const struct ovsdb_idl_condition *a, | |
1119 | const struct ovsdb_idl_condition *b) | |
1120 | { | |
1121 | if (hmap_count(&a->clauses) != hmap_count(&b->clauses)) { | |
1122 | return false; | |
1123 | } | |
1124 | if (a->is_true != b->is_true) { | |
1125 | return false; | |
239fa5bb | 1126 | } |
16ebb90e | 1127 | |
0164e367 BP |
1128 | const struct ovsdb_idl_clause *clause; |
1129 | HMAP_FOR_EACH (clause, hmap_node, &a->clauses) { | |
1130 | if (!ovsdb_idl_condition_find_clause(b, clause, | |
1131 | clause->hmap_node.hash)) { | |
1132 | return false; | |
1133 | } | |
1134 | } | |
1135 | return true; | |
16ebb90e LS |
1136 | } |
1137 | ||
0164e367 BP |
1138 | static void |
1139 | ovsdb_idl_condition_clone(struct ovsdb_idl_condition *dst, | |
1140 | const struct ovsdb_idl_condition *src) | |
1141 | { | |
1142 | ovsdb_idl_condition_init(dst); | |
1143 | ||
1144 | dst->is_true = src->is_true; | |
1145 | ||
1146 | const struct ovsdb_idl_clause *clause; | |
1147 | HMAP_FOR_EACH (clause, hmap_node, &src->clauses) { | |
1148 | ovsdb_idl_condition_add_clause__(dst, clause, clause->hmap_node.hash); | |
1149 | } | |
1150 | } | |
1151 | ||
46437c52 AZ |
1152 | /* Sets the replication condition for 'tc' in 'idl' to 'condition' and |
1153 | * arranges to send the new condition to the database server. | |
1154 | * | |
f5a12b82 BP |
1155 | * Return the next conditional update sequence number. When this |
1156 | * value and ovsdb_idl_get_condition_seqno() matches, the 'idl' | |
1157 | * contains rows that match the 'condition'. */ | |
46437c52 | 1158 | unsigned int |
0164e367 BP |
1159 | ovsdb_idl_set_condition(struct ovsdb_idl *idl, |
1160 | const struct ovsdb_idl_table_class *tc, | |
1161 | const struct ovsdb_idl_condition *condition) | |
16ebb90e | 1162 | { |
16ebb90e | 1163 | struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, tc); |
46437c52 | 1164 | unsigned int seqno = idl->cond_seqno; |
0164e367 BP |
1165 | if (!ovsdb_idl_condition_equals(condition, &table->condition)) { |
1166 | ovsdb_idl_condition_destroy(&table->condition); | |
1167 | ovsdb_idl_condition_clone(&table->condition, condition); | |
239fa5bb BP |
1168 | idl->cond_changed = table->cond_changed = true; |
1169 | poll_immediate_wake(); | |
46437c52 | 1170 | return seqno + 1; |
16ebb90e | 1171 | } |
46437c52 AZ |
1172 | |
1173 | return seqno; | |
16ebb90e LS |
1174 | } |
1175 | ||
1176 | static struct json * | |
1177 | ovsdb_idl_condition_to_json(const struct ovsdb_idl_condition *cnd) | |
1178 | { | |
0164e367 BP |
1179 | if (cnd->is_true) { |
1180 | return json_array_create_empty(); | |
1181 | } | |
16ebb90e | 1182 | |
0164e367 BP |
1183 | size_t n = hmap_count(&cnd->clauses); |
1184 | if (!n) { | |
1185 | return json_array_create_1(json_boolean_create(false)); | |
16ebb90e LS |
1186 | } |
1187 | ||
0164e367 BP |
1188 | struct json **clauses = xmalloc(n * sizeof *clauses); |
1189 | const struct ovsdb_idl_clause *clause; | |
1190 | size_t i = 0; | |
1191 | HMAP_FOR_EACH (clause, hmap_node, &cnd->clauses) { | |
1192 | clauses[i++] = ovsdb_idl_clause_to_json(clause); | |
1193 | } | |
1194 | ovs_assert(i == n); | |
1195 | return json_array_create(clauses, n); | |
16ebb90e | 1196 | } |
0164e367 | 1197 | \f |
239fa5bb | 1198 | static struct json * |
16ebb90e LS |
1199 | ovsdb_idl_create_cond_change_req(struct ovsdb_idl_table *table) |
1200 | { | |
1201 | const struct ovsdb_idl_condition *cond = &table->condition; | |
1202 | struct json *monitor_cond_change_request = json_object_create(); | |
1203 | struct json *cond_json = ovsdb_idl_condition_to_json(cond); | |
1204 | ||
1205 | json_object_put(monitor_cond_change_request, "where", cond_json); | |
1206 | ||
1207 | return monitor_cond_change_request; | |
1208 | } | |
1209 | ||
1210 | static void | |
1211 | ovsdb_idl_send_cond_change(struct ovsdb_idl *idl) | |
1212 | { | |
1213 | int i; | |
1214 | char uuid[UUID_LEN + 1]; | |
1215 | struct json *params, *json_uuid; | |
1216 | struct jsonrpc_msg *request; | |
1217 | ||
114ba2eb AZ |
1218 | /* When 'idl-request_id' is not NULL, there is an outstanding |
1219 | * conditional monitoring update request that we have not heard | |
1220 | * from the server yet. Don't generate another request in this case. */ | |
16ebb90e | 1221 | if (!idl->cond_changed || !jsonrpc_session_is_connected(idl->session) || |
114ba2eb | 1222 | idl->state != IDL_S_MONITORING_COND || idl->request_id) { |
16ebb90e LS |
1223 | return; |
1224 | } | |
1225 | ||
1226 | struct json *monitor_cond_change_requests = NULL; | |
1227 | ||
3eb14233 | 1228 | for (i = 0; i < idl->class_->n_tables; i++) { |
16ebb90e LS |
1229 | struct ovsdb_idl_table *table = &idl->tables[i]; |
1230 | ||
1231 | if (table->cond_changed) { | |
1232 | struct json *req = ovsdb_idl_create_cond_change_req(table); | |
1233 | if (req) { | |
1234 | if (!monitor_cond_change_requests) { | |
1235 | monitor_cond_change_requests = json_object_create(); | |
1236 | } | |
1237 | json_object_put(monitor_cond_change_requests, | |
3eb14233 | 1238 | table->class_->name, |
16ebb90e LS |
1239 | json_array_create_1(req)); |
1240 | } | |
1241 | table->cond_changed = false; | |
1242 | } | |
1243 | } | |
1244 | ||
1245 | /* Send request if not empty. */ | |
1246 | if (monitor_cond_change_requests) { | |
1247 | snprintf(uuid, sizeof uuid, UUID_FMT, | |
1248 | UUID_ARGS(&idl->uuid)); | |
1249 | json_uuid = json_string_create(uuid); | |
1250 | ||
1251 | /* Create a new uuid */ | |
1252 | uuid_generate(&idl->uuid); | |
1253 | snprintf(uuid, sizeof uuid, UUID_FMT, | |
1254 | UUID_ARGS(&idl->uuid)); | |
1255 | params = json_array_create_3(json_uuid, json_string_create(uuid), | |
1256 | monitor_cond_change_requests); | |
1257 | ||
114ba2eb AZ |
1258 | request = jsonrpc_create_request("monitor_cond_change", params, |
1259 | &idl->request_id); | |
16ebb90e LS |
1260 | jsonrpc_session_send(idl->session, request); |
1261 | } | |
1262 | idl->cond_changed = false; | |
1263 | } | |
1264 | ||
ef73f86c | 1265 | /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'. |
c547535a | 1266 | * |
ef73f86c BP |
1267 | * This function should be called between ovsdb_idl_create() and the first call |
1268 | * to ovsdb_idl_run(). | |
1269 | */ | |
c547535a | 1270 | void |
ef73f86c BP |
1271 | ovsdb_idl_omit_alert(struct ovsdb_idl *idl, |
1272 | const struct ovsdb_idl_column *column) | |
c547535a | 1273 | { |
ef73f86c | 1274 | *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_ALERT; |
c547535a BP |
1275 | } |
1276 | ||
ef73f86c BP |
1277 | /* Sets the mode for 'column' in 'idl' to 0. See the big comment above |
1278 | * OVSDB_IDL_MONITOR for details. | |
c547535a | 1279 | * |
ef73f86c BP |
1280 | * This function should be called between ovsdb_idl_create() and the first call |
1281 | * to ovsdb_idl_run(). | |
1282 | */ | |
c547535a BP |
1283 | void |
1284 | ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) | |
1285 | { | |
ef73f86c | 1286 | *ovsdb_idl_get_mode(idl, column) = 0; |
c547535a | 1287 | } |
932104f4 SA |
1288 | |
1289 | /* Returns the most recent IDL change sequence number that caused a | |
1290 | * insert, modify or delete update to the table with class 'table_class'. | |
1291 | */ | |
1292 | unsigned int | |
1293 | ovsdb_idl_table_get_seqno(const struct ovsdb_idl *idl, | |
1294 | const struct ovsdb_idl_table_class *table_class) | |
1295 | { | |
1296 | struct ovsdb_idl_table *table | |
1297 | = ovsdb_idl_table_from_class(idl, table_class); | |
1298 | unsigned int max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_INSERT]; | |
1299 | ||
1300 | if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]) { | |
1301 | max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]; | |
1302 | } | |
1303 | if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_DELETE]) { | |
1304 | max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_DELETE]; | |
1305 | } | |
1306 | return max_seqno; | |
1307 | } | |
1308 | ||
1309 | /* For each row that contains tracked columns, IDL stores the most | |
1310 | * recent IDL change sequence numbers associateed with insert, modify | |
1311 | * and delete updates to the table. | |
1312 | */ | |
1313 | unsigned int | |
1314 | ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row *row, | |
1315 | enum ovsdb_idl_change change) | |
1316 | { | |
1317 | return row->change_seqno[change]; | |
1318 | } | |
1319 | ||
1320 | /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that | |
1321 | * all rows whose 'column' is modified are traced. Similarly, insert | |
1322 | * or delete of rows having 'column' are tracked. Clients are able | |
1323 | * to retrive the tracked rows with the ovsdb_idl_track_get_*() | |
1324 | * functions. | |
1325 | * | |
1326 | * This function should be called between ovsdb_idl_create() and | |
1327 | * the first call to ovsdb_idl_run(). The column to be tracked | |
1328 | * should have OVSDB_IDL_ALERT turned on. | |
1329 | */ | |
1330 | void | |
1331 | ovsdb_idl_track_add_column(struct ovsdb_idl *idl, | |
1332 | const struct ovsdb_idl_column *column) | |
1333 | { | |
1334 | if (!(*ovsdb_idl_get_mode(idl, column) & OVSDB_IDL_ALERT)) { | |
1335 | ovsdb_idl_add_column(idl, column); | |
1336 | } | |
1337 | *ovsdb_idl_get_mode(idl, column) |= OVSDB_IDL_TRACK; | |
1338 | } | |
1339 | ||
1340 | void | |
1341 | ovsdb_idl_track_add_all(struct ovsdb_idl *idl) | |
1342 | { | |
1343 | size_t i, j; | |
1344 | ||
3eb14233 JS |
1345 | for (i = 0; i < idl->class_->n_tables; i++) { |
1346 | const struct ovsdb_idl_table_class *tc = &idl->class_->tables[i]; | |
932104f4 SA |
1347 | |
1348 | for (j = 0; j < tc->n_columns; j++) { | |
1349 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
1350 | ovsdb_idl_track_add_column(idl, column); | |
1351 | } | |
1352 | } | |
1353 | } | |
1354 | ||
1355 | /* Returns true if 'table' has any tracked column. */ | |
1356 | static bool | |
1357 | ovsdb_idl_track_is_set(struct ovsdb_idl_table *table) | |
1358 | { | |
1359 | size_t i; | |
1360 | ||
3eb14233 | 1361 | for (i = 0; i < table->class_->n_columns; i++) { |
932104f4 SA |
1362 | if (table->modes[i] & OVSDB_IDL_TRACK) { |
1363 | return true; | |
1364 | } | |
1365 | } | |
1366 | return false; | |
1367 | } | |
1368 | ||
1369 | /* Returns the first tracked row in table with class 'table_class' | |
1370 | * for the specified 'idl'. Returns NULL if there are no tracked rows */ | |
1371 | const struct ovsdb_idl_row * | |
1372 | ovsdb_idl_track_get_first(const struct ovsdb_idl *idl, | |
1373 | const struct ovsdb_idl_table_class *table_class) | |
1374 | { | |
1375 | struct ovsdb_idl_table *table | |
1376 | = ovsdb_idl_table_from_class(idl, table_class); | |
1377 | ||
417e7e66 BW |
1378 | if (!ovs_list_is_empty(&table->track_list)) { |
1379 | return CONTAINER_OF(ovs_list_front(&table->track_list), struct ovsdb_idl_row, track_node); | |
932104f4 SA |
1380 | } |
1381 | return NULL; | |
1382 | } | |
1383 | ||
1384 | /* Returns the next tracked row in table after the specified 'row' | |
1385 | * (in no particular order). Returns NULL if there are no tracked rows */ | |
1386 | const struct ovsdb_idl_row * | |
1387 | ovsdb_idl_track_get_next(const struct ovsdb_idl_row *row) | |
1388 | { | |
1389 | if (row->track_node.next != &row->table->track_list) { | |
1390 | return CONTAINER_OF(row->track_node.next, struct ovsdb_idl_row, track_node); | |
1391 | } | |
1392 | ||
1393 | return NULL; | |
1394 | } | |
1395 | ||
32d37ce8 SA |
1396 | /* Returns true if a tracked 'column' in 'row' was updated by IDL, false |
1397 | * otherwise. The tracking data is cleared by ovsdb_idl_track_clear() | |
1398 | * | |
1399 | * Function returns false if 'column' is not tracked (see | |
1400 | * ovsdb_idl_track_add_column()). | |
1401 | */ | |
1402 | bool | |
1403 | ovsdb_idl_track_is_updated(const struct ovsdb_idl_row *row, | |
1404 | const struct ovsdb_idl_column *column) | |
1405 | { | |
1406 | const struct ovsdb_idl_table_class *class; | |
1407 | size_t column_idx; | |
1408 | ||
3eb14233 | 1409 | class = row->table->class_; |
32d37ce8 SA |
1410 | column_idx = column - class->columns; |
1411 | ||
1412 | if (row->updated && bitmap_is_set(row->updated, column_idx)) { | |
1413 | return true; | |
1414 | } else { | |
1415 | return false; | |
1416 | } | |
1417 | } | |
1418 | ||
932104f4 SA |
1419 | /* Flushes the tracked rows. Client calls this function after calling |
1420 | * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*() | |
1421 | * functions. This is usually done at the end of the client's processing | |
1422 | * loop when it is ready to do ovsdb_idl_run() again. | |
1423 | */ | |
1424 | void | |
1425 | ovsdb_idl_track_clear(const struct ovsdb_idl *idl) | |
1426 | { | |
1427 | size_t i; | |
1428 | ||
3eb14233 | 1429 | for (i = 0; i < idl->class_->n_tables; i++) { |
932104f4 SA |
1430 | struct ovsdb_idl_table *table = &idl->tables[i]; |
1431 | ||
417e7e66 | 1432 | if (!ovs_list_is_empty(&table->track_list)) { |
932104f4 SA |
1433 | struct ovsdb_idl_row *row, *next; |
1434 | ||
1435 | LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) { | |
32d37ce8 SA |
1436 | if (row->updated) { |
1437 | free(row->updated); | |
1438 | row->updated = NULL; | |
1439 | } | |
417e7e66 BW |
1440 | ovs_list_remove(&row->track_node); |
1441 | ovs_list_init(&row->track_node); | |
932104f4 SA |
1442 | if (ovsdb_idl_row_is_orphan(row)) { |
1443 | ovsdb_idl_row_clear_old(row); | |
1444 | free(row); | |
1445 | } | |
1446 | } | |
1447 | } | |
1448 | } | |
1449 | } | |
1450 | ||
c3bb4bd7 BP |
1451 | \f |
1452 | static void | |
d18e52e3 BP |
1453 | ovsdb_idl_send_schema_request(struct ovsdb_idl *idl) |
1454 | { | |
1455 | struct jsonrpc_msg *msg; | |
1456 | ||
1457 | json_destroy(idl->request_id); | |
1458 | msg = jsonrpc_create_request( | |
1459 | "get_schema", | |
3eb14233 | 1460 | json_array_create_1(json_string_create(idl->class_->database)), |
d18e52e3 BP |
1461 | &idl->request_id); |
1462 | jsonrpc_session_send(idl->session, msg); | |
1463 | } | |
1464 | ||
1465 | static void | |
1466 | log_error(struct ovsdb_error *error) | |
1467 | { | |
1468 | char *s = ovsdb_error_to_string(error); | |
1469 | VLOG_WARN("error parsing database schema: %s", s); | |
1470 | free(s); | |
1471 | ovsdb_error_destroy(error); | |
1472 | } | |
1473 | ||
1474 | /* Frees 'schema', which is in the format returned by parse_schema(). */ | |
1475 | static void | |
1476 | free_schema(struct shash *schema) | |
1477 | { | |
1478 | if (schema) { | |
1479 | struct shash_node *node, *next; | |
1480 | ||
1481 | SHASH_FOR_EACH_SAFE (node, next, schema) { | |
1482 | struct sset *sset = node->data; | |
1483 | sset_destroy(sset); | |
1484 | free(sset); | |
1485 | shash_delete(schema, node); | |
1486 | } | |
1487 | shash_destroy(schema); | |
1488 | free(schema); | |
1489 | } | |
1490 | } | |
1491 | ||
1492 | /* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC | |
1493 | * 7047, to obtain the names of its rows and columns. If successful, returns | |
1494 | * an shash whose keys are table names and whose values are ssets, where each | |
1495 | * sset contains the names of its table's columns. On failure (due to a parse | |
1496 | * error), returns NULL. | |
1497 | * | |
1498 | * It would also be possible to use the general-purpose OVSDB schema parser in | |
1499 | * ovsdb-server, but that's overkill, possibly too strict for the current use | |
1500 | * case, and would require restructuring ovsdb-server to separate the schema | |
1501 | * code from the rest. */ | |
1502 | static struct shash * | |
1503 | parse_schema(const struct json *schema_json) | |
1504 | { | |
1505 | struct ovsdb_parser parser; | |
1506 | const struct json *tables_json; | |
1507 | struct ovsdb_error *error; | |
1508 | struct shash_node *node; | |
1509 | struct shash *schema; | |
1510 | ||
1511 | ovsdb_parser_init(&parser, schema_json, "database schema"); | |
1512 | tables_json = ovsdb_parser_member(&parser, "tables", OP_OBJECT); | |
1513 | error = ovsdb_parser_destroy(&parser); | |
1514 | if (error) { | |
1515 | log_error(error); | |
1516 | return NULL; | |
1517 | } | |
1518 | ||
1519 | schema = xmalloc(sizeof *schema); | |
1520 | shash_init(schema); | |
1521 | SHASH_FOR_EACH (node, json_object(tables_json)) { | |
1522 | const char *table_name = node->name; | |
1523 | const struct json *json = node->data; | |
1524 | const struct json *columns_json; | |
1525 | ||
1526 | ovsdb_parser_init(&parser, json, "table schema for table %s", | |
1527 | table_name); | |
1528 | columns_json = ovsdb_parser_member(&parser, "columns", OP_OBJECT); | |
1529 | error = ovsdb_parser_destroy(&parser); | |
1530 | if (error) { | |
1531 | log_error(error); | |
1532 | free_schema(schema); | |
1533 | return NULL; | |
1534 | } | |
1535 | ||
1536 | struct sset *columns = xmalloc(sizeof *columns); | |
1537 | sset_init(columns); | |
1538 | ||
1539 | struct shash_node *node2; | |
1540 | SHASH_FOR_EACH (node2, json_object(columns_json)) { | |
1541 | const char *column_name = node2->name; | |
1542 | sset_add(columns, column_name); | |
1543 | } | |
1544 | shash_add(schema, table_name, columns); | |
1545 | } | |
1546 | return schema; | |
1547 | } | |
1548 | ||
1549 | static void | |
db2b5757 AZ |
1550 | ovsdb_idl_send_monitor_request__(struct ovsdb_idl *idl, |
1551 | const char *method) | |
c3bb4bd7 | 1552 | { |
db2b5757 | 1553 | struct shash *schema; |
c3bb4bd7 | 1554 | struct json *monitor_requests; |
c3bb4bd7 | 1555 | struct jsonrpc_msg *msg; |
7152b6fa | 1556 | char uuid[UUID_LEN + 1]; |
115f1e4d | 1557 | size_t i; |
c3bb4bd7 | 1558 | |
db2b5757 | 1559 | schema = parse_schema(idl->schema); |
c3bb4bd7 | 1560 | monitor_requests = json_object_create(); |
3eb14233 | 1561 | for (i = 0; i < idl->class_->n_tables; i++) { |
16ebb90e | 1562 | struct ovsdb_idl_table *table = &idl->tables[i]; |
3eb14233 | 1563 | const struct ovsdb_idl_table_class *tc = table->class_; |
16ebb90e | 1564 | struct json *monitor_request, *columns, *where; |
d18e52e3 | 1565 | const struct sset *table_schema; |
2a022368 | 1566 | size_t j; |
c3bb4bd7 | 1567 | |
d18e52e3 | 1568 | table_schema = (schema |
3eb14233 | 1569 | ? shash_find_data(schema, table->class_->name) |
d18e52e3 BP |
1570 | : NULL); |
1571 | ||
ef73f86c | 1572 | columns = table->need_table ? json_array_create_empty() : NULL; |
2a022368 BP |
1573 | for (j = 0; j < tc->n_columns; j++) { |
1574 | const struct ovsdb_idl_column *column = &tc->columns[j]; | |
ef73f86c | 1575 | if (table->modes[j] & OVSDB_IDL_MONITOR) { |
d18e52e3 BP |
1576 | if (table_schema |
1577 | && !sset_contains(table_schema, column->name)) { | |
1578 | VLOG_WARN("%s table in %s database lacks %s column " | |
1579 | "(database needs upgrade?)", | |
3eb14233 | 1580 | table->class_->name, idl->class_->database, |
d18e52e3 BP |
1581 | column->name); |
1582 | continue; | |
1583 | } | |
ef73f86c BP |
1584 | if (!columns) { |
1585 | columns = json_array_create_empty(); | |
1586 | } | |
c547535a BP |
1587 | json_array_add(columns, json_string_create(column->name)); |
1588 | } | |
c3bb4bd7 | 1589 | } |
ef73f86c BP |
1590 | |
1591 | if (columns) { | |
d18e52e3 BP |
1592 | if (schema && !table_schema) { |
1593 | VLOG_WARN("%s database lacks %s table " | |
1594 | "(database needs upgrade?)", | |
3eb14233 | 1595 | idl->class_->database, table->class_->name); |
d18e52e3 BP |
1596 | json_destroy(columns); |
1597 | continue; | |
1598 | } | |
1599 | ||
ef73f86c BP |
1600 | monitor_request = json_object_create(); |
1601 | json_object_put(monitor_request, "columns", columns); | |
0164e367 BP |
1602 | if (!strcmp(method, "monitor_cond") |
1603 | && !ovsdb_idl_condition_is_true(&table->condition)) { | |
16ebb90e LS |
1604 | where = ovsdb_idl_condition_to_json(&table->condition); |
1605 | json_object_put(monitor_request, "where", where); | |
1606 | table->cond_changed = false; | |
1607 | } | |
ef73f86c BP |
1608 | json_object_put(monitor_requests, tc->name, monitor_request); |
1609 | } | |
c3bb4bd7 | 1610 | } |
d18e52e3 | 1611 | free_schema(schema); |
c3bb4bd7 | 1612 | |
d18e52e3 | 1613 | json_destroy(idl->request_id); |
7152b6fa LS |
1614 | |
1615 | snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&idl->uuid)); | |
c3bb4bd7 | 1616 | msg = jsonrpc_create_request( |
db2b5757 | 1617 | method, |
3eb14233 | 1618 | json_array_create_3(json_string_create(idl->class_->database), |
7152b6fa | 1619 | json_string_create(uuid), monitor_requests), |
d18e52e3 | 1620 | &idl->request_id); |
c3bb4bd7 | 1621 | jsonrpc_session_send(idl->session, msg); |
16ebb90e | 1622 | idl->cond_changed = false; |
c3bb4bd7 BP |
1623 | } |
1624 | ||
1625 | static void | |
db2b5757 AZ |
1626 | ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl) |
1627 | { | |
1628 | ovsdb_idl_send_monitor_request__(idl, "monitor"); | |
1629 | } | |
1630 | ||
1631 | static void | |
1632 | log_parse_update_error(struct ovsdb_error *error) | |
c3bb4bd7 | 1633 | { |
c3bb4bd7 BP |
1634 | if (!VLOG_DROP_WARN(&syntax_rl)) { |
1635 | char *s = ovsdb_error_to_string(error); | |
1636 | VLOG_WARN_RL(&syntax_rl, "%s", s); | |
1637 | free(s); | |
1638 | } | |
1639 | ovsdb_error_destroy(error); | |
db2b5757 AZ |
1640 | } |
1641 | ||
1642 | static void | |
c383f3bf | 1643 | ovsdb_idl_send_monitor_cond_request(struct ovsdb_idl *idl) |
db2b5757 | 1644 | { |
c383f3bf | 1645 | ovsdb_idl_send_monitor_request__(idl, "monitor_cond"); |
db2b5757 AZ |
1646 | } |
1647 | ||
1648 | static void | |
1649 | ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates, | |
1650 | enum ovsdb_update_version version) | |
1651 | { | |
1652 | struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates, | |
1653 | version); | |
1654 | if (error) { | |
1655 | log_parse_update_error(error); | |
c3bb4bd7 BP |
1656 | } |
1657 | } | |
1658 | ||
1659 | static struct ovsdb_error * | |
1660 | ovsdb_idl_parse_update__(struct ovsdb_idl *idl, | |
db2b5757 AZ |
1661 | const struct json *table_updates, |
1662 | enum ovsdb_update_version version) | |
c3bb4bd7 BP |
1663 | { |
1664 | const struct shash_node *tables_node; | |
db2b5757 AZ |
1665 | const char *table_updates_name = table_updates_names[version]; |
1666 | const char *table_update_name = table_update_names[version]; | |
1667 | const char *row_update_name = row_update_names[version]; | |
c3bb4bd7 BP |
1668 | |
1669 | if (table_updates->type != JSON_OBJECT) { | |
1670 | return ovsdb_syntax_error(table_updates, NULL, | |
db2b5757 AZ |
1671 | "<%s> is not an object", |
1672 | table_updates_name); | |
c3bb4bd7 | 1673 | } |
db2b5757 | 1674 | |
c3bb4bd7 BP |
1675 | SHASH_FOR_EACH (tables_node, json_object(table_updates)) { |
1676 | const struct json *table_update = tables_node->data; | |
1677 | const struct shash_node *table_node; | |
1678 | struct ovsdb_idl_table *table; | |
1679 | ||
115f1e4d | 1680 | table = shash_find_data(&idl->table_by_name, tables_node->name); |
c3bb4bd7 BP |
1681 | if (!table) { |
1682 | return ovsdb_syntax_error( | |
1683 | table_updates, NULL, | |
db2b5757 AZ |
1684 | "<%s> includes unknown table \"%s\"", |
1685 | table_updates_name, | |
c3bb4bd7 BP |
1686 | tables_node->name); |
1687 | } | |
1688 | ||
1689 | if (table_update->type != JSON_OBJECT) { | |
1690 | return ovsdb_syntax_error(table_update, NULL, | |
db2b5757 AZ |
1691 | "<%s> for table \"%s\" is " |
1692 | "not an object", | |
1693 | table_update_name, | |
3eb14233 | 1694 | table->class_->name); |
c3bb4bd7 BP |
1695 | } |
1696 | SHASH_FOR_EACH (table_node, json_object(table_update)) { | |
1697 | const struct json *row_update = table_node->data; | |
1698 | const struct json *old_json, *new_json; | |
1699 | struct uuid uuid; | |
1700 | ||
1701 | if (!uuid_from_string(&uuid, table_node->name)) { | |
1702 | return ovsdb_syntax_error(table_update, NULL, | |
db2b5757 | 1703 | "<%s> for table \"%s\" " |
c3bb4bd7 BP |
1704 | "contains bad UUID " |
1705 | "\"%s\" as member name", | |
db2b5757 | 1706 | table_update_name, |
3eb14233 | 1707 | table->class_->name, |
c3bb4bd7 BP |
1708 | table_node->name); |
1709 | } | |
1710 | if (row_update->type != JSON_OBJECT) { | |
1711 | return ovsdb_syntax_error(row_update, NULL, | |
db2b5757 AZ |
1712 | "<%s> for table \"%s\" " |
1713 | "contains <%s> for %s that " | |
c3bb4bd7 | 1714 | "is not an object", |
db2b5757 | 1715 | table_update_name, |
3eb14233 | 1716 | table->class_->name, |
db2b5757 | 1717 | row_update_name, |
c3bb4bd7 BP |
1718 | table_node->name); |
1719 | } | |
1720 | ||
db2b5757 AZ |
1721 | switch(version) { |
1722 | case OVSDB_UPDATE: | |
1723 | old_json = shash_find_data(json_object(row_update), "old"); | |
1724 | new_json = shash_find_data(json_object(row_update), "new"); | |
1725 | if (old_json && old_json->type != JSON_OBJECT) { | |
1726 | return ovsdb_syntax_error(old_json, NULL, | |
1727 | "\"old\" <row> is not object"); | |
1728 | } else if (new_json && new_json->type != JSON_OBJECT) { | |
1729 | return ovsdb_syntax_error(new_json, NULL, | |
1730 | "\"new\" <row> is not object"); | |
1731 | } else if ((old_json != NULL) + (new_json != NULL) | |
1732 | != shash_count(json_object(row_update))) { | |
1733 | return ovsdb_syntax_error(row_update, NULL, | |
1734 | "<row-update> contains " | |
1735 | "unexpected member"); | |
1736 | } else if (!old_json && !new_json) { | |
1737 | return ovsdb_syntax_error(row_update, NULL, | |
1738 | "<row-update> missing \"old\" " | |
1739 | "and \"new\" members"); | |
1740 | } | |
1741 | ||
1742 | if (ovsdb_idl_process_update(table, &uuid, old_json, | |
1743 | new_json)) { | |
1744 | idl->change_seqno++; | |
1745 | } | |
1746 | break; | |
1747 | ||
1748 | case OVSDB_UPDATE2: { | |
1749 | const char *ops[] = {"modify", "insert", "delete", "initial"}; | |
1750 | const char *operation; | |
1751 | const struct json *row; | |
1752 | int i; | |
1753 | ||
1754 | for (i = 0; i < ARRAY_SIZE(ops); i++) { | |
1755 | operation = ops[i]; | |
1756 | row = shash_find_data(json_object(row_update), operation); | |
1757 | ||
1758 | if (row) { | |
1759 | if (ovsdb_idl_process_update2(table, &uuid, operation, | |
1760 | row)) { | |
1761 | idl->change_seqno++; | |
1762 | } | |
1763 | break; | |
1764 | } | |
1765 | } | |
1766 | ||
1767 | /* row_update2 should contain one of the objects */ | |
1768 | if (i == ARRAY_SIZE(ops)) { | |
1769 | return ovsdb_syntax_error(row_update, NULL, | |
1770 | "<row_update2> includes unknown " | |
1771 | "object"); | |
1772 | } | |
1773 | break; | |
c3bb4bd7 BP |
1774 | } |
1775 | ||
db2b5757 AZ |
1776 | default: |
1777 | OVS_NOT_REACHED(); | |
c547535a | 1778 | } |
c3bb4bd7 BP |
1779 | } |
1780 | } | |
1781 | ||
1782 | return NULL; | |
1783 | } | |
1784 | ||
1785 | static struct ovsdb_idl_row * | |
1786 | ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
1787 | { | |
1788 | struct ovsdb_idl_row *row; | |
1789 | ||
4e8e4213 | 1790 | HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { |
c3bb4bd7 BP |
1791 | if (uuid_equals(&row->uuid, uuid)) { |
1792 | return row; | |
1793 | } | |
1794 | } | |
1795 | return NULL; | |
1796 | } | |
1797 | ||
c547535a BP |
1798 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
1799 | * otherwise. */ | |
1800 | static bool | |
c3bb4bd7 BP |
1801 | ovsdb_idl_process_update(struct ovsdb_idl_table *table, |
1802 | const struct uuid *uuid, const struct json *old, | |
1803 | const struct json *new) | |
1804 | { | |
1805 | struct ovsdb_idl_row *row; | |
1806 | ||
1807 | row = ovsdb_idl_get_row(table, uuid); | |
1808 | if (!new) { | |
1809 | /* Delete row. */ | |
1810 | if (row && !ovsdb_idl_row_is_orphan(row)) { | |
1811 | /* XXX perhaps we should check the 'old' values? */ | |
1812 | ovsdb_idl_delete_row(row); | |
1813 | } else { | |
1814 | VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " | |
1815 | "from table %s", | |
3eb14233 | 1816 | UUID_ARGS(uuid), table->class_->name); |
c547535a | 1817 | return false; |
c3bb4bd7 BP |
1818 | } |
1819 | } else if (!old) { | |
1820 | /* Insert row. */ | |
1821 | if (!row) { | |
1822 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); | |
1823 | } else if (ovsdb_idl_row_is_orphan(row)) { | |
1824 | ovsdb_idl_insert_row(row, new); | |
1825 | } else { | |
1826 | VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " | |
3eb14233 | 1827 | "table %s", UUID_ARGS(uuid), table->class_->name); |
c547535a | 1828 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
1829 | } |
1830 | } else { | |
1831 | /* Modify row. */ | |
1832 | if (row) { | |
1833 | /* XXX perhaps we should check the 'old' values? */ | |
1834 | if (!ovsdb_idl_row_is_orphan(row)) { | |
c547535a | 1835 | return ovsdb_idl_modify_row(row, new); |
c3bb4bd7 BP |
1836 | } else { |
1837 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing but " | |
1838 | "referenced row "UUID_FMT" in table %s", | |
3eb14233 | 1839 | UUID_ARGS(uuid), table->class_->name); |
c3bb4bd7 BP |
1840 | ovsdb_idl_insert_row(row, new); |
1841 | } | |
1842 | } else { | |
1843 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " | |
3eb14233 | 1844 | "in table %s", UUID_ARGS(uuid), table->class_->name); |
c3bb4bd7 BP |
1845 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); |
1846 | } | |
1847 | } | |
c547535a BP |
1848 | |
1849 | return true; | |
c3bb4bd7 BP |
1850 | } |
1851 | ||
c547535a BP |
1852 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
1853 | * otherwise. */ | |
1854 | static bool | |
db2b5757 AZ |
1855 | ovsdb_idl_process_update2(struct ovsdb_idl_table *table, |
1856 | const struct uuid *uuid, | |
1857 | const char *operation, | |
1858 | const struct json *json_row) | |
1859 | { | |
1860 | struct ovsdb_idl_row *row; | |
1861 | ||
1862 | row = ovsdb_idl_get_row(table, uuid); | |
1863 | if (!strcmp(operation, "delete")) { | |
1864 | /* Delete row. */ | |
1865 | if (row && !ovsdb_idl_row_is_orphan(row)) { | |
1866 | ovsdb_idl_delete_row(row); | |
1867 | } else { | |
1868 | VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " | |
1869 | "from table %s", | |
3eb14233 | 1870 | UUID_ARGS(uuid), table->class_->name); |
db2b5757 AZ |
1871 | return false; |
1872 | } | |
1873 | } else if (!strcmp(operation, "insert") || !strcmp(operation, "initial")) { | |
1874 | /* Insert row. */ | |
1875 | if (!row) { | |
1876 | ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), json_row); | |
1877 | } else if (ovsdb_idl_row_is_orphan(row)) { | |
1878 | ovsdb_idl_insert_row(row, json_row); | |
1879 | } else { | |
1880 | VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " | |
3eb14233 | 1881 | "table %s", UUID_ARGS(uuid), table->class_->name); |
db2b5757 AZ |
1882 | ovsdb_idl_delete_row(row); |
1883 | ovsdb_idl_insert_row(row, json_row); | |
1884 | } | |
1885 | } else if (!strcmp(operation, "modify")) { | |
1886 | /* Modify row. */ | |
1887 | if (row) { | |
1888 | if (!ovsdb_idl_row_is_orphan(row)) { | |
1889 | return ovsdb_idl_modify_row_by_diff(row, json_row); | |
1890 | } else { | |
1891 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing but " | |
1892 | "referenced row "UUID_FMT" in table %s", | |
3eb14233 | 1893 | UUID_ARGS(uuid), table->class_->name); |
db2b5757 AZ |
1894 | return false; |
1895 | } | |
1896 | } else { | |
1897 | VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " | |
3eb14233 | 1898 | "in table %s", UUID_ARGS(uuid), table->class_->name); |
db2b5757 AZ |
1899 | return false; |
1900 | } | |
1901 | } else { | |
1902 | VLOG_WARN_RL(&semantic_rl, "unknown operation %s to " | |
3eb14233 | 1903 | "table %s", operation, table->class_->name); |
db2b5757 AZ |
1904 | return false; |
1905 | } | |
1906 | ||
1907 | return true; | |
1908 | } | |
1909 | ||
db2b5757 AZ |
1910 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
1911 | * otherwise. | |
1912 | * | |
1913 | * Change 'row' either with the content of 'row_json' or by apply 'diff'. | |
1914 | * Caller needs to provide either valid 'row_json' or 'diff', but not | |
1915 | * both. */ | |
1916 | static bool | |
1917 | ovsdb_idl_row_change__(struct ovsdb_idl_row *row, const struct json *row_json, | |
1918 | const struct json *diff_json, | |
1919 | enum ovsdb_idl_change change) | |
1920 | { | |
1921 | struct ovsdb_idl_table *table = row->table; | |
3eb14233 | 1922 | const struct ovsdb_idl_table_class *class = table->class_; |
db2b5757 AZ |
1923 | struct shash_node *node; |
1924 | bool changed = false; | |
1925 | bool apply_diff = diff_json != NULL; | |
1926 | const struct json *json = apply_diff ? diff_json : row_json; | |
e85bbd75 | 1927 | |
db2b5757 AZ |
1928 | SHASH_FOR_EACH (node, json_object(json)) { |
1929 | const char *column_name = node->name; | |
1930 | const struct ovsdb_idl_column *column; | |
1931 | struct ovsdb_datum datum; | |
1932 | struct ovsdb_error *error; | |
1933 | unsigned int column_idx; | |
1934 | struct ovsdb_datum *old; | |
1935 | ||
1936 | column = shash_find_data(&table->columns, column_name); | |
1937 | if (!column) { | |
1938 | VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT, | |
1939 | column_name, UUID_ARGS(&row->uuid)); | |
1940 | continue; | |
1941 | } | |
1942 | ||
3eb14233 | 1943 | column_idx = column - table->class_->columns; |
4e0b4acc | 1944 | old = &row->old_datum[column_idx]; |
db2b5757 AZ |
1945 | |
1946 | error = NULL; | |
1947 | if (apply_diff) { | |
1948 | struct ovsdb_datum diff; | |
1949 | ||
1950 | ovs_assert(!row_json); | |
1951 | error = ovsdb_transient_datum_from_json(&diff, &column->type, | |
1952 | node->data); | |
1953 | if (!error) { | |
1954 | error = ovsdb_datum_apply_diff(&datum, old, &diff, | |
1955 | &column->type); | |
1956 | ovsdb_datum_destroy(&diff, &column->type); | |
1957 | } | |
1958 | } else { | |
1959 | ovs_assert(!diff_json); | |
1960 | error = ovsdb_datum_from_json(&datum, &column->type, node->data, | |
1961 | NULL); | |
1962 | } | |
1963 | ||
1964 | if (!error) { | |
e85bbd75 BP |
1965 | if (!ovsdb_datum_equals(old, &datum, &column->type)) { |
1966 | ovsdb_datum_swap(old, &datum); | |
ef73f86c | 1967 | if (table->modes[column_idx] & OVSDB_IDL_ALERT) { |
e85bbd75 | 1968 | changed = true; |
932104f4 SA |
1969 | row->change_seqno[change] |
1970 | = row->table->change_seqno[change] | |
1971 | = row->table->idl->change_seqno + 1; | |
1972 | if (table->modes[column_idx] & OVSDB_IDL_TRACK) { | |
417e7e66 BW |
1973 | if (!ovs_list_is_empty(&row->track_node)) { |
1974 | ovs_list_remove(&row->track_node); | |
932104f4 | 1975 | } |
417e7e66 | 1976 | ovs_list_push_back(&row->table->track_list, |
c88b940e | 1977 | &row->track_node); |
32d37ce8 SA |
1978 | if (!row->updated) { |
1979 | row->updated = bitmap_allocate(class->n_columns); | |
1980 | } | |
1981 | bitmap_set1(row->updated, column_idx); | |
932104f4 | 1982 | } |
e85bbd75 BP |
1983 | } |
1984 | } else { | |
1985 | /* Didn't really change but the OVSDB monitor protocol always | |
1986 | * includes every value in a row. */ | |
c547535a | 1987 | } |
e85bbd75 BP |
1988 | |
1989 | ovsdb_datum_destroy(&datum, &column->type); | |
c3bb4bd7 BP |
1990 | } else { |
1991 | char *s = ovsdb_error_to_string(error); | |
1992 | VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT | |
1993 | " in table %s: %s", column_name, | |
3eb14233 | 1994 | UUID_ARGS(&row->uuid), table->class_->name, s); |
c3bb4bd7 BP |
1995 | free(s); |
1996 | ovsdb_error_destroy(error); | |
1997 | } | |
1998 | } | |
c547535a | 1999 | return changed; |
c3bb4bd7 BP |
2000 | } |
2001 | ||
db2b5757 AZ |
2002 | static bool |
2003 | ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json, | |
2004 | enum ovsdb_idl_change change) | |
2005 | { | |
2006 | return ovsdb_idl_row_change__(row, row_json, NULL, change); | |
2007 | } | |
2008 | ||
2009 | static bool | |
2010 | ovsdb_idl_row_apply_diff(struct ovsdb_idl_row *row, | |
2011 | const struct json *diff_json, | |
2012 | enum ovsdb_idl_change change) | |
2013 | { | |
2014 | return ovsdb_idl_row_change__(row, NULL, diff_json, change); | |
2015 | } | |
2016 | ||
90887925 BP |
2017 | /* When a row A refers to row B through a column with a "refTable" constraint, |
2018 | * but row B does not exist, row B is called an "orphan row". Orphan rows | |
2019 | * should not persist, because the database enforces referential integrity, but | |
2020 | * they can appear transiently as changes from the database are received (the | |
2021 | * database doesn't try to topologically sort them and circular references mean | |
2022 | * it isn't always possible anyhow). | |
2023 | * | |
2024 | * This function returns true if 'row' is an orphan row, otherwise false. | |
2025 | */ | |
c3bb4bd7 BP |
2026 | static bool |
2027 | ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) | |
2028 | { | |
4e0b4acc | 2029 | return !row->old_datum && !row->new_datum; |
90887925 BP |
2030 | } |
2031 | ||
f2ba3c0a BP |
2032 | /* Returns true if 'row' is conceptually part of the database as modified by |
2033 | * the current transaction (if any), false otherwise. | |
2034 | * | |
2035 | * This function will return true if 'row' is not an orphan (see the comment on | |
2036 | * ovsdb_idl_row_is_orphan()) and: | |
2037 | * | |
2038 | * - 'row' exists in the database and has not been deleted within the | |
2039 | * current transaction (if any). | |
2040 | * | |
2041 | * - 'row' was inserted within the current transaction and has not been | |
2042 | * deleted. (In the latter case you should not have passed 'row' in at | |
2043 | * all, because ovsdb_idl_txn_delete() freed it.) | |
2044 | * | |
2045 | * This function will return false if 'row' is an orphan or if 'row' was | |
2046 | * deleted within the current transaction. | |
2047 | */ | |
2048 | static bool | |
2049 | ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) | |
2050 | { | |
fa50ab0b | 2051 | return row->new_datum != NULL; |
c3bb4bd7 BP |
2052 | } |
2053 | ||
979821c0 BP |
2054 | static void |
2055 | ovsdb_idl_row_parse(struct ovsdb_idl_row *row) | |
2056 | { | |
3eb14233 | 2057 | const struct ovsdb_idl_table_class *class = row->table->class_; |
979821c0 BP |
2058 | size_t i; |
2059 | ||
2060 | for (i = 0; i < class->n_columns; i++) { | |
2061 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
4e0b4acc | 2062 | (c->parse)(row, &row->old_datum[i]); |
979821c0 BP |
2063 | } |
2064 | } | |
2065 | ||
2066 | static void | |
2067 | ovsdb_idl_row_unparse(struct ovsdb_idl_row *row) | |
2068 | { | |
3eb14233 | 2069 | const struct ovsdb_idl_table_class *class = row->table->class_; |
979821c0 BP |
2070 | size_t i; |
2071 | ||
2072 | for (i = 0; i < class->n_columns; i++) { | |
2073 | const struct ovsdb_idl_column *c = &class->columns[i]; | |
2074 | (c->unparse)(row); | |
2075 | } | |
2076 | } | |
93fe0264 LR |
2077 | \f |
2078 | /* The OVSDB-IDL Compound Indexes feature allows for the creation of custom | |
2079 | * table indexes over one or more columns in the IDL. These indexes provide | |
2080 | * the ability to retrieve rows matching a particular search criteria and to | |
2081 | * iterate over a subset of rows in a defined order. | |
2082 | */ | |
2083 | ||
2084 | /* Creates a new index with the provided name, attached to the given idl and | |
2085 | * table. Note that all indexes must be created and indexing columns added | |
2086 | * before the first call to ovsdb_idl_run() is made. | |
2087 | */ | |
2088 | struct ovsdb_idl_index * | |
2089 | ovsdb_idl_create_index(struct ovsdb_idl *idl, | |
2090 | const struct ovsdb_idl_table_class *tc, | |
2091 | const char *index_name) | |
2092 | { | |
2093 | struct ovsdb_idl_index *index; | |
2094 | size_t i; | |
2095 | ||
3eb14233 | 2096 | for (i = 0; i < idl->class_->n_tables; i++) { |
93fe0264 LR |
2097 | struct ovsdb_idl_table *table = &idl->tables[i]; |
2098 | ||
3eb14233 | 2099 | if (table->class_ == tc) { |
93fe0264 LR |
2100 | index = ovsdb_idl_create_index_(table, 1); |
2101 | if (!shash_add_once(&table->indexes, index_name, index)) { | |
2102 | VLOG_ERR("Duplicate index name '%s' in table %s", | |
3eb14233 | 2103 | index_name, table->class_->name); |
93fe0264 LR |
2104 | return NULL; |
2105 | } | |
2106 | index->index_name = index_name; | |
2107 | return index; | |
2108 | } | |
2109 | } | |
2110 | OVS_NOT_REACHED(); | |
2111 | return NULL; | |
2112 | } | |
2113 | ||
2114 | /* Generic comparator that can compare each index, using the custom | |
2115 | * configuration (an struct ovsdb_idl_index) passed to it. | |
2116 | * Not intended for direct usage. | |
2117 | */ | |
2118 | static int | |
2119 | ovsdb_idl_index_generic_comparer(const void *a, | |
2120 | const void *b, const void *conf) | |
2121 | { | |
2122 | const struct ovsdb_idl_column *column; | |
2123 | const struct ovsdb_idl_index *index; | |
2124 | size_t i; | |
2125 | ||
2126 | index = CONST_CAST(struct ovsdb_idl_index *, conf); | |
2127 | ||
2128 | if (a == b) { | |
2129 | return 0; | |
2130 | } | |
2131 | ||
2132 | for (i = 0; i < index->n_columns; i++) { | |
2133 | int val; | |
2134 | if (index->columns[i].comparer) { | |
2135 | val = index->columns[i].comparer(a, b); | |
2136 | } else { | |
2137 | column = index->columns[i].column; | |
2138 | const struct ovsdb_idl_row *row_a, *row_b; | |
2139 | row_a = CONST_CAST(struct ovsdb_idl_row *, a); | |
2140 | row_b = CONST_CAST(struct ovsdb_idl_row *, b); | |
2141 | const struct ovsdb_datum *datum_a, *datum_b; | |
2142 | datum_a = ovsdb_idl_read(row_a, column); | |
2143 | datum_b = ovsdb_idl_read(row_b, column); | |
2144 | val = ovsdb_datum_compare_3way(datum_a, datum_b, &column->type); | |
2145 | } | |
2146 | ||
2147 | if (val) { | |
2148 | return val * index->columns[i].sorting_order; | |
2149 | } | |
2150 | } | |
2151 | ||
2152 | /* If ins_del is true then a row is being inserted into or deleted from | |
2153 | * the index list. In this case, we augment the search key with | |
2154 | * additional values (row UUID and memory address) to create a unique | |
2155 | * search key in order to locate the correct entry efficiently and to | |
2156 | * ensure that the correct entry is deleted in the case of a "delete" | |
2157 | * operation. | |
2158 | */ | |
2159 | if (index->ins_del) { | |
2160 | const struct ovsdb_idl_row *row_a, *row_b; | |
2161 | ||
2162 | row_a = (const struct ovsdb_idl_row *) a; | |
2163 | row_b = (const struct ovsdb_idl_row *) b; | |
2164 | int value = uuid_compare_3way(&row_a->uuid, &row_b->uuid); | |
2165 | ||
2166 | return value ? value : (a < b) - (a > b); | |
2167 | } else { | |
2168 | return 0; | |
2169 | } | |
2170 | } | |
2171 | ||
2172 | static struct ovsdb_idl_index * | |
2173 | ovsdb_idl_create_index_(const struct ovsdb_idl_table *table, | |
2174 | size_t allocated_cols) | |
2175 | { | |
2176 | struct ovsdb_idl_index *index; | |
2177 | ||
2178 | index = xmalloc(sizeof (struct ovsdb_idl_index)); | |
2179 | index->n_columns = 0; | |
2180 | index->alloc_columns = allocated_cols; | |
2181 | index->skiplist = skiplist_create(ovsdb_idl_index_generic_comparer, index); | |
2182 | index->columns = xmalloc(allocated_cols * | |
2183 | sizeof (struct ovsdb_idl_index_column)); | |
2184 | index->ins_del = false; | |
2185 | index->table = table; | |
2186 | return index; | |
2187 | } | |
2188 | ||
2189 | static void | |
2190 | ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table) | |
2191 | { | |
2192 | struct ovsdb_idl_index *index; | |
2193 | struct shash_node *node; | |
2194 | ||
2195 | SHASH_FOR_EACH (node, &(table->indexes)) { | |
2196 | index = node->data; | |
2197 | skiplist_destroy(index->skiplist, NULL); | |
2198 | free(index->columns); | |
2199 | } | |
17b85cd9 | 2200 | shash_destroy_free_data(&table->indexes); |
93fe0264 LR |
2201 | } |
2202 | ||
2203 | static void | |
2204 | ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *row) | |
2205 | { | |
2206 | struct ovsdb_idl_table *table = row->table; | |
2207 | struct ovsdb_idl_index *index; | |
2208 | struct shash_node *node; | |
2209 | ||
2210 | SHASH_FOR_EACH (node, &(table->indexes)) { | |
2211 | index = node->data; | |
2212 | index->ins_del = true; | |
2213 | skiplist_insert(index->skiplist, row); | |
2214 | index->ins_del = false; | |
2215 | } | |
2216 | } | |
2217 | ||
2218 | static void | |
2219 | ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *row) | |
2220 | { | |
2221 | struct ovsdb_idl_table *table = row->table; | |
2222 | struct ovsdb_idl_index *index; | |
2223 | struct shash_node *node; | |
2224 | ||
2225 | SHASH_FOR_EACH (node, &(table->indexes)) { | |
2226 | index = node->data; | |
2227 | index->ins_del = true; | |
2228 | skiplist_delete(index->skiplist, row); | |
2229 | index->ins_del = false; | |
2230 | } | |
2231 | } | |
2232 | ||
2233 | /* Adds a column to an existing index (note that columns can only be added to | |
2234 | * an index before the first call to ovsdb_idl_run()). The 'order' parameter | |
2235 | * specifies whether the sort order should be ascending (OVSDB_INDEX_ASC) or | |
2236 | * descending (OVSDB_INDEX_DESC). The 'custom_comparer' parameter, if non-NULL, | |
2237 | * contains a pointer to a custom comparison function. A default comparison | |
2238 | * function is used if a custom comparison function is not provided (the | |
2239 | * default comparison function can only be used for columns of type string, | |
2240 | * uuid, integer, real, or boolean). | |
2241 | */ | |
2242 | void | |
2243 | ovsdb_idl_index_add_column(struct ovsdb_idl_index *index, | |
2244 | const struct ovsdb_idl_column *column, | |
2245 | int order, column_comparator *custom_comparer) | |
2246 | { | |
2247 | /* Check that the column or table is tracked */ | |
2248 | if (!index->table->need_table && | |
2249 | !((OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT) & | |
2250 | *ovsdb_idl_get_mode(index->table->idl, column))) { | |
2251 | VLOG_ERR("Can't add unmonitored column '%s' at index '%s' in " | |
2252 | "table '%s'.", | |
3eb14233 | 2253 | column->name, index->index_name, index->table->class_->name); |
93fe0264 LR |
2254 | } |
2255 | if (!ovsdb_type_is_scalar(&column->type) && !custom_comparer) { | |
2256 | VLOG_WARN("Comparing non-scalar values."); | |
2257 | } | |
2258 | ||
2259 | /* Allocate more memory for column configuration */ | |
2260 | if (index->n_columns == index->alloc_columns) { | |
2261 | index->alloc_columns++; | |
2262 | index->columns = xrealloc(index->columns, | |
2263 | index->alloc_columns * | |
2264 | sizeof(struct ovsdb_idl_index_column)); | |
2265 | } | |
2266 | ||
2267 | /* Append column to index */ | |
2268 | int i = index->n_columns; | |
2269 | ||
2270 | index->columns[i].column = column; | |
2271 | index->columns[i].comparer = custom_comparer ? custom_comparer : NULL; | |
2272 | if (order == OVSDB_INDEX_ASC) { | |
2273 | index->columns[i].sorting_order = OVSDB_INDEX_ASC; | |
2274 | } else { | |
2275 | index->columns[i].sorting_order = OVSDB_INDEX_DESC; | |
2276 | } | |
2277 | index->n_columns++; | |
2278 | } | |
2279 | ||
2280 | bool | |
2281 | ovsdb_idl_initialize_cursor(struct ovsdb_idl *idl, | |
2282 | const struct ovsdb_idl_table_class *tc, | |
2283 | const char *index_name, | |
2284 | struct ovsdb_idl_index_cursor *cursor) | |
2285 | { | |
2286 | size_t i; | |
2287 | ||
3eb14233 | 2288 | for (i = 0; i < idl->class_->n_tables; i++) { |
93fe0264 LR |
2289 | struct ovsdb_idl_table *table = &idl->tables[i]; |
2290 | ||
3eb14233 | 2291 | if (table->class_ == tc) { |
93fe0264 LR |
2292 | struct shash_node *node = shash_find(&table->indexes, index_name); |
2293 | ||
2294 | if (!node || !node->data) { | |
2295 | VLOG_ERR("Cursor initialization failed, " | |
2296 | "index %s at table %s does not exist.", | |
2297 | index_name, tc->name); | |
2298 | cursor->index = NULL; | |
2299 | cursor->position = NULL; | |
2300 | return false; | |
2301 | } | |
2302 | cursor->index = node->data; | |
2303 | cursor->position = skiplist_first(cursor->index->skiplist); | |
2304 | return true; | |
2305 | } | |
2306 | } | |
2307 | VLOG_ERR("Cursor initialization failed, " | |
2308 | "index %s at table %s does not exist.", index_name, tc->name); | |
2309 | return false; | |
2310 | } | |
2311 | ||
2312 | /* ovsdb_idl_index_write_ writes a datum in an ovsdb_idl_row, | |
2313 | * and updates the corresponding field in the table record. | |
2314 | * Not intended for direct usage. | |
2315 | */ | |
2316 | void | |
2317 | ovsdb_idl_index_write_(struct ovsdb_idl_row *const_row, | |
2318 | const struct ovsdb_idl_column *column, | |
2319 | struct ovsdb_datum *datum, | |
2320 | const struct ovsdb_idl_table_class *class) | |
2321 | { | |
2322 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, const_row); | |
2323 | size_t column_idx = column - class->columns; | |
2324 | ||
2325 | if (bitmap_is_set(row->written, column_idx)) { | |
fa50ab0b JS |
2326 | free(row->new_datum[column_idx].values); |
2327 | free(row->new_datum[column_idx].keys); | |
93fe0264 LR |
2328 | } else { |
2329 | bitmap_set1(row->written, column_idx); | |
2330 | } | |
fa50ab0b | 2331 | row->new_datum[column_idx] = *datum; |
93fe0264 | 2332 | (column->unparse)(row); |
fa50ab0b | 2333 | (column->parse)(row, &row->new_datum[column_idx]); |
93fe0264 LR |
2334 | } |
2335 | ||
3cc1634f HZ |
2336 | /* Magic UUID for index rows */ |
2337 | static const struct uuid index_row_uuid = { | |
2338 | .parts = {0xdeadbeef, | |
2339 | 0xdeadbeef, | |
2340 | 0xdeadbeef, | |
2341 | 0xdeadbeef}}; | |
2342 | ||
2343 | /* Check if a row is an index row */ | |
2344 | static bool | |
2345 | is_index_row(struct ovsdb_idl_row *row) | |
2346 | { | |
2347 | return uuid_equals(&row->uuid, &index_row_uuid); | |
2348 | } | |
2349 | ||
93fe0264 LR |
2350 | /* Initializes a row for use in an indexed query. |
2351 | * Not intended for direct usage. | |
2352 | */ | |
2353 | struct ovsdb_idl_row * | |
2354 | ovsdb_idl_index_init_row(struct ovsdb_idl * idl, | |
2355 | const struct ovsdb_idl_table_class *class) | |
2356 | { | |
2357 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); | |
2358 | class->row_init(row); | |
3cc1634f | 2359 | row->uuid = index_row_uuid; |
fa50ab0b | 2360 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
93fe0264 LR |
2361 | row->written = bitmap_allocate(class->n_columns); |
2362 | row->table = ovsdb_idl_table_from_class(idl, class); | |
3cc1634f HZ |
2363 | /* arcs are not used for index row, but it doesn't harm to initialize */ |
2364 | ovs_list_init(&row->src_arcs); | |
2365 | ovs_list_init(&row->dst_arcs); | |
93fe0264 LR |
2366 | return row; |
2367 | } | |
2368 | ||
2369 | /* Destroys 'row_' and frees all associated memory. This function is intended | |
2370 | * to be used indirectly through one of the "index_destroy_row" functions | |
2371 | * generated by ovsdb-idlc. | |
2372 | */ | |
2373 | void | |
2374 | ovsdb_idl_index_destroy_row__(const struct ovsdb_idl_row *row_) | |
2375 | { | |
2376 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); | |
3eb14233 | 2377 | const struct ovsdb_idl_table_class *class = row->table->class_; |
93fe0264 LR |
2378 | const struct ovsdb_idl_column *c; |
2379 | size_t i; | |
2380 | ||
3cc1634f HZ |
2381 | ovs_assert(ovs_list_is_empty(&row_->src_arcs)); |
2382 | ovs_assert(ovs_list_is_empty(&row_->dst_arcs)); | |
93fe0264 LR |
2383 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { |
2384 | c = &class->columns[i]; | |
2385 | (c->unparse) (row); | |
fa50ab0b JS |
2386 | free(row->new_datum[i].values); |
2387 | free(row->new_datum[i].keys); | |
93fe0264 | 2388 | } |
fa50ab0b | 2389 | free(row->new_datum); |
93fe0264 LR |
2390 | free(row->written); |
2391 | free(row); | |
2392 | } | |
2393 | ||
2394 | /* Moves the cursor to the first entry in the index. Returns a pointer to the | |
2395 | * corresponding ovsdb_idl_row, or NULL if the index list is empy. | |
2396 | */ | |
2397 | struct ovsdb_idl_row * | |
2398 | ovsdb_idl_index_first(struct ovsdb_idl_index_cursor *cursor) | |
2399 | { | |
2400 | cursor->position = skiplist_first(cursor->index->skiplist); | |
2401 | return ovsdb_idl_index_data(cursor); | |
2402 | } | |
2403 | ||
2404 | /* Moves the cursor to the next record in the index list. | |
2405 | */ | |
2406 | struct ovsdb_idl_row * | |
2407 | ovsdb_idl_index_next(struct ovsdb_idl_index_cursor *cursor) | |
2408 | { | |
2409 | if (!cursor->position) { | |
2410 | return NULL; | |
2411 | } | |
2412 | cursor->position = skiplist_next(cursor->position); | |
2413 | return ovsdb_idl_index_data(cursor); | |
2414 | } | |
2415 | ||
2416 | /* Returns the ovsdb_idl_row pointer corresponding to the record at the | |
2417 | * current cursor location. | |
2418 | */ | |
2419 | struct ovsdb_idl_row * | |
2420 | ovsdb_idl_index_data(struct ovsdb_idl_index_cursor *cursor) | |
2421 | { | |
2422 | return skiplist_get_data(cursor->position); | |
2423 | } | |
2424 | ||
2425 | /* Moves the cursor to the first entry in the index matching the specified | |
2426 | * value. If 'value' is NULL, the cursor is moved to the last entry in the | |
2427 | * list. Returns a pointer to the corresponding ovsdb_idl_row or NULL. | |
2428 | */ | |
2429 | struct ovsdb_idl_row * | |
2430 | ovsdb_idl_index_find(struct ovsdb_idl_index_cursor *cursor, | |
2431 | struct ovsdb_idl_row *value) | |
2432 | { | |
2433 | if (value) { | |
2434 | cursor->position = skiplist_find(cursor->index->skiplist, value); | |
2435 | } else { | |
2436 | cursor->position = skiplist_first(cursor->index->skiplist); | |
2437 | } | |
2438 | return ovsdb_idl_index_data(cursor); | |
2439 | } | |
2440 | ||
2441 | /* Moves the cursor to the first entry in the index with a value greater than | |
2442 | * or equal to the given value. If 'value' is NULL, the cursor is moved to the | |
2443 | * first entry in the index. Returns a pointer to the corresponding | |
2444 | * ovsdb_idl_row or NULL if such a row does not exist. | |
2445 | */ | |
2446 | struct ovsdb_idl_row * | |
2447 | ovsdb_idl_index_forward_to(struct ovsdb_idl_index_cursor *cursor, | |
2448 | struct ovsdb_idl_row *value) | |
2449 | { | |
2450 | if (value) { | |
2451 | cursor->position = skiplist_forward_to(cursor->index->skiplist, value); | |
2452 | } else { | |
2453 | cursor->position = skiplist_first(cursor->index->skiplist); | |
2454 | } | |
2455 | return ovsdb_idl_index_data(cursor); | |
2456 | } | |
2457 | ||
2458 | /* Returns the result of comparing two rows using the comparison function | |
2459 | * for this index. | |
2460 | * Returns: | |
2461 | * < 0 if a < b | |
2462 | * 0 if a == b | |
2463 | * > 0 if a > b | |
2464 | * When the pointer to either row is NULL, this function considers NULL to be | |
2465 | * greater than any other value, and NULL == NULL. | |
2466 | */ | |
2467 | int | |
2468 | ovsdb_idl_index_compare(struct ovsdb_idl_index_cursor *cursor, | |
2469 | struct ovsdb_idl_row *a, struct ovsdb_idl_row *b) | |
2470 | { | |
2471 | if (a && b) { | |
2472 | return ovsdb_idl_index_generic_comparer(a, b, cursor->index); | |
2473 | } else if (!a && !b) { | |
2474 | return 0; | |
2475 | } else if (a) { | |
2476 | return -1; | |
2477 | } else { | |
2478 | return 1; | |
2479 | } | |
2480 | } | |
979821c0 | 2481 | |
c3bb4bd7 | 2482 | static void |
475281c0 | 2483 | ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row) |
c3bb4bd7 | 2484 | { |
4e0b4acc | 2485 | ovs_assert(row->old_datum == row->new_datum); |
c3bb4bd7 | 2486 | if (!ovsdb_idl_row_is_orphan(row)) { |
3eb14233 | 2487 | const struct ovsdb_idl_table_class *class = row->table->class_; |
c3bb4bd7 BP |
2488 | size_t i; |
2489 | ||
2490 | for (i = 0; i < class->n_columns; i++) { | |
4e0b4acc | 2491 | ovsdb_datum_destroy(&row->old_datum[i], &class->columns[i].type); |
475281c0 | 2492 | } |
4e0b4acc JS |
2493 | free(row->old_datum); |
2494 | row->old_datum = row->new_datum = NULL; | |
475281c0 BP |
2495 | } |
2496 | } | |
2497 | ||
2498 | static void | |
2499 | ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row) | |
2500 | { | |
4e0b4acc | 2501 | if (row->old_datum != row->new_datum) { |
fa50ab0b | 2502 | if (row->new_datum) { |
3eb14233 | 2503 | const struct ovsdb_idl_table_class *class = row->table->class_; |
475281c0 BP |
2504 | size_t i; |
2505 | ||
35c2ce98 JG |
2506 | if (row->written) { |
2507 | BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { | |
fa50ab0b JS |
2508 | ovsdb_datum_destroy(&row->new_datum[i], |
2509 | &class->columns[i].type); | |
35c2ce98 | 2510 | } |
475281c0 | 2511 | } |
fa50ab0b | 2512 | free(row->new_datum); |
475281c0 BP |
2513 | free(row->written); |
2514 | row->written = NULL; | |
c3bb4bd7 | 2515 | } |
4e0b4acc | 2516 | row->new_datum = row->old_datum; |
c3bb4bd7 BP |
2517 | } |
2518 | } | |
2519 | ||
2520 | static void | |
2521 | ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) | |
2522 | { | |
2523 | struct ovsdb_idl_arc *arc, *next; | |
2524 | ||
2525 | /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows | |
932104f4 SA |
2526 | * that this causes to be unreferenced, if tracking is not enabled. |
2527 | * If tracking is enabled, orphaned nodes are removed from hmap but not | |
2528 | * freed. | |
2529 | */ | |
4e8e4213 | 2530 | LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) { |
417e7e66 | 2531 | ovs_list_remove(&arc->dst_node); |
c3bb4bd7 BP |
2532 | if (destroy_dsts |
2533 | && ovsdb_idl_row_is_orphan(arc->dst) | |
417e7e66 | 2534 | && ovs_list_is_empty(&arc->dst->dst_arcs)) { |
c3bb4bd7 BP |
2535 | ovsdb_idl_row_destroy(arc->dst); |
2536 | } | |
2537 | free(arc); | |
2538 | } | |
417e7e66 | 2539 | ovs_list_init(&row->src_arcs); |
c3bb4bd7 BP |
2540 | } |
2541 | ||
2542 | /* Force nodes that reference 'row' to reparse. */ | |
2543 | static void | |
0196cc15 | 2544 | ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) |
c3bb4bd7 BP |
2545 | { |
2546 | struct ovsdb_idl_arc *arc, *next; | |
2547 | ||
2548 | /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy | |
2549 | * 'arc', so we need to use the "safe" variant of list traversal. However, | |
979821c0 BP |
2550 | * calling an ovsdb_idl_column's 'parse' function will add an arc |
2551 | * equivalent to 'arc' to row->arcs. That could be a problem for | |
2552 | * traversal, but it adds it at the beginning of the list to prevent us | |
2553 | * from stumbling upon it again. | |
c3bb4bd7 BP |
2554 | * |
2555 | * (If duplicate arcs were possible then we would need to make sure that | |
2556 | * 'next' didn't also point into 'arc''s destination, but we forbid | |
2557 | * duplicate arcs.) */ | |
4e8e4213 | 2558 | LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) { |
c3bb4bd7 BP |
2559 | struct ovsdb_idl_row *ref = arc->src; |
2560 | ||
979821c0 | 2561 | ovsdb_idl_row_unparse(ref); |
0196cc15 | 2562 | ovsdb_idl_row_clear_arcs(ref, false); |
979821c0 | 2563 | ovsdb_idl_row_parse(ref); |
c3bb4bd7 BP |
2564 | } |
2565 | } | |
2566 | ||
475281c0 BP |
2567 | static struct ovsdb_idl_row * |
2568 | ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class) | |
2569 | { | |
72c6edc5 | 2570 | struct ovsdb_idl_row *row = xzalloc(class->allocation_size); |
a699f614 | 2571 | class->row_init(row); |
417e7e66 BW |
2572 | ovs_list_init(&row->src_arcs); |
2573 | ovs_list_init(&row->dst_arcs); | |
475281c0 | 2574 | hmap_node_nullify(&row->txn_node); |
417e7e66 | 2575 | ovs_list_init(&row->track_node); |
475281c0 BP |
2576 | return row; |
2577 | } | |
2578 | ||
c3bb4bd7 BP |
2579 | static struct ovsdb_idl_row * |
2580 | ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid) | |
2581 | { | |
3eb14233 | 2582 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class_); |
c3bb4bd7 BP |
2583 | hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); |
2584 | row->uuid = *uuid; | |
c3bb4bd7 | 2585 | row->table = table; |
f199df26 EA |
2586 | row->map_op_written = NULL; |
2587 | row->map_op_lists = NULL; | |
f1ab6e06 RM |
2588 | row->set_op_written = NULL; |
2589 | row->set_op_lists = NULL; | |
c3bb4bd7 BP |
2590 | return row; |
2591 | } | |
2592 | ||
2593 | static void | |
2594 | ovsdb_idl_row_destroy(struct ovsdb_idl_row *row) | |
2595 | { | |
2596 | if (row) { | |
475281c0 | 2597 | ovsdb_idl_row_clear_old(row); |
c3bb4bd7 | 2598 | hmap_remove(&row->table->rows, &row->hmap_node); |
f199df26 | 2599 | ovsdb_idl_destroy_all_map_op_lists(row); |
f1ab6e06 | 2600 | ovsdb_idl_destroy_all_set_op_lists(row); |
932104f4 SA |
2601 | if (ovsdb_idl_track_is_set(row->table)) { |
2602 | row->change_seqno[OVSDB_IDL_CHANGE_DELETE] | |
2603 | = row->table->change_seqno[OVSDB_IDL_CHANGE_DELETE] | |
2604 | = row->table->idl->change_seqno + 1; | |
2605 | } | |
faa9e235 RM |
2606 | if (!ovs_list_is_empty(&row->track_node)) { |
2607 | ovs_list_remove(&row->track_node); | |
932104f4 | 2608 | } |
faa9e235 | 2609 | ovs_list_push_back(&row->table->track_list, &row->track_node); |
932104f4 SA |
2610 | } |
2611 | } | |
2612 | ||
f199df26 EA |
2613 | static void |
2614 | ovsdb_idl_destroy_all_map_op_lists(struct ovsdb_idl_row *row) | |
2615 | { | |
2616 | if (row->map_op_written) { | |
2617 | /* Clear Map Operation Lists */ | |
2618 | size_t idx, n_columns; | |
2619 | const struct ovsdb_idl_column *columns; | |
2620 | const struct ovsdb_type *type; | |
3eb14233 JS |
2621 | n_columns = row->table->class_->n_columns; |
2622 | columns = row->table->class_->columns; | |
f199df26 EA |
2623 | BITMAP_FOR_EACH_1 (idx, n_columns, row->map_op_written) { |
2624 | type = &columns[idx].type; | |
2625 | map_op_list_destroy(row->map_op_lists[idx], type); | |
2626 | } | |
2627 | free(row->map_op_lists); | |
2628 | bitmap_free(row->map_op_written); | |
2629 | row->map_op_lists = NULL; | |
2630 | row->map_op_written = NULL; | |
2631 | } | |
2632 | } | |
2633 | ||
f1ab6e06 RM |
2634 | static void |
2635 | ovsdb_idl_destroy_all_set_op_lists(struct ovsdb_idl_row *row) | |
2636 | { | |
2637 | if (row->set_op_written) { | |
2638 | /* Clear Set Operation Lists */ | |
2639 | size_t idx, n_columns; | |
2640 | const struct ovsdb_idl_column *columns; | |
2641 | const struct ovsdb_type *type; | |
3eb14233 JS |
2642 | n_columns = row->table->class_->n_columns; |
2643 | columns = row->table->class_->columns; | |
f1ab6e06 RM |
2644 | BITMAP_FOR_EACH_1 (idx, n_columns, row->set_op_written) { |
2645 | type = &columns[idx].type; | |
2646 | set_op_list_destroy(row->set_op_lists[idx], type); | |
2647 | } | |
2648 | free(row->set_op_lists); | |
2649 | bitmap_free(row->set_op_written); | |
2650 | row->set_op_lists = NULL; | |
2651 | row->set_op_written = NULL; | |
2652 | } | |
2653 | } | |
2654 | ||
932104f4 SA |
2655 | static void |
2656 | ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *idl) | |
2657 | { | |
2658 | size_t i; | |
2659 | ||
3eb14233 | 2660 | for (i = 0; i < idl->class_->n_tables; i++) { |
932104f4 SA |
2661 | struct ovsdb_idl_table *table = &idl->tables[i]; |
2662 | ||
417e7e66 | 2663 | if (!ovs_list_is_empty(&table->track_list)) { |
932104f4 SA |
2664 | struct ovsdb_idl_row *row, *next; |
2665 | ||
2666 | LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) { | |
2667 | if (!ovsdb_idl_track_is_set(row->table)) { | |
417e7e66 | 2668 | ovs_list_remove(&row->track_node); |
932104f4 SA |
2669 | free(row); |
2670 | } | |
2671 | } | |
2672 | } | |
c3bb4bd7 BP |
2673 | } |
2674 | } | |
2675 | ||
2676 | static void | |
2677 | ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json) | |
2678 | { | |
3eb14233 | 2679 | const struct ovsdb_idl_table_class *class = row->table->class_; |
4e0b4acc | 2680 | size_t i, datum_size; |
c3bb4bd7 | 2681 | |
4e0b4acc JS |
2682 | ovs_assert(!row->old_datum && !row->new_datum); |
2683 | datum_size = class->n_columns * sizeof *row->old_datum; | |
2684 | row->old_datum = row->new_datum = xmalloc(datum_size); | |
c3bb4bd7 | 2685 | for (i = 0; i < class->n_columns; i++) { |
4e0b4acc | 2686 | ovsdb_datum_init_default(&row->old_datum[i], &class->columns[i].type); |
c3bb4bd7 | 2687 | } |
932104f4 | 2688 | ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_INSERT); |
979821c0 | 2689 | ovsdb_idl_row_parse(row); |
c3bb4bd7 | 2690 | |
0196cc15 | 2691 | ovsdb_idl_row_reparse_backrefs(row); |
93fe0264 | 2692 | ovsdb_idl_add_to_indexes(row); |
c3bb4bd7 BP |
2693 | } |
2694 | ||
2695 | static void | |
2696 | ovsdb_idl_delete_row(struct ovsdb_idl_row *row) | |
2697 | { | |
93fe0264 | 2698 | ovsdb_idl_remove_from_indexes(row); |
979821c0 | 2699 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 2700 | ovsdb_idl_row_clear_arcs(row, true); |
475281c0 | 2701 | ovsdb_idl_row_clear_old(row); |
417e7e66 | 2702 | if (ovs_list_is_empty(&row->dst_arcs)) { |
c3bb4bd7 BP |
2703 | ovsdb_idl_row_destroy(row); |
2704 | } else { | |
0196cc15 | 2705 | ovsdb_idl_row_reparse_backrefs(row); |
c3bb4bd7 BP |
2706 | } |
2707 | } | |
2708 | ||
c547535a BP |
2709 | /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false |
2710 | * otherwise. */ | |
2711 | static bool | |
c3bb4bd7 BP |
2712 | ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json) |
2713 | { | |
c547535a BP |
2714 | bool changed; |
2715 | ||
93fe0264 | 2716 | ovsdb_idl_remove_from_indexes(row); |
979821c0 | 2717 | ovsdb_idl_row_unparse(row); |
c3bb4bd7 | 2718 | ovsdb_idl_row_clear_arcs(row, true); |
932104f4 | 2719 | changed = ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_MODIFY); |
979821c0 | 2720 | ovsdb_idl_row_parse(row); |
93fe0264 | 2721 | ovsdb_idl_add_to_indexes(row); |
c547535a BP |
2722 | |
2723 | return changed; | |
c3bb4bd7 BP |
2724 | } |
2725 | ||
db2b5757 AZ |
2726 | static bool |
2727 | ovsdb_idl_modify_row_by_diff(struct ovsdb_idl_row *row, | |
2728 | const struct json *diff_json) | |
2729 | { | |
2730 | bool changed; | |
2731 | ||
2732 | ovsdb_idl_row_unparse(row); | |
2733 | ovsdb_idl_row_clear_arcs(row, true); | |
2734 | changed = ovsdb_idl_row_apply_diff(row, diff_json, | |
2735 | OVSDB_IDL_CHANGE_MODIFY); | |
2736 | ovsdb_idl_row_parse(row); | |
2737 | ||
2738 | return changed; | |
2739 | } | |
2740 | ||
c3bb4bd7 BP |
2741 | static bool |
2742 | may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) | |
2743 | { | |
2744 | const struct ovsdb_idl_arc *arc; | |
2745 | ||
2746 | /* No self-arcs. */ | |
2747 | if (src == dst) { | |
2748 | return false; | |
2749 | } | |
2750 | ||
2751 | /* No duplicate arcs. | |
2752 | * | |
2753 | * We only need to test whether the first arc in dst->dst_arcs originates | |
2754 | * at 'src', since we add all of the arcs from a given source in a clump | |
979821c0 | 2755 | * (in a single call to ovsdb_idl_row_parse()) and new arcs are always |
c3bb4bd7 | 2756 | * added at the front of the dst_arcs list. */ |
417e7e66 | 2757 | if (ovs_list_is_empty(&dst->dst_arcs)) { |
c3bb4bd7 BP |
2758 | return true; |
2759 | } | |
2760 | arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node); | |
2761 | return arc->src != src; | |
2762 | } | |
2763 | ||
115f1e4d | 2764 | static struct ovsdb_idl_table * |
475281c0 BP |
2765 | ovsdb_idl_table_from_class(const struct ovsdb_idl *idl, |
2766 | const struct ovsdb_idl_table_class *table_class) | |
115f1e4d | 2767 | { |
3eb14233 | 2768 | return &idl->tables[table_class - idl->class_->tables]; |
115f1e4d BP |
2769 | } |
2770 | ||
2f926787 | 2771 | /* Called by ovsdb-idlc generated code. */ |
c3bb4bd7 BP |
2772 | struct ovsdb_idl_row * |
2773 | ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src, | |
012d3762 | 2774 | const struct ovsdb_idl_table_class *dst_table_class, |
c3bb4bd7 BP |
2775 | const struct uuid *dst_uuid) |
2776 | { | |
2777 | struct ovsdb_idl *idl = src->table->idl; | |
e9011ac8 | 2778 | struct ovsdb_idl_table *dst_table; |
c3bb4bd7 BP |
2779 | struct ovsdb_idl_arc *arc; |
2780 | struct ovsdb_idl_row *dst; | |
2781 | ||
475281c0 | 2782 | dst_table = ovsdb_idl_table_from_class(idl, dst_table_class); |
e9011ac8 | 2783 | dst = ovsdb_idl_get_row(dst_table, dst_uuid); |
3cc1634f HZ |
2784 | if (idl->txn || is_index_row(src)) { |
2785 | /* There are two cases we should not update any arcs: | |
2786 | * | |
2787 | * 1. We're being called from ovsdb_idl_txn_write(). We must not update | |
979821c0 BP |
2788 | * any arcs, because the transaction will be backed out at commit or |
2789 | * abort time and we don't want our graph screwed up. | |
2790 | * | |
3cc1634f HZ |
2791 | * 2. The row is used as an index for querying purpose only. |
2792 | * | |
2793 | * In these cases, just return the destination row, if there is one and | |
2794 | * it has not been deleted. */ | |
fa50ab0b | 2795 | if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new_datum)) { |
979821c0 BP |
2796 | return dst; |
2797 | } | |
2798 | return NULL; | |
2799 | } else { | |
2800 | /* We're being called from some other context. Update the graph. */ | |
2801 | if (!dst) { | |
2802 | dst = ovsdb_idl_row_create(dst_table, dst_uuid); | |
2803 | } | |
c3bb4bd7 | 2804 | |
979821c0 BP |
2805 | /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */ |
2806 | if (may_add_arc(src, dst)) { | |
2807 | /* The arc *must* be added at the front of the dst_arcs list. See | |
2808 | * ovsdb_idl_row_reparse_backrefs() for details. */ | |
2809 | arc = xmalloc(sizeof *arc); | |
417e7e66 BW |
2810 | ovs_list_push_front(&src->src_arcs, &arc->src_node); |
2811 | ovs_list_push_front(&dst->dst_arcs, &arc->dst_node); | |
979821c0 BP |
2812 | arc->src = src; |
2813 | arc->dst = dst; | |
2814 | } | |
2815 | ||
2816 | return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL; | |
c3bb4bd7 | 2817 | } |
979821c0 | 2818 | } |
c3bb4bd7 | 2819 | |
2f926787 BP |
2820 | /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a |
2821 | * pointer to the row if there is one, otherwise a null pointer. */ | |
979821c0 BP |
2822 | const struct ovsdb_idl_row * |
2823 | ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl, | |
2824 | const struct ovsdb_idl_table_class *tc, | |
2825 | const struct uuid *uuid) | |
2826 | { | |
2827 | return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid); | |
c3bb4bd7 BP |
2828 | } |
2829 | ||
2830 | static struct ovsdb_idl_row * | |
2831 | next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) | |
2832 | { | |
2833 | for (; node; node = hmap_next(&table->rows, node)) { | |
2834 | struct ovsdb_idl_row *row; | |
2835 | ||
2836 | row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); | |
f2ba3c0a | 2837 | if (ovsdb_idl_row_exists(row)) { |
c3bb4bd7 BP |
2838 | return row; |
2839 | } | |
2840 | } | |
2841 | return NULL; | |
2842 | } | |
2843 | ||
2f926787 BP |
2844 | /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that |
2845 | * table is empty. | |
2846 | * | |
2847 | * Database tables are internally maintained as hash tables, so adding or | |
2848 | * removing rows while traversing the same table can cause some rows to be | |
2849 | * visited twice or not at apply. */ | |
979821c0 | 2850 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
2851 | ovsdb_idl_first_row(const struct ovsdb_idl *idl, |
2852 | const struct ovsdb_idl_table_class *table_class) | |
2853 | { | |
475281c0 BP |
2854 | struct ovsdb_idl_table *table |
2855 | = ovsdb_idl_table_from_class(idl, table_class); | |
c3bb4bd7 BP |
2856 | return next_real_row(table, hmap_first(&table->rows)); |
2857 | } | |
2858 | ||
2f926787 BP |
2859 | /* Returns a row following 'row' within its table, or a null pointer if 'row' |
2860 | * is the last row in its table. */ | |
979821c0 | 2861 | const struct ovsdb_idl_row * |
c3bb4bd7 BP |
2862 | ovsdb_idl_next_row(const struct ovsdb_idl_row *row) |
2863 | { | |
2864 | struct ovsdb_idl_table *table = row->table; | |
2865 | ||
2866 | return next_real_row(table, hmap_next(&table->rows, &row->hmap_node)); | |
2867 | } | |
8c3c2f30 BP |
2868 | |
2869 | /* Reads and returns the value of 'column' within 'row'. If an ongoing | |
2870 | * transaction has changed 'column''s value, the modified value is returned. | |
2871 | * | |
2872 | * The caller must not modify or free the returned value. | |
2873 | * | |
2874 | * Various kinds of changes can invalidate the returned value: writing to the | |
2875 | * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row' | |
2876 | * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction | |
2877 | * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the | |
2878 | * returned value is needed for a long time, it is best to make a copy of it | |
2879 | * with ovsdb_datum_clone(). */ | |
2880 | const struct ovsdb_datum * | |
2881 | ovsdb_idl_read(const struct ovsdb_idl_row *row, | |
2882 | const struct ovsdb_idl_column *column) | |
2883 | { | |
942d31c8 BP |
2884 | const struct ovsdb_idl_table_class *class; |
2885 | size_t column_idx; | |
2886 | ||
cb22974d | 2887 | ovs_assert(!ovsdb_idl_row_is_synthetic(row)); |
942d31c8 | 2888 | |
3eb14233 | 2889 | class = row->table->class_; |
942d31c8 | 2890 | column_idx = column - class->columns; |
8c3c2f30 | 2891 | |
fa50ab0b | 2892 | ovs_assert(row->new_datum != NULL); |
cb22974d | 2893 | ovs_assert(column_idx < class->n_columns); |
8c3c2f30 BP |
2894 | |
2895 | if (row->written && bitmap_is_set(row->written, column_idx)) { | |
fa50ab0b | 2896 | return &row->new_datum[column_idx]; |
4e0b4acc JS |
2897 | } else if (row->old_datum) { |
2898 | return &row->old_datum[column_idx]; | |
8c3c2f30 BP |
2899 | } else { |
2900 | return ovsdb_datum_default(&column->type); | |
2901 | } | |
2902 | } | |
2903 | ||
2904 | /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key | |
2905 | * type 'key_type' and value type 'value_type'. (Scalar and set types will | |
2906 | * have a value type of OVSDB_TYPE_VOID.) | |
2907 | * | |
2908 | * This is useful in code that "knows" that a particular column has a given | |
2909 | * type, so that it will abort if someone changes the column's type without | |
2910 | * updating the code that uses it. */ | |
2911 | const struct ovsdb_datum * | |
2912 | ovsdb_idl_get(const struct ovsdb_idl_row *row, | |
2913 | const struct ovsdb_idl_column *column, | |
2914 | enum ovsdb_atomic_type key_type OVS_UNUSED, | |
2915 | enum ovsdb_atomic_type value_type OVS_UNUSED) | |
2916 | { | |
cb22974d BP |
2917 | ovs_assert(column->type.key.type == key_type); |
2918 | ovs_assert(column->type.value.type == value_type); | |
8c3c2f30 BP |
2919 | |
2920 | return ovsdb_idl_read(row, column); | |
2921 | } | |
cfea354b | 2922 | |
ff495b63 BP |
2923 | /* Returns true if the field represented by 'column' in 'row' may be modified, |
2924 | * false if it is immutable. | |
2925 | * | |
2926 | * Normally, whether a field is mutable is controlled by its column's schema. | |
2927 | * However, an immutable column can be set to any initial value at the time of | |
2928 | * insertion, so if 'row' is a new row (one that is being added as part of the | |
2929 | * current transaction, supposing that a transaction is in progress) then even | |
2930 | * its "immutable" fields are actually mutable. */ | |
2931 | bool | |
2932 | ovsdb_idl_is_mutable(const struct ovsdb_idl_row *row, | |
2933 | const struct ovsdb_idl_column *column) | |
2934 | { | |
4e0b4acc | 2935 | return column->is_mutable || (row->new_datum && !row->old_datum); |
ff495b63 BP |
2936 | } |
2937 | ||
cfea354b BP |
2938 | /* Returns false if 'row' was obtained from the IDL, true if it was initialized |
2939 | * to all-zero-bits by some other entity. If 'row' was set up some other way | |
2940 | * then the return value is indeterminate. */ | |
2941 | bool | |
2942 | ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *row) | |
2943 | { | |
2944 | return row->table == NULL; | |
2945 | } | |
475281c0 BP |
2946 | \f |
2947 | /* Transactions. */ | |
2948 | ||
2949 | static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
2950 | enum ovsdb_idl_txn_status); | |
2951 | ||
2f926787 BP |
2952 | /* Returns a string representation of 'status'. The caller must not modify or |
2953 | * free the returned string. | |
2954 | * | |
2955 | * The return value is probably useful only for debug log messages and unit | |
2956 | * tests. */ | |
475281c0 BP |
2957 | const char * |
2958 | ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status) | |
2959 | { | |
2960 | switch (status) { | |
2096903b BP |
2961 | case TXN_UNCOMMITTED: |
2962 | return "uncommitted"; | |
b54e22e9 BP |
2963 | case TXN_UNCHANGED: |
2964 | return "unchanged"; | |
475281c0 BP |
2965 | case TXN_INCOMPLETE: |
2966 | return "incomplete"; | |
2967 | case TXN_ABORTED: | |
2968 | return "aborted"; | |
2969 | case TXN_SUCCESS: | |
2970 | return "success"; | |
854a94d9 BP |
2971 | case TXN_TRY_AGAIN: |
2972 | return "try again"; | |
06b6d651 BP |
2973 | case TXN_NOT_LOCKED: |
2974 | return "not locked"; | |
475281c0 BP |
2975 | case TXN_ERROR: |
2976 | return "error"; | |
2977 | } | |
2978 | return "<unknown>"; | |
2979 | } | |
2980 | ||
2f926787 BP |
2981 | /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single |
2982 | * active transaction at a time. See the large comment in ovsdb-idl.h for | |
2983 | * general information on transactions. */ | |
475281c0 BP |
2984 | struct ovsdb_idl_txn * |
2985 | ovsdb_idl_txn_create(struct ovsdb_idl *idl) | |
2986 | { | |
2987 | struct ovsdb_idl_txn *txn; | |
2988 | ||
cb22974d | 2989 | ovs_assert(!idl->txn); |
475281c0 | 2990 | idl->txn = txn = xmalloc(sizeof *txn); |
0196cc15 | 2991 | txn->request_id = NULL; |
475281c0 | 2992 | txn->idl = idl; |
475281c0 | 2993 | hmap_init(&txn->txn_rows); |
2096903b | 2994 | txn->status = TXN_UNCOMMITTED; |
91e310a5 | 2995 | txn->error = NULL; |
577aebdf | 2996 | txn->dry_run = false; |
d171b584 | 2997 | ds_init(&txn->comment); |
0196cc15 | 2998 | |
b54e22e9 BP |
2999 | txn->inc_table = NULL; |
3000 | txn->inc_column = NULL; | |
0196cc15 | 3001 | |
69490970 | 3002 | hmap_init(&txn->inserted_rows); |
0196cc15 | 3003 | |
475281c0 BP |
3004 | return txn; |
3005 | } | |
3006 | ||
e1c0e2d1 BP |
3007 | /* Appends 's', which is treated as a printf()-type format string, to the |
3008 | * comments that will be passed to the OVSDB server when 'txn' is committed. | |
3009 | * (The comment will be committed to the OVSDB log, which "ovsdb-tool | |
3010 | * show-log" can print in a relatively human-readable form.) */ | |
d171b584 | 3011 | void |
e1c0e2d1 | 3012 | ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) |
d171b584 | 3013 | { |
e1c0e2d1 BP |
3014 | va_list args; |
3015 | ||
d171b584 BP |
3016 | if (txn->comment.length) { |
3017 | ds_put_char(&txn->comment, '\n'); | |
3018 | } | |
e1c0e2d1 BP |
3019 | |
3020 | va_start(args, s); | |
3021 | ds_put_format_valist(&txn->comment, s, args); | |
3022 | va_end(args); | |
d171b584 BP |
3023 | } |
3024 | ||
2f926787 BP |
3025 | /* Marks 'txn' as a transaction that will not actually modify the database. In |
3026 | * almost every way, the transaction is treated like other transactions. It | |
3027 | * must be committed or aborted like other transactions, it will be sent to the | |
3028 | * database server like other transactions, and so on. The only difference is | |
3029 | * that the operations sent to the database server will include, as the last | |
3030 | * step, an "abort" operation, so that any changes made by the transaction will | |
3031 | * not actually take effect. */ | |
577aebdf BP |
3032 | void |
3033 | ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn) | |
3034 | { | |
3035 | txn->dry_run = true; | |
3036 | } | |
3037 | ||
94fbe1aa BP |
3038 | /* Causes 'txn', when committed, to increment the value of 'column' within |
3039 | * 'row' by 1. 'column' must have an integer type. After 'txn' commits | |
3040 | * successfully, the client may retrieve the final (incremented) value of | |
3041 | * 'column' with ovsdb_idl_txn_get_increment_new_value(). | |
3042 | * | |
de32cec7 BP |
3043 | * If at time of commit the transaction is otherwise empty, that is, it doesn't |
3044 | * change the database, then 'force' is important. If 'force' is false in this | |
3045 | * case, the IDL suppresses the increment and skips a round trip to the | |
3046 | * database server. If 'force' is true, the IDL will still increment the | |
3047 | * column. | |
3048 | * | |
94fbe1aa BP |
3049 | * The client could accomplish something similar with ovsdb_idl_read(), |
3050 | * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc | |
3051 | * generated wrappers for these functions. However, ovsdb_idl_txn_increment() | |
3052 | * will never (by itself) fail because of a verify error. | |
3053 | * | |
3054 | * The intended use is for incrementing the "next_cfg" column in the | |
3055 | * Open_vSwitch table. */ | |
b54e22e9 | 3056 | void |
94fbe1aa BP |
3057 | ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn, |
3058 | const struct ovsdb_idl_row *row, | |
de32cec7 BP |
3059 | const struct ovsdb_idl_column *column, |
3060 | bool force) | |
b54e22e9 | 3061 | { |
cb22974d BP |
3062 | ovs_assert(!txn->inc_table); |
3063 | ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER); | |
3064 | ovs_assert(column->type.value.type == OVSDB_TYPE_VOID); | |
94fbe1aa | 3065 | |
3eb14233 | 3066 | txn->inc_table = row->table->class_->name; |
94fbe1aa BP |
3067 | txn->inc_column = column->name; |
3068 | txn->inc_row = row->uuid; | |
de32cec7 | 3069 | txn->inc_force = force; |
b54e22e9 BP |
3070 | } |
3071 | ||
2f926787 BP |
3072 | /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit() |
3073 | * has been called for 'txn' but the commit is still incomplete (that is, the | |
3074 | * last call returned TXN_INCOMPLETE) then the transaction may or may not still | |
3075 | * end up committing at the database server, but the client will not be able to | |
3076 | * get any further status information back. */ | |
475281c0 BP |
3077 | void |
3078 | ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) | |
3079 | { | |
69490970 BP |
3080 | struct ovsdb_idl_txn_insert *insert, *next; |
3081 | ||
0196cc15 | 3082 | json_destroy(txn->request_id); |
fbd8fd40 BP |
3083 | if (txn->status == TXN_INCOMPLETE) { |
3084 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
3085 | } | |
475281c0 | 3086 | ovsdb_idl_txn_abort(txn); |
d171b584 | 3087 | ds_destroy(&txn->comment); |
91e310a5 | 3088 | free(txn->error); |
4e8e4213 | 3089 | HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) { |
69490970 BP |
3090 | free(insert); |
3091 | } | |
0196cc15 | 3092 | hmap_destroy(&txn->inserted_rows); |
475281c0 BP |
3093 | free(txn); |
3094 | } | |
3095 | ||
2f926787 | 3096 | /* Causes poll_block() to wake up if 'txn' has completed committing. */ |
586bb84a BP |
3097 | void |
3098 | ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn) | |
3099 | { | |
2096903b | 3100 | if (txn->status != TXN_UNCOMMITTED && txn->status != TXN_INCOMPLETE) { |
586bb84a BP |
3101 | poll_immediate_wake(); |
3102 | } | |
3103 | } | |
3104 | ||
475281c0 BP |
3105 | static struct json * |
3106 | where_uuid_equals(const struct uuid *uuid) | |
3107 | { | |
3108 | return | |
3109 | json_array_create_1( | |
3110 | json_array_create_3( | |
3111 | json_string_create("_uuid"), | |
3112 | json_string_create("=="), | |
3113 | json_array_create_2( | |
3114 | json_string_create("uuid"), | |
3115 | json_string_create_nocopy( | |
3116 | xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); | |
3117 | } | |
3118 | ||
3119 | static char * | |
3120 | uuid_name_from_uuid(const struct uuid *uuid) | |
3121 | { | |
3122 | char *name; | |
3123 | char *p; | |
3124 | ||
3125 | name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid)); | |
3126 | for (p = name; *p != '\0'; p++) { | |
3127 | if (*p == '-') { | |
3128 | *p = '_'; | |
3129 | } | |
3130 | } | |
3131 | ||
3132 | return name; | |
3133 | } | |
3134 | ||
3135 | static const struct ovsdb_idl_row * | |
3136 | ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) | |
3137 | { | |
3138 | const struct ovsdb_idl_row *row; | |
3139 | ||
4e8e4213 | 3140 | HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) { |
475281c0 BP |
3141 | if (uuid_equals(&row->uuid, uuid)) { |
3142 | return row; | |
3143 | } | |
3144 | } | |
3145 | return NULL; | |
3146 | } | |
3147 | ||
3148 | /* XXX there must be a cleaner way to do this */ | |
3149 | static struct json * | |
3150 | substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn) | |
3151 | { | |
3152 | if (json->type == JSON_ARRAY) { | |
3153 | struct uuid uuid; | |
3154 | size_t i; | |
3155 | ||
3156 | if (json->u.array.n == 2 | |
3157 | && json->u.array.elems[0]->type == JSON_STRING | |
3158 | && json->u.array.elems[1]->type == JSON_STRING | |
3159 | && !strcmp(json->u.array.elems[0]->u.string, "uuid") | |
3160 | && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) { | |
3161 | const struct ovsdb_idl_row *row; | |
3162 | ||
3163 | row = ovsdb_idl_txn_get_row(txn, &uuid); | |
4e0b4acc | 3164 | if (row && !row->old_datum && row->new_datum) { |
475281c0 BP |
3165 | json_destroy(json); |
3166 | ||
3167 | return json_array_create_2( | |
3168 | json_string_create("named-uuid"), | |
3169 | json_string_create_nocopy(uuid_name_from_uuid(&uuid))); | |
3170 | } | |
3171 | } | |
3172 | ||
3173 | for (i = 0; i < json->u.array.n; i++) { | |
3174 | json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i], | |
3175 | txn); | |
3176 | } | |
3177 | } else if (json->type == JSON_OBJECT) { | |
3178 | struct shash_node *node; | |
3179 | ||
3180 | SHASH_FOR_EACH (node, json_object(json)) { | |
3181 | node->data = substitute_uuids(node->data, txn); | |
3182 | } | |
3183 | } | |
3184 | return json; | |
3185 | } | |
3186 | ||
3187 | static void | |
3188 | ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) | |
3189 | { | |
3190 | struct ovsdb_idl_row *row, *next; | |
3191 | ||
979821c0 BP |
3192 | /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an |
3193 | * ovsdb_idl_column's 'parse' function, which will call | |
3194 | * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a | |
3195 | * transaction and fail to update the graph. */ | |
3196 | txn->idl->txn = NULL; | |
3197 | ||
4e8e4213 | 3198 | HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) { |
f199df26 | 3199 | ovsdb_idl_destroy_all_map_op_lists(row); |
f1ab6e06 | 3200 | ovsdb_idl_destroy_all_set_op_lists(row); |
4e0b4acc | 3201 | if (row->old_datum) { |
979821c0 BP |
3202 | if (row->written) { |
3203 | ovsdb_idl_row_unparse(row); | |
3204 | ovsdb_idl_row_clear_arcs(row, false); | |
3205 | ovsdb_idl_row_parse(row); | |
3206 | } | |
3207 | } else { | |
0196cc15 | 3208 | ovsdb_idl_row_unparse(row); |
8bc915de | 3209 | } |
475281c0 BP |
3210 | ovsdb_idl_row_clear_new(row); |
3211 | ||
3212 | free(row->prereqs); | |
3213 | row->prereqs = NULL; | |
3214 | ||
3215 | free(row->written); | |
3216 | row->written = NULL; | |
3217 | ||
3218 | hmap_remove(&txn->txn_rows, &row->txn_node); | |
3219 | hmap_node_nullify(&row->txn_node); | |
4e0b4acc | 3220 | if (!row->old_datum) { |
0196cc15 BP |
3221 | hmap_remove(&row->table->rows, &row->hmap_node); |
3222 | free(row); | |
3223 | } | |
475281c0 BP |
3224 | } |
3225 | hmap_destroy(&txn->txn_rows); | |
3226 | hmap_init(&txn->txn_rows); | |
3227 | } | |
3228 | ||
f199df26 EA |
3229 | static bool |
3230 | ovsdb_idl_txn_extract_mutations(struct ovsdb_idl_row *row, | |
3231 | struct json *mutations) | |
3232 | { | |
3eb14233 | 3233 | const struct ovsdb_idl_table_class *class = row->table->class_; |
f199df26 EA |
3234 | size_t idx; |
3235 | bool any_mutations = false; | |
3236 | ||
f1ab6e06 RM |
3237 | if (row->map_op_written) { |
3238 | BITMAP_FOR_EACH_1(idx, class->n_columns, row->map_op_written) { | |
3239 | struct map_op_list *map_op_list; | |
3240 | const struct ovsdb_idl_column *column; | |
3241 | const struct ovsdb_datum *old_datum; | |
3242 | enum ovsdb_atomic_type key_type, value_type; | |
3243 | struct json *mutation, *map, *col_name, *mutator; | |
3244 | struct json *del_set, *ins_map; | |
3245 | bool any_del, any_ins; | |
3246 | ||
3247 | map_op_list = row->map_op_lists[idx]; | |
3248 | column = &class->columns[idx]; | |
3249 | key_type = column->type.key.type; | |
3250 | value_type = column->type.value.type; | |
3251 | ||
3252 | /* Get the value to be changed */ | |
fa50ab0b JS |
3253 | if (row->new_datum && row->written |
3254 | && bitmap_is_set(row->written,idx)) { | |
3255 | old_datum = &row->new_datum[idx]; | |
4e0b4acc JS |
3256 | } else if (row->old_datum != NULL) { |
3257 | old_datum = &row->old_datum[idx]; | |
f1ab6e06 RM |
3258 | } else { |
3259 | old_datum = ovsdb_datum_default(&column->type); | |
3260 | } | |
3261 | ||
3262 | del_set = json_array_create_empty(); | |
3263 | ins_map = json_array_create_empty(); | |
3264 | any_del = false; | |
3265 | any_ins = false; | |
3266 | ||
3267 | for (struct map_op *map_op = map_op_list_first(map_op_list); map_op; | |
3268 | map_op = map_op_list_next(map_op_list, map_op)) { | |
3269 | ||
3270 | if (map_op_type(map_op) == MAP_OP_UPDATE) { | |
3271 | /* Find out if value really changed. */ | |
3272 | struct ovsdb_datum *new_datum; | |
3273 | unsigned int pos; | |
3274 | new_datum = map_op_datum(map_op); | |
3275 | pos = ovsdb_datum_find_key(old_datum, | |
3276 | &new_datum->keys[0], | |
3277 | key_type); | |
3278 | if (ovsdb_atom_equals(&new_datum->values[0], | |
3279 | &old_datum->values[pos], | |
3280 | value_type)) { | |
3281 | /* No change in value. Move on to next update. */ | |
3282 | continue; | |
3283 | } | |
3284 | } else if (map_op_type(map_op) == MAP_OP_DELETE){ | |
3285 | /* Verify that there is a key to delete. */ | |
3286 | unsigned int pos; | |
3287 | pos = ovsdb_datum_find_key(old_datum, | |
3288 | &map_op_datum(map_op)->keys[0], | |
3289 | key_type); | |
3290 | if (pos == UINT_MAX) { | |
3291 | /* No key to delete. Move on to next update. */ | |
3292 | VLOG_WARN("Trying to delete a key that doesn't " | |
3293 | "exist in the map."); | |
3294 | continue; | |
3295 | } | |
f199df26 | 3296 | } |
f1ab6e06 RM |
3297 | |
3298 | if (map_op_type(map_op) == MAP_OP_INSERT) { | |
3299 | map = json_array_create_2( | |
3300 | ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], | |
3301 | key_type), | |
3302 | ovsdb_atom_to_json(&map_op_datum(map_op)->values[0], | |
3303 | value_type)); | |
3304 | json_array_add(ins_map, map); | |
3305 | any_ins = true; | |
3306 | } else { /* MAP_OP_UPDATE or MAP_OP_DELETE */ | |
3307 | map = ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], | |
3308 | key_type); | |
3309 | json_array_add(del_set, map); | |
3310 | any_del = true; | |
f199df26 | 3311 | } |
f199df26 | 3312 | |
f1ab6e06 RM |
3313 | /* Generate an additional insert mutate for updates. */ |
3314 | if (map_op_type(map_op) == MAP_OP_UPDATE) { | |
3315 | map = json_array_create_2( | |
3316 | ovsdb_atom_to_json(&map_op_datum(map_op)->keys[0], | |
3317 | key_type), | |
3318 | ovsdb_atom_to_json(&map_op_datum(map_op)->values[0], | |
3319 | value_type)); | |
3320 | json_array_add(ins_map, map); | |
3321 | any_ins = true; | |
3322 | } | |
f199df26 EA |
3323 | } |
3324 | ||
f1ab6e06 RM |
3325 | if (any_del) { |
3326 | col_name = json_string_create(column->name); | |
3327 | mutator = json_string_create("delete"); | |
3328 | map = json_array_create_2(json_string_create("set"), del_set); | |
3329 | mutation = json_array_create_3(col_name, mutator, map); | |
3330 | json_array_add(mutations, mutation); | |
3331 | any_mutations = true; | |
3332 | } else { | |
3333 | json_destroy(del_set); | |
3334 | } | |
3335 | if (any_ins) { | |
3336 | col_name = json_string_create(column->name); | |
3337 | mutator = json_string_create("insert"); | |
3338 | map = json_array_create_2(json_string_create("map"), ins_map); | |
3339 | mutation = json_array_create_3(col_name, mutator, map); | |
3340 | json_array_add(mutations, mutation); | |
3341 | any_mutations = true; | |
3342 | } else { | |
3343 | json_destroy(ins_map); | |
f199df26 EA |
3344 | } |
3345 | } | |
f1ab6e06 RM |
3346 | } |
3347 | if (row->set_op_written) { | |
3348 | BITMAP_FOR_EACH_1(idx, class->n_columns, row->set_op_written) { | |
3349 | struct set_op_list *set_op_list; | |
3350 | const struct ovsdb_idl_column *column; | |
3351 | const struct ovsdb_datum *old_datum; | |
3352 | enum ovsdb_atomic_type key_type; | |
3353 | struct json *mutation, *set, *col_name, *mutator; | |
3354 | struct json *del_set, *ins_set; | |
3355 | bool any_del, any_ins; | |
3356 | ||
3357 | set_op_list = row->set_op_lists[idx]; | |
3358 | column = &class->columns[idx]; | |
3359 | key_type = column->type.key.type; | |
3360 | ||
3361 | /* Get the value to be changed */ | |
fa50ab0b JS |
3362 | if (row->new_datum && row->written |
3363 | && bitmap_is_set(row->written,idx)) { | |
3364 | old_datum = &row->new_datum[idx]; | |
4e0b4acc JS |
3365 | } else if (row->old_datum != NULL) { |
3366 | old_datum = &row->old_datum[idx]; | |
f1ab6e06 RM |
3367 | } else { |
3368 | old_datum = ovsdb_datum_default(&column->type); | |
3369 | } | |
f199df26 | 3370 | |
f1ab6e06 RM |
3371 | del_set = json_array_create_empty(); |
3372 | ins_set = json_array_create_empty(); | |
3373 | any_del = false; | |
3374 | any_ins = false; | |
3375 | ||
3376 | for (struct set_op *set_op = set_op_list_first(set_op_list); set_op; | |
3377 | set_op = set_op_list_next(set_op_list, set_op)) { | |
3378 | if (set_op_type(set_op) == SET_OP_INSERT) { | |
3379 | set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0], | |
3380 | key_type); | |
3381 | json_array_add(ins_set, set); | |
3382 | any_ins = true; | |
3383 | } else { /* SETP_OP_DELETE */ | |
3384 | /* Verify that there is a key to delete. */ | |
3385 | unsigned int pos; | |
3386 | pos = ovsdb_datum_find_key(old_datum, | |
3387 | &set_op_datum(set_op)->keys[0], | |
3388 | key_type); | |
3389 | if (pos == UINT_MAX) { | |
3390 | /* No key to delete. Move on to next update. */ | |
3391 | VLOG_WARN("Trying to delete a key that doesn't " | |
3392 | "exist in the set."); | |
3393 | continue; | |
3394 | } | |
3395 | set = ovsdb_atom_to_json(&set_op_datum(set_op)->keys[0], | |
3396 | key_type); | |
3397 | json_array_add(del_set, set); | |
3398 | any_del = true; | |
3399 | } | |
3400 | } | |
3401 | if (any_del) { | |
3402 | col_name = json_string_create(column->name); | |
3403 | mutator = json_string_create("delete"); | |
3404 | set = json_array_create_2(json_string_create("set"), del_set); | |
3405 | mutation = json_array_create_3(col_name, mutator, set); | |
3406 | json_array_add(mutations, mutation); | |
3407 | any_mutations = true; | |
3408 | } else { | |
3409 | json_destroy(del_set); | |
3410 | } | |
3411 | if (any_ins) { | |
3412 | col_name = json_string_create(column->name); | |
3413 | mutator = json_string_create("insert"); | |
3414 | set = json_array_create_2(json_string_create("set"), ins_set); | |
3415 | mutation = json_array_create_3(col_name, mutator, set); | |
3416 | json_array_add(mutations, mutation); | |
3417 | any_mutations = true; | |
3418 | } else { | |
3419 | json_destroy(ins_set); | |
3420 | } | |
f199df26 EA |
3421 | } |
3422 | } | |
3423 | return any_mutations; | |
3424 | } | |
3425 | ||
2f926787 BP |
3426 | /* Attempts to commit 'txn'. Returns the status of the commit operation, one |
3427 | * of the following TXN_* constants: | |
3428 | * | |
3429 | * TXN_INCOMPLETE: | |
3430 | * | |
3431 | * The transaction is in progress, but not yet complete. The caller | |
3432 | * should call again later, after calling ovsdb_idl_run() to let the IDL | |
3433 | * do OVSDB protocol processing. | |
3434 | * | |
3435 | * TXN_UNCHANGED: | |
3436 | * | |
3437 | * The transaction is complete. (It didn't actually change the database, | |
3438 | * so the IDL didn't send any request to the database server.) | |
3439 | * | |
3440 | * TXN_ABORTED: | |
3441 | * | |
3442 | * The caller previously called ovsdb_idl_txn_abort(). | |
3443 | * | |
3444 | * TXN_SUCCESS: | |
3445 | * | |
3446 | * The transaction was successful. The update made by the transaction | |
3447 | * (and possibly other changes made by other database clients) should | |
3448 | * already be visible in the IDL. | |
3449 | * | |
3450 | * TXN_TRY_AGAIN: | |
3451 | * | |
3452 | * The transaction failed for some transient reason, e.g. because a | |
3453 | * "verify" operation reported an inconsistency or due to a network | |
3454 | * problem. The caller should wait for a change to the database, then | |
3455 | * compose a new transaction, and commit the new transaction. | |
3456 | * | |
3457 | * Use the return value of ovsdb_idl_get_seqno() to wait for a change in | |
3458 | * the database. It is important to use its return value *before* the | |
3459 | * initial call to ovsdb_idl_txn_commit() as the baseline for this | |
3460 | * purpose, because the change that one should wait for can happen after | |
3461 | * the initial call but before the call that returns TXN_TRY_AGAIN, and | |
3462 | * using some other baseline value in that situation could cause an | |
3463 | * indefinite wait if the database rarely changes. | |
3464 | * | |
3465 | * TXN_NOT_LOCKED: | |
3466 | * | |
3467 | * The transaction failed because the IDL has been configured to require | |
3468 | * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or | |
3469 | * has already lost it. | |
3470 | * | |
3471 | * Committing a transaction rolls back all of the changes that it made to the | |
3472 | * IDL's copy of the database. If the transaction commits successfully, then | |
3473 | * the database server will send an update and, thus, the IDL will be updated | |
3474 | * with the committed changes. */ | |
475281c0 BP |
3475 | enum ovsdb_idl_txn_status |
3476 | ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) | |
3477 | { | |
3478 | struct ovsdb_idl_row *row; | |
3479 | struct json *operations; | |
3480 | bool any_updates; | |
475281c0 BP |
3481 | |
3482 | if (txn != txn->idl->txn) { | |
60032110 | 3483 | goto coverage_out; |
475281c0 BP |
3484 | } |
3485 | ||
06b6d651 BP |
3486 | /* If we need a lock but don't have it, give up quickly. */ |
3487 | if (txn->idl->lock_name && !ovsdb_idl_has_lock(txn->idl)) { | |
3488 | txn->status = TXN_NOT_LOCKED; | |
60032110 | 3489 | goto disassemble_out; |
06b6d651 BP |
3490 | } |
3491 | ||
9cb53f26 | 3492 | operations = json_array_create_1( |
3eb14233 | 3493 | json_string_create(txn->idl->class_->database)); |
475281c0 | 3494 | |
06b6d651 BP |
3495 | /* Assert that we have the required lock (avoiding a race). */ |
3496 | if (txn->idl->lock_name) { | |
3497 | struct json *op = json_object_create(); | |
3498 | json_array_add(operations, op); | |
3499 | json_object_put_string(op, "op", "assert"); | |
3500 | json_object_put_string(op, "lock", txn->idl->lock_name); | |
3501 | } | |
3502 | ||
475281c0 | 3503 | /* Add prerequisites and declarations of new rows. */ |
4e8e4213 | 3504 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
475281c0 BP |
3505 | /* XXX check that deleted rows exist even if no prereqs? */ |
3506 | if (row->prereqs) { | |
3eb14233 | 3507 | const struct ovsdb_idl_table_class *class = row->table->class_; |
475281c0 BP |
3508 | size_t n_columns = class->n_columns; |
3509 | struct json *op, *columns, *row_json; | |
3510 | size_t idx; | |
3511 | ||
3512 | op = json_object_create(); | |
3513 | json_array_add(operations, op); | |
3514 | json_object_put_string(op, "op", "wait"); | |
3515 | json_object_put_string(op, "table", class->name); | |
3516 | json_object_put(op, "timeout", json_integer_create(0)); | |
3517 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
3518 | json_object_put_string(op, "until", "=="); | |
3519 | columns = json_array_create_empty(); | |
3520 | json_object_put(op, "columns", columns); | |
3521 | row_json = json_object_create(); | |
3522 | json_object_put(op, "rows", json_array_create_1(row_json)); | |
3523 | ||
3524 | BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) { | |
3525 | const struct ovsdb_idl_column *column = &class->columns[idx]; | |
3526 | json_array_add(columns, json_string_create(column->name)); | |
3527 | json_object_put(row_json, column->name, | |
4e0b4acc | 3528 | ovsdb_datum_to_json(&row->old_datum[idx], |
475281c0 BP |
3529 | &column->type)); |
3530 | } | |
3531 | } | |
475281c0 BP |
3532 | } |
3533 | ||
3534 | /* Add updates. */ | |
3535 | any_updates = false; | |
4e8e4213 | 3536 | HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { |
3eb14233 | 3537 | const struct ovsdb_idl_table_class *class = row->table->class_; |
475281c0 | 3538 | |
fa50ab0b | 3539 | if (!row->new_datum) { |
dcd1dbc5 BP |
3540 | if (class->is_root) { |
3541 | struct json *op = json_object_create(); | |
3542 | json_object_put_string(op, "op", "delete"); | |
3543 | json_object_put_string(op, "table", class->name); | |
3544 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
3545 | json_array_add(operations, op); | |
3546 | any_updates = true; | |
3547 | } else { | |
3548 | /* Let ovsdb-server decide whether to really delete it. */ | |
3549 | } | |
4e0b4acc | 3550 | } else if (row->old_datum != row->new_datum) { |
f0f54cb4 BP |
3551 | struct json *row_json; |
3552 | struct json *op; | |
3553 | size_t idx; | |
3554 | ||
3555 | op = json_object_create(); | |
4e0b4acc JS |
3556 | json_object_put_string(op, "op", row->old_datum ? "update" |
3557 | : "insert"); | |
f0f54cb4 | 3558 | json_object_put_string(op, "table", class->name); |
4e0b4acc | 3559 | if (row->old_datum) { |
f0f54cb4 BP |
3560 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); |
3561 | } else { | |
69490970 BP |
3562 | struct ovsdb_idl_txn_insert *insert; |
3563 | ||
c15f1d11 BP |
3564 | any_updates = true; |
3565 | ||
f0f54cb4 BP |
3566 | json_object_put(op, "uuid-name", |
3567 | json_string_create_nocopy( | |
3568 | uuid_name_from_uuid(&row->uuid))); | |
69490970 BP |
3569 | |
3570 | insert = xmalloc(sizeof *insert); | |
3571 | insert->dummy = row->uuid; | |
9cb53f26 | 3572 | insert->op_index = operations->u.array.n - 1; |
69490970 BP |
3573 | uuid_zero(&insert->real); |
3574 | hmap_insert(&txn->inserted_rows, &insert->hmap_node, | |
3575 | uuid_hash(&insert->dummy)); | |
f0f54cb4 BP |
3576 | } |
3577 | row_json = json_object_create(); | |
3578 | json_object_put(op, "row", row_json); | |
3579 | ||
35c2ce98 JG |
3580 | if (row->written) { |
3581 | BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) { | |
3582 | const struct ovsdb_idl_column *column = | |
3583 | &class->columns[idx]; | |
3584 | ||
4e0b4acc | 3585 | if (row->old_datum |
fa50ab0b | 3586 | || !ovsdb_datum_is_default(&row->new_datum[idx], |
35c2ce98 | 3587 | &column->type)) { |
fa50ab0b JS |
3588 | struct json *value; |
3589 | ||
3590 | value = ovsdb_datum_to_json(&row->new_datum[idx], | |
3591 | &column->type); | |
35c2ce98 | 3592 | json_object_put(row_json, column->name, |
fa50ab0b | 3593 | substitute_uuids(value, txn)); |
c15f1d11 BP |
3594 | |
3595 | /* If anything really changed, consider it an update. | |
3596 | * We can't suppress not-really-changed values earlier | |
3597 | * or transactions would become nonatomic (see the big | |
3598 | * comment inside ovsdb_idl_txn_write()). */ | |
4e0b4acc JS |
3599 | if (!any_updates && row->old_datum && |
3600 | !ovsdb_datum_equals(&row->old_datum[idx], | |
fa50ab0b | 3601 | &row->new_datum[idx], |
c15f1d11 BP |
3602 | &column->type)) { |
3603 | any_updates = true; | |
3604 | } | |
35c2ce98 | 3605 | } |
475281c0 | 3606 | } |
f0f54cb4 BP |
3607 | } |
3608 | ||
4e0b4acc | 3609 | if (!row->old_datum || !shash_is_empty(json_object(row_json))) { |
f0f54cb4 | 3610 | json_array_add(operations, op); |
f0f54cb4 BP |
3611 | } else { |
3612 | json_destroy(op); | |
475281c0 BP |
3613 | } |
3614 | } | |
f199df26 | 3615 | |
f1ab6e06 RM |
3616 | /* Add mutate operation, for partial map or partial set updates. */ |
3617 | if (row->map_op_written || row->set_op_written) { | |
f199df26 EA |
3618 | struct json *op, *mutations; |
3619 | bool any_mutations; | |
3620 | ||
3621 | op = json_object_create(); | |
3622 | json_object_put_string(op, "op", "mutate"); | |
3623 | json_object_put_string(op, "table", class->name); | |
3624 | json_object_put(op, "where", where_uuid_equals(&row->uuid)); | |
3625 | mutations = json_array_create_empty(); | |
3626 | any_mutations = ovsdb_idl_txn_extract_mutations(row, mutations); | |
3627 | json_object_put(op, "mutations", mutations); | |
3628 | ||
3629 | if (any_mutations) { | |
3630 | op = substitute_uuids(op, txn); | |
3631 | json_array_add(operations, op); | |
3632 | any_updates = true; | |
3633 | } else { | |
3634 | json_destroy(op); | |
3635 | } | |
3636 | } | |
475281c0 BP |
3637 | } |
3638 | ||
b54e22e9 | 3639 | /* Add increment. */ |
de32cec7 BP |
3640 | if (txn->inc_table && (any_updates || txn->inc_force)) { |
3641 | any_updates = true; | |
9cb53f26 | 3642 | txn->inc_index = operations->u.array.n - 1; |
b54e22e9 | 3643 | |
de32cec7 | 3644 | struct json *op = json_object_create(); |
b54e22e9 BP |
3645 | json_object_put_string(op, "op", "mutate"); |
3646 | json_object_put_string(op, "table", txn->inc_table); | |
3647 | json_object_put(op, "where", | |
94fbe1aa BP |
3648 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
3649 | txn)); | |
b54e22e9 BP |
3650 | json_object_put(op, "mutations", |
3651 | json_array_create_1( | |
3652 | json_array_create_3( | |
3653 | json_string_create(txn->inc_column), | |
3654 | json_string_create("+="), | |
3655 | json_integer_create(1)))); | |
3656 | json_array_add(operations, op); | |
3657 | ||
3658 | op = json_object_create(); | |
3659 | json_object_put_string(op, "op", "select"); | |
3660 | json_object_put_string(op, "table", txn->inc_table); | |
3661 | json_object_put(op, "where", | |
94fbe1aa BP |
3662 | substitute_uuids(where_uuid_equals(&txn->inc_row), |
3663 | txn)); | |
b54e22e9 BP |
3664 | json_object_put(op, "columns", |
3665 | json_array_create_1(json_string_create( | |
3666 | txn->inc_column))); | |
3667 | json_array_add(operations, op); | |
3668 | } | |
3669 | ||
d171b584 BP |
3670 | if (txn->comment.length) { |
3671 | struct json *op = json_object_create(); | |
3672 | json_object_put_string(op, "op", "comment"); | |
3673 | json_object_put_string(op, "comment", ds_cstr(&txn->comment)); | |
3674 | json_array_add(operations, op); | |
3675 | } | |
3676 | ||
577aebdf BP |
3677 | if (txn->dry_run) { |
3678 | struct json *op = json_object_create(); | |
3679 | json_object_put_string(op, "op", "abort"); | |
3680 | json_array_add(operations, op); | |
3681 | } | |
3682 | ||
72c6edc5 | 3683 | if (!any_updates) { |
b54e22e9 | 3684 | txn->status = TXN_UNCHANGED; |
2f3ca7ea | 3685 | json_destroy(operations); |
72c6edc5 BP |
3686 | } else if (!jsonrpc_session_send( |
3687 | txn->idl->session, | |
3688 | jsonrpc_create_request( | |
3689 | "transact", operations, &txn->request_id))) { | |
3690 | hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node, | |
3691 | json_hash(txn->request_id, 0)); | |
2096903b | 3692 | txn->status = TXN_INCOMPLETE; |
72c6edc5 | 3693 | } else { |
854a94d9 | 3694 | txn->status = TXN_TRY_AGAIN; |
72c6edc5 | 3695 | } |
475281c0 | 3696 | |
60032110 | 3697 | disassemble_out: |
475281c0 | 3698 | ovsdb_idl_txn_disassemble(txn); |
60032110 RW |
3699 | coverage_out: |
3700 | switch (txn->status) { | |
3701 | case TXN_UNCOMMITTED: COVERAGE_INC(txn_uncommitted); break; | |
3702 | case TXN_UNCHANGED: COVERAGE_INC(txn_unchanged); break; | |
3703 | case TXN_INCOMPLETE: COVERAGE_INC(txn_incomplete); break; | |
3704 | case TXN_ABORTED: COVERAGE_INC(txn_aborted); break; | |
3705 | case TXN_SUCCESS: COVERAGE_INC(txn_success); break; | |
3706 | case TXN_TRY_AGAIN: COVERAGE_INC(txn_try_again); break; | |
3707 | case TXN_NOT_LOCKED: COVERAGE_INC(txn_not_locked); break; | |
3708 | case TXN_ERROR: COVERAGE_INC(txn_error); break; | |
3709 | } | |
3710 | ||
475281c0 BP |
3711 | return txn->status; |
3712 | } | |
3713 | ||
af96ccd2 BP |
3714 | /* Attempts to commit 'txn', blocking until the commit either succeeds or |
3715 | * fails. Returns the final commit status, which may be any TXN_* value other | |
2f926787 BP |
3716 | * than TXN_INCOMPLETE. |
3717 | * | |
3718 | * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the | |
3719 | * return value of ovsdb_idl_get_seqno() to change. */ | |
af96ccd2 BP |
3720 | enum ovsdb_idl_txn_status |
3721 | ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) | |
3722 | { | |
3723 | enum ovsdb_idl_txn_status status; | |
3724 | ||
b302749b | 3725 | fatal_signal_run(); |
af96ccd2 BP |
3726 | while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { |
3727 | ovsdb_idl_run(txn->idl); | |
3728 | ovsdb_idl_wait(txn->idl); | |
3729 | ovsdb_idl_txn_wait(txn); | |
3730 | poll_block(); | |
3731 | } | |
3732 | return status; | |
3733 | } | |
3734 | ||
2f926787 BP |
3735 | /* Returns the final (incremented) value of the column in 'txn' that was set to |
3736 | * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed | |
3737 | * successfully. */ | |
b54e22e9 BP |
3738 | int64_t |
3739 | ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) | |
3740 | { | |
cb22974d | 3741 | ovs_assert(txn->status == TXN_SUCCESS); |
b54e22e9 BP |
3742 | return txn->inc_new_value; |
3743 | } | |
3744 | ||
2f926787 BP |
3745 | /* Aborts 'txn' without sending it to the database server. This is effective |
3746 | * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'. | |
3747 | * Otherwise, it has no effect. | |
3748 | * | |
3749 | * Aborting a transaction doesn't free its memory. Use | |
3750 | * ovsdb_idl_txn_destroy() to do that. */ | |
475281c0 BP |
3751 | void |
3752 | ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) | |
3753 | { | |
3754 | ovsdb_idl_txn_disassemble(txn); | |
2096903b | 3755 | if (txn->status == TXN_UNCOMMITTED || txn->status == TXN_INCOMPLETE) { |
475281c0 BP |
3756 | txn->status = TXN_ABORTED; |
3757 | } | |
3758 | } | |
3759 | ||
2f926787 BP |
3760 | /* Returns a string that reports the error status for 'txn'. The caller must |
3761 | * not modify or free the returned string. A call to ovsdb_idl_txn_destroy() | |
3762 | * for 'txn' may free the returned string. | |
3763 | * | |
3764 | * The return value is ordinarily one of the strings that | |
3765 | * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed | |
3766 | * due to an error reported by the database server, the return value is that | |
3767 | * error. */ | |
91e310a5 BP |
3768 | const char * |
3769 | ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) | |
3770 | { | |
3771 | if (txn->status != TXN_ERROR) { | |
3772 | return ovsdb_idl_txn_status_to_string(txn->status); | |
3773 | } else if (txn->error) { | |
3774 | return txn->error; | |
3775 | } else { | |
3776 | return "no error details available"; | |
3777 | } | |
3778 | } | |
3779 | ||
3780 | static void | |
3781 | ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, | |
3782 | const struct json *json) | |
3783 | { | |
3784 | if (txn->error == NULL) { | |
3785 | txn->error = json_to_string(json, JSSF_SORT); | |
3786 | } | |
3787 | } | |
3788 | ||
69490970 BP |
3789 | /* For transaction 'txn' that completed successfully, finds and returns the |
3790 | * permanent UUID that the database assigned to a newly inserted row, given the | |
3791 | * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. | |
3792 | * | |
3793 | * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or | |
3794 | * if it was assigned by that function and then deleted by | |
3795 | * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted | |
3796 | * and then deleted within a single transaction are never sent to the database | |
3797 | * server, so it never assigns them a permanent UUID.) */ | |
3798 | const struct uuid * | |
3799 | ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn, | |
3800 | const struct uuid *uuid) | |
3801 | { | |
3802 | const struct ovsdb_idl_txn_insert *insert; | |
3803 | ||
cb22974d | 3804 | ovs_assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED); |
4e8e4213 | 3805 | HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node, |
69490970 BP |
3806 | uuid_hash(uuid), &txn->inserted_rows) { |
3807 | if (uuid_equals(uuid, &insert->dummy)) { | |
3808 | return &insert->real; | |
3809 | } | |
3810 | } | |
3811 | return NULL; | |
3812 | } | |
3813 | ||
475281c0 BP |
3814 | static void |
3815 | ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, | |
3816 | enum ovsdb_idl_txn_status status) | |
3817 | { | |
3818 | txn->status = status; | |
3819 | hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); | |
3820 | } | |
3821 | ||
fe19569a BP |
3822 | static void |
3823 | ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_, | |
3824 | const struct ovsdb_idl_column *column, | |
3825 | struct ovsdb_datum *datum, bool owns_datum) | |
475281c0 | 3826 | { |
ebc56baa | 3827 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9b446bfa BP |
3828 | const struct ovsdb_idl_table_class *class; |
3829 | size_t column_idx; | |
8cdec725 | 3830 | bool write_only; |
9b446bfa BP |
3831 | |
3832 | if (ovsdb_idl_row_is_synthetic(row)) { | |
fe19569a | 3833 | goto discard_datum; |
9b446bfa BP |
3834 | } |
3835 | ||
3eb14233 | 3836 | class = row->table->class_; |
9b446bfa | 3837 | column_idx = column - class->columns; |
8cdec725 | 3838 | write_only = row->table->modes[column_idx] == OVSDB_IDL_MONITOR; |
475281c0 | 3839 | |
fa50ab0b | 3840 | ovs_assert(row->new_datum != NULL); |
cb22974d | 3841 | ovs_assert(column_idx < class->n_columns); |
4e0b4acc | 3842 | ovs_assert(row->old_datum == NULL || |
cb22974d | 3843 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); |
c547535a | 3844 | |
8cdec725 EJ |
3845 | if (row->table->idl->verify_write_only && !write_only) { |
3846 | VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when" | |
3847 | " explicitly configured not to.", class->name, column->name); | |
fe19569a | 3848 | goto discard_datum; |
8cdec725 EJ |
3849 | } |
3850 | ||
1cc618c3 BP |
3851 | /* If this is a write-only column and the datum being written is the same |
3852 | * as the one already there, just skip the update entirely. This is worth | |
3853 | * optimizing because we have a lot of columns that get periodically | |
3854 | * refreshed into the database but don't actually change that often. | |
3855 | * | |
3856 | * We don't do this for read/write columns because that would break | |
3857 | * atomicity of transactions--some other client might have written a | |
c15f1d11 BP |
3858 | * different value in that column since we read it. (But if a whole |
3859 | * transaction only does writes of existing values, without making any real | |
3860 | * changes, we will drop the whole transaction later in | |
3861 | * ovsdb_idl_txn_commit().) */ | |
8cdec725 EJ |
3862 | if (write_only && ovsdb_datum_equals(ovsdb_idl_read(row, column), |
3863 | datum, &column->type)) { | |
fe19569a | 3864 | goto discard_datum; |
1cc618c3 BP |
3865 | } |
3866 | ||
475281c0 BP |
3867 | if (hmap_node_is_null(&row->txn_node)) { |
3868 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
3869 | uuid_hash(&row->uuid)); | |
3870 | } | |
4e0b4acc | 3871 | if (row->old_datum == row->new_datum) { |
fa50ab0b | 3872 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
475281c0 BP |
3873 | } |
3874 | if (!row->written) { | |
3875 | row->written = bitmap_allocate(class->n_columns); | |
3876 | } | |
3877 | if (bitmap_is_set(row->written, column_idx)) { | |
fa50ab0b | 3878 | ovsdb_datum_destroy(&row->new_datum[column_idx], &column->type); |
475281c0 BP |
3879 | } else { |
3880 | bitmap_set1(row->written, column_idx); | |
3881 | } | |
fe19569a | 3882 | if (owns_datum) { |
fa50ab0b | 3883 | row->new_datum[column_idx] = *datum; |
fe19569a | 3884 | } else { |
fa50ab0b | 3885 | ovsdb_datum_clone(&row->new_datum[column_idx], datum, &column->type); |
fe19569a | 3886 | } |
979821c0 | 3887 | (column->unparse)(row); |
fa50ab0b | 3888 | (column->parse)(row, &row->new_datum[column_idx]); |
fe19569a BP |
3889 | return; |
3890 | ||
3891 | discard_datum: | |
3892 | if (owns_datum) { | |
3893 | ovsdb_datum_destroy(datum, &column->type); | |
3894 | } | |
3895 | } | |
3896 | ||
36f4c3c8 BP |
3897 | /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_' |
3898 | * itself and the structs derived from it (e.g. the "struct ovsrec_*", for | |
3899 | * ovs-vswitchd). | |
3900 | * | |
aa8628aa BP |
3901 | * 'datum' must have the correct type for its column, but it needs not be |
3902 | * sorted or unique because this function will take care of that. The IDL does | |
3903 | * not check that it meets schema constraints, but ovsdb-server will do so at | |
3904 | * commit time so it had better be correct. | |
36f4c3c8 BP |
3905 | * |
3906 | * A transaction must be in progress. Replication of 'column' must not have | |
3907 | * been disabled (by calling ovsdb_idl_omit()). | |
3908 | * | |
3909 | * Usually this function is used indirectly through one of the "set" functions | |
3910 | * generated by ovsdb-idlc. | |
3911 | * | |
3912 | * Takes ownership of what 'datum' points to (and in some cases destroys that | |
3913 | * data before returning) but makes a copy of 'datum' itself. (Commonly | |
3914 | * 'datum' is on the caller's stack.) */ | |
fe19569a BP |
3915 | void |
3916 | ovsdb_idl_txn_write(const struct ovsdb_idl_row *row, | |
3917 | const struct ovsdb_idl_column *column, | |
3918 | struct ovsdb_datum *datum) | |
3919 | { | |
aa8628aa BP |
3920 | ovsdb_datum_sort_unique(datum, |
3921 | column->type.key.type, column->type.value.type); | |
fe19569a BP |
3922 | ovsdb_idl_txn_write__(row, column, datum, true); |
3923 | } | |
3924 | ||
aa8628aa BP |
3925 | /* Similar to ovsdb_idl_txn_write(), except: |
3926 | * | |
3927 | * - The caller retains ownership of 'datum' and what it points to. | |
3928 | * | |
3929 | * - The caller must ensure that 'datum' is sorted and unique (e.g. via | |
3930 | * ovsdb_datum_sort_unique().) */ | |
fe19569a BP |
3931 | void |
3932 | ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row, | |
3933 | const struct ovsdb_idl_column *column, | |
3934 | const struct ovsdb_datum *datum) | |
3935 | { | |
3936 | ovsdb_idl_txn_write__(row, column, | |
3937 | CONST_CAST(struct ovsdb_datum *, datum), false); | |
475281c0 BP |
3938 | } |
3939 | ||
b827c67b BP |
3940 | /* Causes the original contents of 'column' in 'row_' to be verified as a |
3941 | * prerequisite to completing the transaction. That is, if 'column' in 'row_' | |
3942 | * changed (or if 'row_' was deleted) between the time that the IDL originally | |
3943 | * read its contents and the time that the transaction commits, then the | |
b17c8228 | 3944 | * transaction aborts and ovsdb_idl_txn_commit() returns TXN_TRY_AGAIN. |
b827c67b BP |
3945 | * |
3946 | * The intention is that, to ensure that no transaction commits based on dirty | |
3947 | * reads, an application should call ovsdb_idl_txn_verify() on each data item | |
3948 | * read as part of a read-modify-write operation. | |
3949 | * | |
3950 | * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current | |
3951 | * value of 'column' is already known: | |
3952 | * | |
3953 | * - If 'row_' is a row created by the current transaction (returned by | |
3954 | * ovsdb_idl_txn_insert()). | |
3955 | * | |
3956 | * - If 'column' has already been modified (with ovsdb_idl_txn_write()) | |
3957 | * within the current transaction. | |
3958 | * | |
3959 | * Because of the latter property, always call ovsdb_idl_txn_verify() *before* | |
3960 | * ovsdb_idl_txn_write() for a given read-modify-write. | |
3961 | * | |
3962 | * A transaction must be in progress. | |
3963 | * | |
3964 | * Usually this function is used indirectly through one of the "verify" | |
3965 | * functions generated by ovsdb-idlc. */ | |
475281c0 BP |
3966 | void |
3967 | ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_, | |
3968 | const struct ovsdb_idl_column *column) | |
3969 | { | |
ebc56baa | 3970 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9b446bfa BP |
3971 | const struct ovsdb_idl_table_class *class; |
3972 | size_t column_idx; | |
3973 | ||
3974 | if (ovsdb_idl_row_is_synthetic(row)) { | |
3975 | return; | |
3976 | } | |
3977 | ||
3eb14233 | 3978 | class = row->table->class_; |
9b446bfa | 3979 | column_idx = column - class->columns; |
475281c0 | 3980 | |
fa50ab0b | 3981 | ovs_assert(row->new_datum != NULL); |
4e0b4acc | 3982 | ovs_assert(row->old_datum == NULL || |
cb22974d | 3983 | row->table->modes[column_idx] & OVSDB_IDL_MONITOR); |
4e0b4acc | 3984 | if (!row->old_datum |
475281c0 BP |
3985 | || (row->written && bitmap_is_set(row->written, column_idx))) { |
3986 | return; | |
3987 | } | |
3988 | ||
3989 | if (hmap_node_is_null(&row->txn_node)) { | |
3990 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
3991 | uuid_hash(&row->uuid)); | |
3992 | } | |
3993 | if (!row->prereqs) { | |
3994 | row->prereqs = bitmap_allocate(class->n_columns); | |
3995 | } | |
3996 | bitmap_set1(row->prereqs, column_idx); | |
3997 | } | |
3998 | ||
b827c67b BP |
3999 | /* Deletes 'row_' from its table. May free 'row_', so it must not be |
4000 | * accessed afterward. | |
4001 | * | |
4002 | * A transaction must be in progress. | |
4003 | * | |
4004 | * Usually this function is used indirectly through one of the "delete" | |
4005 | * functions generated by ovsdb-idlc. */ | |
475281c0 | 4006 | void |
9e336f49 | 4007 | ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) |
475281c0 | 4008 | { |
ebc56baa | 4009 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); |
9e336f49 | 4010 | |
9b446bfa BP |
4011 | if (ovsdb_idl_row_is_synthetic(row)) { |
4012 | return; | |
4013 | } | |
4014 | ||
fa50ab0b | 4015 | ovs_assert(row->new_datum != NULL); |
4e0b4acc | 4016 | if (!row->old_datum) { |
0196cc15 | 4017 | ovsdb_idl_row_unparse(row); |
475281c0 | 4018 | ovsdb_idl_row_clear_new(row); |
cb22974d | 4019 | ovs_assert(!row->prereqs); |
979821c0 | 4020 | hmap_remove(&row->table->rows, &row->hmap_node); |
475281c0 BP |
4021 | hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node); |
4022 | free(row); | |
c7f7adb7 | 4023 | return; |
475281c0 BP |
4024 | } |
4025 | if (hmap_node_is_null(&row->txn_node)) { | |
4026 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
4027 | uuid_hash(&row->uuid)); | |
4028 | } | |
1ebeed63 | 4029 | ovsdb_idl_row_clear_new(row); |
fa50ab0b | 4030 | row->new_datum = NULL; |
475281c0 BP |
4031 | } |
4032 | ||
b827c67b BP |
4033 | /* Inserts and returns a new row in the table with the specified 'class' in the |
4034 | * database with open transaction 'txn'. | |
4035 | * | |
4036 | * The new row is assigned a provisional UUID. If 'uuid' is null then one is | |
4037 | * randomly generated; otherwise 'uuid' should specify a randomly generated | |
4038 | * UUID not otherwise in use. ovsdb-server will assign a different UUID when | |
4039 | * 'txn' is committed, but the IDL will replace any uses of the provisional | |
4040 | * UUID in the data to be to be committed by the UUID assigned by | |
4041 | * ovsdb-server. | |
4042 | * | |
4043 | * Usually this function is used indirectly through one of the "insert" | |
4044 | * functions generated by ovsdb-idlc. */ | |
9e336f49 | 4045 | const struct ovsdb_idl_row * |
475281c0 | 4046 | ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, |
ce5a3e38 BP |
4047 | const struct ovsdb_idl_table_class *class, |
4048 | const struct uuid *uuid) | |
475281c0 BP |
4049 | { |
4050 | struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); | |
ce5a3e38 BP |
4051 | |
4052 | if (uuid) { | |
cb22974d | 4053 | ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid)); |
ce5a3e38 BP |
4054 | row->uuid = *uuid; |
4055 | } else { | |
4056 | uuid_generate(&row->uuid); | |
4057 | } | |
4058 | ||
475281c0 | 4059 | row->table = ovsdb_idl_table_from_class(txn->idl, class); |
fa50ab0b | 4060 | row->new_datum = xmalloc(class->n_columns * sizeof *row->new_datum); |
979821c0 | 4061 | hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid)); |
475281c0 BP |
4062 | hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); |
4063 | return row; | |
4064 | } | |
4065 | ||
4066 | static void | |
4067 | ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl) | |
4068 | { | |
4069 | struct ovsdb_idl_txn *txn; | |
4070 | ||
4e8e4213 | 4071 | HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) { |
854a94d9 | 4072 | ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN); |
475281c0 BP |
4073 | } |
4074 | } | |
4075 | ||
4076 | static struct ovsdb_idl_txn * | |
4077 | ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id) | |
4078 | { | |
4079 | struct ovsdb_idl_txn *txn; | |
4080 | ||
4e8e4213 | 4081 | HMAP_FOR_EACH_WITH_HASH (txn, hmap_node, |
475281c0 BP |
4082 | json_hash(id, 0), &idl->outstanding_txns) { |
4083 | if (json_equal(id, txn->request_id)) { | |
4084 | return txn; | |
4085 | } | |
4086 | } | |
4087 | return NULL; | |
4088 | } | |
4089 | ||
b54e22e9 BP |
4090 | static bool |
4091 | check_json_type(const struct json *json, enum json_type type, const char *name) | |
4092 | { | |
4093 | if (!json) { | |
4094 | VLOG_WARN_RL(&syntax_rl, "%s is missing", name); | |
4095 | return false; | |
4096 | } else if (json->type != type) { | |
4097 | VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s", | |
4098 | name, json_type_to_string(json->type), | |
4099 | json_type_to_string(type)); | |
4100 | return false; | |
4101 | } else { | |
4102 | return true; | |
4103 | } | |
4104 | } | |
4105 | ||
4106 | static bool | |
4107 | ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn, | |
4108 | const struct json_array *results) | |
4109 | { | |
4110 | struct json *count, *rows, *row, *column; | |
4111 | struct shash *mutate, *select; | |
4112 | ||
4113 | if (txn->inc_index + 2 > results->n) { | |
4114 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
34582733 | 4115 | "for increment (has %"PRIuSIZE", needs %u)", |
b54e22e9 BP |
4116 | results->n, txn->inc_index + 2); |
4117 | return false; | |
4118 | } | |
4119 | ||
69490970 | 4120 | /* We know that this is a JSON object because the loop in |
b54e22e9 BP |
4121 | * ovsdb_idl_txn_process_reply() checked. */ |
4122 | mutate = json_object(results->elems[txn->inc_index]); | |
4123 | count = shash_find_data(mutate, "count"); | |
4124 | if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) { | |
4125 | return false; | |
4126 | } | |
4127 | if (count->u.integer != 1) { | |
4128 | VLOG_WARN_RL(&syntax_rl, | |
6fce4487 | 4129 | "\"mutate\" reply \"count\" is %lld instead of 1", |
b54e22e9 BP |
4130 | count->u.integer); |
4131 | return false; | |
4132 | } | |
4133 | ||
4134 | select = json_object(results->elems[txn->inc_index + 1]); | |
4135 | rows = shash_find_data(select, "rows"); | |
4136 | if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) { | |
4137 | return false; | |
4138 | } | |
4139 | if (rows->u.array.n != 1) { | |
34582733 | 4140 | VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements " |
b54e22e9 BP |
4141 | "instead of 1", |
4142 | rows->u.array.n); | |
4143 | return false; | |
4144 | } | |
4145 | row = rows->u.array.elems[0]; | |
4146 | if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) { | |
4147 | return false; | |
4148 | } | |
4149 | column = shash_find_data(json_object(row), txn->inc_column); | |
4150 | if (!check_json_type(column, JSON_INTEGER, | |
4151 | "\"select\" reply inc column")) { | |
4152 | return false; | |
4153 | } | |
4154 | txn->inc_new_value = column->u.integer; | |
4155 | return true; | |
4156 | } | |
4157 | ||
69490970 BP |
4158 | static bool |
4159 | ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, | |
4160 | const struct json_array *results) | |
4161 | { | |
bd76d25d | 4162 | static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; |
69490970 BP |
4163 | struct ovsdb_error *error; |
4164 | struct json *json_uuid; | |
4165 | union ovsdb_atom uuid; | |
4166 | struct shash *reply; | |
4167 | ||
4168 | if (insert->op_index >= results->n) { | |
4169 | VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " | |
34582733 | 4170 | "for insert (has %"PRIuSIZE", needs %u)", |
69490970 BP |
4171 | results->n, insert->op_index); |
4172 | return false; | |
4173 | } | |
4174 | ||
4175 | /* We know that this is a JSON object because the loop in | |
4176 | * ovsdb_idl_txn_process_reply() checked. */ | |
4177 | reply = json_object(results->elems[insert->op_index]); | |
4178 | json_uuid = shash_find_data(reply, "uuid"); | |
4179 | if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) { | |
4180 | return false; | |
4181 | } | |
4182 | ||
bd76d25d | 4183 | error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); |
69490970 BP |
4184 | if (error) { |
4185 | char *s = ovsdb_error_to_string(error); | |
4186 | VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " | |
4187 | "UUID: %s", s); | |
4188 | free(s); | |
9582c4f5 | 4189 | ovsdb_error_destroy(error); |
69490970 BP |
4190 | return false; |
4191 | } | |
4192 | ||
4193 | insert->real = uuid.uuid; | |
4194 | ||
4195 | return true; | |
4196 | } | |
b54e22e9 | 4197 | |
475281c0 BP |
4198 | static bool |
4199 | ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, | |
4200 | const struct jsonrpc_msg *msg) | |
4201 | { | |
4202 | struct ovsdb_idl_txn *txn; | |
4203 | enum ovsdb_idl_txn_status status; | |
4204 | ||
4205 | txn = ovsdb_idl_txn_find(idl, msg->id); | |
4206 | if (!txn) { | |
4207 | return false; | |
4208 | } | |
4209 | ||
4210 | if (msg->type == JSONRPC_ERROR) { | |
4211 | status = TXN_ERROR; | |
4212 | } else if (msg->result->type != JSON_ARRAY) { | |
4213 | VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array"); | |
4214 | status = TXN_ERROR; | |
4215 | } else { | |
69490970 | 4216 | struct json_array *ops = &msg->result->u.array; |
475281c0 BP |
4217 | int hard_errors = 0; |
4218 | int soft_errors = 0; | |
06b6d651 | 4219 | int lock_errors = 0; |
475281c0 BP |
4220 | size_t i; |
4221 | ||
69490970 BP |
4222 | for (i = 0; i < ops->n; i++) { |
4223 | struct json *op = ops->elems[i]; | |
475281c0 | 4224 | |
69490970 | 4225 | if (op->type == JSON_NULL) { |
475281c0 BP |
4226 | /* This isn't an error in itself but indicates that some prior |
4227 | * operation failed, so make sure that we know about it. */ | |
4228 | soft_errors++; | |
69490970 | 4229 | } else if (op->type == JSON_OBJECT) { |
475281c0 BP |
4230 | struct json *error; |
4231 | ||
69490970 | 4232 | error = shash_find_data(json_object(op), "error"); |
475281c0 BP |
4233 | if (error) { |
4234 | if (error->type == JSON_STRING) { | |
4235 | if (!strcmp(error->u.string, "timed out")) { | |
4236 | soft_errors++; | |
06b6d651 BP |
4237 | } else if (!strcmp(error->u.string, "not owner")) { |
4238 | lock_errors++; | |
d6db7b3c LR |
4239 | } else if (!strcmp(error->u.string, "not allowed")) { |
4240 | hard_errors++; | |
4241 | ovsdb_idl_txn_set_error_json(txn, op); | |
577aebdf | 4242 | } else if (strcmp(error->u.string, "aborted")) { |
475281c0 | 4243 | hard_errors++; |
91e310a5 | 4244 | ovsdb_idl_txn_set_error_json(txn, op); |
d6db7b3c LR |
4245 | VLOG_WARN_RL(&other_rl, |
4246 | "transaction error: %s", txn->error); | |
475281c0 BP |
4247 | } |
4248 | } else { | |
4249 | hard_errors++; | |
91e310a5 | 4250 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
4251 | VLOG_WARN_RL(&syntax_rl, |
4252 | "\"error\" in reply is not JSON string"); | |
4253 | } | |
4254 | } | |
4255 | } else { | |
4256 | hard_errors++; | |
91e310a5 | 4257 | ovsdb_idl_txn_set_error_json(txn, op); |
475281c0 BP |
4258 | VLOG_WARN_RL(&syntax_rl, |
4259 | "operation reply is not JSON null or object"); | |
4260 | } | |
4261 | } | |
4262 | ||
06b6d651 | 4263 | if (!soft_errors && !hard_errors && !lock_errors) { |
69490970 BP |
4264 | struct ovsdb_idl_txn_insert *insert; |
4265 | ||
4266 | if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) { | |
4267 | hard_errors++; | |
4268 | } | |
4269 | ||
4e8e4213 | 4270 | HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) { |
69490970 BP |
4271 | if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) { |
4272 | hard_errors++; | |
4273 | } | |
4274 | } | |
b54e22e9 BP |
4275 | } |
4276 | ||
475281c0 | 4277 | status = (hard_errors ? TXN_ERROR |
06b6d651 | 4278 | : lock_errors ? TXN_NOT_LOCKED |
854a94d9 | 4279 | : soft_errors ? TXN_TRY_AGAIN |
475281c0 BP |
4280 | : TXN_SUCCESS); |
4281 | } | |
4282 | ||
4283 | ovsdb_idl_txn_complete(txn, status); | |
4284 | return true; | |
4285 | } | |
76c91af9 | 4286 | |
2f926787 BP |
4287 | /* Returns the transaction currently active for 'row''s IDL. A transaction |
4288 | * must currently be active. */ | |
76c91af9 BP |
4289 | struct ovsdb_idl_txn * |
4290 | ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) | |
4291 | { | |
4292 | struct ovsdb_idl_txn *txn = row->table->idl->txn; | |
cb22974d | 4293 | ovs_assert(txn != NULL); |
76c91af9 BP |
4294 | return txn; |
4295 | } | |
1e86ae6f | 4296 | |
2f926787 | 4297 | /* Returns the IDL on which 'txn' acts. */ |
1e86ae6f BP |
4298 | struct ovsdb_idl * |
4299 | ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) | |
4300 | { | |
4301 | return txn->idl; | |
4302 | } | |
a660eac8 AW |
4303 | |
4304 | /* Blocks until 'idl' successfully connects to the remote database and | |
4305 | * retrieves its contents. */ | |
4306 | void | |
4307 | ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *idl) | |
4308 | { | |
4309 | while (1) { | |
4310 | ovsdb_idl_run(idl); | |
4311 | if (ovsdb_idl_has_ever_connected(idl)) { | |
4312 | return; | |
4313 | } | |
4314 | ovsdb_idl_wait(idl); | |
4315 | poll_block(); | |
4316 | } | |
4317 | } | |
06b6d651 BP |
4318 | \f |
4319 | /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from | |
4320 | * the database server and to avoid modifying the database when the lock cannot | |
4321 | * be acquired (that is, when another client has the same lock). | |
4322 | * | |
4323 | * If 'lock_name' is NULL, drops the locking requirement and releases the | |
4324 | * lock. */ | |
4325 | void | |
4326 | ovsdb_idl_set_lock(struct ovsdb_idl *idl, const char *lock_name) | |
4327 | { | |
cb22974d BP |
4328 | ovs_assert(!idl->txn); |
4329 | ovs_assert(hmap_is_empty(&idl->outstanding_txns)); | |
06b6d651 BP |
4330 | |
4331 | if (idl->lock_name && (!lock_name || strcmp(lock_name, idl->lock_name))) { | |
4332 | /* Release previous lock. */ | |
4333 | ovsdb_idl_send_unlock_request(idl); | |
4334 | free(idl->lock_name); | |
4335 | idl->lock_name = NULL; | |
4336 | idl->is_lock_contended = false; | |
4337 | } | |
4338 | ||
4339 | if (lock_name && !idl->lock_name) { | |
4340 | /* Acquire new lock. */ | |
4341 | idl->lock_name = xstrdup(lock_name); | |
4342 | ovsdb_idl_send_lock_request(idl); | |
4343 | } | |
4344 | } | |
4345 | ||
4346 | /* Returns true if 'idl' is configured to obtain a lock and owns that lock. | |
4347 | * | |
4348 | * Locking and unlocking happens asynchronously from the database client's | |
4349 | * point of view, so the information is only useful for optimization (e.g. if | |
4350 | * the client doesn't have the lock then there's no point in trying to write to | |
4351 | * the database). */ | |
4352 | bool | |
4353 | ovsdb_idl_has_lock(const struct ovsdb_idl *idl) | |
4354 | { | |
4355 | return idl->has_lock; | |
4356 | } | |
4357 | ||
4358 | /* Returns true if 'idl' is configured to obtain a lock but the database server | |
4359 | * has indicated that some other client already owns the requested lock. */ | |
4360 | bool | |
4361 | ovsdb_idl_is_lock_contended(const struct ovsdb_idl *idl) | |
4362 | { | |
4363 | return idl->is_lock_contended; | |
4364 | } | |
4365 | ||
4366 | static void | |
4367 | ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock) | |
4368 | { | |
4369 | if (new_has_lock && !idl->has_lock) { | |
db2b5757 | 4370 | if (idl->state == IDL_S_MONITORING || |
c383f3bf | 4371 | idl->state == IDL_S_MONITORING_COND) { |
06b6d651 BP |
4372 | idl->change_seqno++; |
4373 | } else { | |
d18e52e3 BP |
4374 | /* We're setting up a session, so don't signal that the database |
4375 | * changed. Finalizing the session will increment change_seqno | |
06b6d651 BP |
4376 | * anyhow. */ |
4377 | } | |
4378 | idl->is_lock_contended = false; | |
4379 | } | |
4380 | idl->has_lock = new_has_lock; | |
4381 | } | |
4382 | ||
4383 | static void | |
4384 | ovsdb_idl_send_lock_request__(struct ovsdb_idl *idl, const char *method, | |
4385 | struct json **idp) | |
4386 | { | |
4387 | ovsdb_idl_update_has_lock(idl, false); | |
4388 | ||
4389 | json_destroy(idl->lock_request_id); | |
4390 | idl->lock_request_id = NULL; | |
4391 | ||
4392 | if (jsonrpc_session_is_connected(idl->session)) { | |
4393 | struct json *params; | |
4394 | ||
4395 | params = json_array_create_1(json_string_create(idl->lock_name)); | |
4396 | jsonrpc_session_send(idl->session, | |
4397 | jsonrpc_create_request(method, params, idp)); | |
4398 | } | |
4399 | } | |
1e86ae6f | 4400 | |
06b6d651 BP |
4401 | static void |
4402 | ovsdb_idl_send_lock_request(struct ovsdb_idl *idl) | |
4403 | { | |
4404 | ovsdb_idl_send_lock_request__(idl, "lock", &idl->lock_request_id); | |
4405 | } | |
4406 | ||
4407 | static void | |
4408 | ovsdb_idl_send_unlock_request(struct ovsdb_idl *idl) | |
4409 | { | |
4410 | ovsdb_idl_send_lock_request__(idl, "unlock", NULL); | |
4411 | } | |
4412 | ||
4413 | static void | |
4414 | ovsdb_idl_parse_lock_reply(struct ovsdb_idl *idl, const struct json *result) | |
4415 | { | |
4416 | bool got_lock; | |
4417 | ||
4418 | json_destroy(idl->lock_request_id); | |
4419 | idl->lock_request_id = NULL; | |
4420 | ||
4421 | if (result->type == JSON_OBJECT) { | |
4422 | const struct json *locked; | |
4423 | ||
4424 | locked = shash_find_data(json_object(result), "locked"); | |
4425 | got_lock = locked && locked->type == JSON_TRUE; | |
4426 | } else { | |
4427 | got_lock = false; | |
4428 | } | |
4429 | ||
4430 | ovsdb_idl_update_has_lock(idl, got_lock); | |
4431 | if (!got_lock) { | |
4432 | idl->is_lock_contended = true; | |
4433 | } | |
4434 | } | |
4435 | ||
4436 | static void | |
4437 | ovsdb_idl_parse_lock_notify(struct ovsdb_idl *idl, | |
4438 | const struct json *params, | |
4439 | bool new_has_lock) | |
4440 | { | |
4441 | if (idl->lock_name | |
4442 | && params->type == JSON_ARRAY | |
4443 | && json_array(params)->n > 0 | |
4444 | && json_array(params)->elems[0]->type == JSON_STRING) { | |
4445 | const char *lock_name = json_string(json_array(params)->elems[0]); | |
4446 | ||
4447 | if (!strcmp(idl->lock_name, lock_name)) { | |
4448 | ovsdb_idl_update_has_lock(idl, new_has_lock); | |
4449 | if (!new_has_lock) { | |
4450 | idl->is_lock_contended = true; | |
4451 | } | |
4452 | } | |
4453 | } | |
4454 | } | |
a548a764 | 4455 | |
f199df26 EA |
4456 | /* Inserts a new Map Operation into current transaction. */ |
4457 | static void | |
4458 | ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *row, | |
4459 | const struct ovsdb_idl_column *column, | |
4460 | struct ovsdb_datum *datum, | |
4461 | enum map_op_type op_type) | |
4462 | { | |
4463 | const struct ovsdb_idl_table_class *class; | |
4464 | size_t column_idx; | |
4465 | struct map_op *map_op; | |
4466 | ||
3eb14233 | 4467 | class = row->table->class_; |
f199df26 EA |
4468 | column_idx = column - class->columns; |
4469 | ||
4470 | /* Check if a map operation list exists for this column. */ | |
4471 | if (!row->map_op_written) { | |
4472 | row->map_op_written = bitmap_allocate(class->n_columns); | |
4473 | row->map_op_lists = xzalloc(class->n_columns * | |
4474 | sizeof *row->map_op_lists); | |
4475 | } | |
4476 | if (!row->map_op_lists[column_idx]) { | |
4477 | row->map_op_lists[column_idx] = map_op_list_create(); | |
4478 | } | |
4479 | ||
4480 | /* Add a map operation to the corresponding list. */ | |
4481 | map_op = map_op_create(datum, op_type); | |
4482 | bitmap_set1(row->map_op_written, column_idx); | |
4483 | map_op_list_add(row->map_op_lists[column_idx], map_op, &column->type); | |
4484 | ||
4485 | /* Add this row to transaction's list of rows. */ | |
4486 | if (hmap_node_is_null(&row->txn_node)) { | |
4487 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
4488 | uuid_hash(&row->uuid)); | |
4489 | } | |
4490 | } | |
4491 | ||
f1ab6e06 RM |
4492 | /* Inserts a new Set Operation into current transaction. */ |
4493 | static void | |
4494 | ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *row, | |
4495 | const struct ovsdb_idl_column *column, | |
4496 | struct ovsdb_datum *datum, | |
4497 | enum set_op_type op_type) | |
4498 | { | |
4499 | const struct ovsdb_idl_table_class *class; | |
4500 | size_t column_idx; | |
4501 | struct set_op *set_op; | |
4502 | ||
3eb14233 | 4503 | class = row->table->class_; |
f1ab6e06 RM |
4504 | column_idx = column - class->columns; |
4505 | ||
4506 | /* Check if a set operation list exists for this column. */ | |
4507 | if (!row->set_op_written) { | |
4508 | row->set_op_written = bitmap_allocate(class->n_columns); | |
4509 | row->set_op_lists = xzalloc(class->n_columns * | |
4510 | sizeof *row->set_op_lists); | |
4511 | } | |
4512 | if (!row->set_op_lists[column_idx]) { | |
4513 | row->set_op_lists[column_idx] = set_op_list_create(); | |
4514 | } | |
4515 | ||
4516 | /* Add a set operation to the corresponding list. */ | |
4517 | set_op = set_op_create(datum, op_type); | |
4518 | bitmap_set1(row->set_op_written, column_idx); | |
4519 | set_op_list_add(row->set_op_lists[column_idx], set_op, &column->type); | |
4520 | ||
4521 | /* Add this row to the transactions's list of rows. */ | |
4522 | if (hmap_node_is_null(&row->txn_node)) { | |
4523 | hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, | |
4524 | uuid_hash(&row->uuid)); | |
4525 | } | |
4526 | } | |
4527 | ||
f199df26 EA |
4528 | static bool |
4529 | is_valid_partial_update(const struct ovsdb_idl_row *row, | |
4530 | const struct ovsdb_idl_column *column, | |
4531 | struct ovsdb_datum *datum) | |
4532 | { | |
4533 | /* Verify that this column is being monitored. */ | |
3eb14233 | 4534 | unsigned int column_idx = column - row->table->class_->columns; |
f199df26 EA |
4535 | if (!(row->table->modes[column_idx] & OVSDB_IDL_MONITOR)) { |
4536 | VLOG_WARN("cannot partially update non-monitored column"); | |
4537 | return false; | |
4538 | } | |
4539 | ||
4540 | /* Verify that the update affects a single element. */ | |
4541 | if (datum->n != 1) { | |
4542 | VLOG_WARN("invalid datum for partial update"); | |
4543 | return false; | |
4544 | } | |
4545 | ||
4546 | return true; | |
4547 | } | |
4548 | ||
f1ab6e06 RM |
4549 | /* Inserts the value described in 'datum' into the map in 'column' in |
4550 | * 'row_'. If the value doesn't already exist in 'column' then it's value | |
4551 | * is added. The value in 'datum' must be of the same type as the values | |
4552 | * in 'column'. This function takes ownership of 'datum'. | |
4553 | * | |
4554 | * Usually this function is used indirectly through one of the "update" | |
4555 | * functions generated by vswitch-idl. */ | |
4556 | void | |
4557 | ovsdb_idl_txn_write_partial_set(const struct ovsdb_idl_row *row_, | |
4558 | const struct ovsdb_idl_column *column, | |
4559 | struct ovsdb_datum *datum) | |
4560 | { | |
4561 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); | |
4562 | enum set_op_type op_type; | |
4563 | ||
4564 | if (!is_valid_partial_update(row, column, datum)) { | |
4565 | ovsdb_datum_destroy(datum, &column->type); | |
4566 | free(datum); | |
4567 | return; | |
4568 | } | |
4569 | ||
4570 | op_type = SET_OP_INSERT; | |
4571 | ||
4572 | ovsdb_idl_txn_add_set_op(row, column, datum, op_type); | |
4573 | } | |
4574 | ||
4575 | /* Deletes the value specified in 'datum' from the set in 'column' in 'row_'. | |
4576 | * The value in 'datum' must be of the same type as the keys in 'column'. | |
4577 | * This function takes ownership of 'datum'. | |
4578 | * | |
4579 | * Usually this function is used indirectly through one of the "update" | |
4580 | * functions generated by vswitch-idl. */ | |
4581 | void | |
4582 | ovsdb_idl_txn_delete_partial_set(const struct ovsdb_idl_row *row_, | |
4583 | const struct ovsdb_idl_column *column, | |
4584 | struct ovsdb_datum *datum) | |
4585 | { | |
4586 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); | |
4587 | ||
4588 | if (!is_valid_partial_update(row, column, datum)) { | |
4589 | struct ovsdb_type type_ = column->type; | |
4590 | type_.value.type = OVSDB_TYPE_VOID; | |
4591 | ovsdb_datum_destroy(datum, &type_); | |
4592 | free(datum); | |
4593 | return; | |
4594 | } | |
4595 | ovsdb_idl_txn_add_set_op(row, column, datum, SET_OP_DELETE); | |
4596 | } | |
4597 | ||
f199df26 EA |
4598 | /* Inserts the key-value specified in 'datum' into the map in 'column' in |
4599 | * 'row_'. If the key already exist in 'column', then it's value is updated | |
4600 | * with the value in 'datum'. The key-value in 'datum' must be of the same type | |
4601 | * as the keys-values in 'column'. This function takes ownership of 'datum'. | |
4602 | * | |
4603 | * Usually this function is used indirectly through one of the "update" | |
4604 | * functions generated by vswitch-idl. */ | |
4605 | void | |
4606 | ovsdb_idl_txn_write_partial_map(const struct ovsdb_idl_row *row_, | |
4607 | const struct ovsdb_idl_column *column, | |
4608 | struct ovsdb_datum *datum) | |
4609 | { | |
4610 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); | |
4611 | enum ovsdb_atomic_type key_type; | |
4612 | enum map_op_type op_type; | |
4613 | unsigned int pos; | |
4614 | const struct ovsdb_datum *old_datum; | |
4615 | ||
4616 | if (!is_valid_partial_update(row, column, datum)) { | |
4617 | ovsdb_datum_destroy(datum, &column->type); | |
b1048e6a | 4618 | free(datum); |
f199df26 EA |
4619 | return; |
4620 | } | |
4621 | ||
4622 | /* Find out if this is an insert or an update. */ | |
4623 | key_type = column->type.key.type; | |
4624 | old_datum = ovsdb_idl_read(row, column); | |
4625 | pos = ovsdb_datum_find_key(old_datum, &datum->keys[0], key_type); | |
4626 | op_type = pos == UINT_MAX ? MAP_OP_INSERT : MAP_OP_UPDATE; | |
4627 | ||
4628 | ovsdb_idl_txn_add_map_op(row, column, datum, op_type); | |
4629 | } | |
4630 | ||
4631 | /* Deletes the key specified in 'datum' from the map in 'column' in 'row_'. | |
4632 | * The key in 'datum' must be of the same type as the keys in 'column'. | |
4633 | * The value in 'datum' must be NULL. This function takes ownership of | |
4634 | * 'datum'. | |
4635 | * | |
4636 | * Usually this function is used indirectly through one of the "update" | |
4637 | * functions generated by vswitch-idl. */ | |
4638 | void | |
4639 | ovsdb_idl_txn_delete_partial_map(const struct ovsdb_idl_row *row_, | |
4640 | const struct ovsdb_idl_column *column, | |
4641 | struct ovsdb_datum *datum) | |
4642 | { | |
4643 | struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); | |
4644 | ||
4645 | if (!is_valid_partial_update(row, column, datum)) { | |
4646 | struct ovsdb_type type_ = column->type; | |
4647 | type_.value.type = OVSDB_TYPE_VOID; | |
4648 | ovsdb_datum_destroy(datum, &type_); | |
b1048e6a | 4649 | free(datum); |
f199df26 EA |
4650 | return; |
4651 | } | |
4652 | ovsdb_idl_txn_add_map_op(row, column, datum, MAP_OP_DELETE); | |
4653 | } | |
4654 | ||
a548a764 AW |
4655 | void |
4656 | ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *loop) | |
4657 | { | |
4658 | if (loop) { | |
4659 | ovsdb_idl_destroy(loop->idl); | |
4660 | } | |
4661 | } | |
4662 | ||
4663 | struct ovsdb_idl_txn * | |
4664 | ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) | |
4665 | { | |
4666 | ovsdb_idl_run(loop->idl); | |
4667 | loop->open_txn = (loop->committing_txn | |
4668 | || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno | |
4669 | ? NULL | |
4670 | : ovsdb_idl_txn_create(loop->idl)); | |
4671 | return loop->open_txn; | |
4672 | } | |
4673 | ||
8ba0e38a BP |
4674 | /* Attempts to commit the current transaction, if one is open, and sets up the |
4675 | * poll loop to wake up when some more work might be needed. | |
4676 | * | |
4677 | * If a transaction was open, in this or a previous iteration of the main loop, | |
4678 | * and had not before finished committing (successfully or unsuccessfully), the | |
4679 | * return value is one of: | |
4680 | * | |
4681 | * 1: The transaction committed successfully (or it did not change anything in | |
4682 | * the database). | |
4683 | * 0: The transaction failed. | |
4684 | * -1: The commit is still in progress. | |
4685 | * | |
4686 | * Thus, the return value is -1 if the transaction is in progress and otherwise | |
4687 | * true for success, false for failure. | |
4688 | * | |
4689 | * (In the corner case where the IDL sends a transaction to the database and | |
4690 | * the database commits it, and the connection between the IDL and the database | |
4691 | * drops before the IDL receives the message confirming the commit, this | |
4692 | * function can return 0 even though the transaction succeeded.) | |
4693 | */ | |
4694 | int | |
a548a764 AW |
4695 | ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) |
4696 | { | |
4697 | if (loop->open_txn) { | |
4698 | loop->committing_txn = loop->open_txn; | |
4699 | loop->open_txn = NULL; | |
4700 | ||
4701 | loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); | |
4702 | } | |
4703 | ||
4704 | struct ovsdb_idl_txn *txn = loop->committing_txn; | |
8ba0e38a | 4705 | int retval; |
a548a764 AW |
4706 | if (txn) { |
4707 | enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); | |
4708 | if (status != TXN_INCOMPLETE) { | |
4709 | switch (status) { | |
4710 | case TXN_TRY_AGAIN: | |
4711 | /* We want to re-evaluate the database when it's changed from | |
4712 | * the contents that it had when we started the commit. (That | |
4713 | * might have already happened.) */ | |
4714 | loop->skip_seqno = loop->precommit_seqno; | |
4715 | if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { | |
4716 | poll_immediate_wake(); | |
4717 | } | |
8ba0e38a | 4718 | retval = 0; |
a548a764 AW |
4719 | break; |
4720 | ||
4721 | case TXN_SUCCESS: | |
207d29e1 BP |
4722 | /* Possibly some work on the database was deferred because no |
4723 | * further transaction could proceed. Wake up again. */ | |
8ba0e38a | 4724 | retval = 1; |
fa183acc | 4725 | loop->cur_cfg = loop->next_cfg; |
207d29e1 | 4726 | poll_immediate_wake(); |
a548a764 AW |
4727 | break; |
4728 | ||
4729 | case TXN_UNCHANGED: | |
8ba0e38a | 4730 | retval = 1; |
fa183acc BP |
4731 | loop->cur_cfg = loop->next_cfg; |
4732 | break; | |
4733 | ||
a548a764 AW |
4734 | case TXN_ABORTED: |
4735 | case TXN_NOT_LOCKED: | |
4736 | case TXN_ERROR: | |
8ba0e38a | 4737 | retval = 0; |
a548a764 AW |
4738 | break; |
4739 | ||
4740 | case TXN_UNCOMMITTED: | |
4741 | case TXN_INCOMPLETE: | |
8ba0e38a | 4742 | default: |
a548a764 | 4743 | OVS_NOT_REACHED(); |
a548a764 AW |
4744 | } |
4745 | ovsdb_idl_txn_destroy(txn); | |
4746 | loop->committing_txn = NULL; | |
8ba0e38a BP |
4747 | } else { |
4748 | retval = -1; | |
a548a764 | 4749 | } |
8ba0e38a BP |
4750 | } else { |
4751 | /* Not a meaningful return value: no transaction was in progress. */ | |
4752 | retval = 1; | |
a548a764 AW |
4753 | } |
4754 | ||
4755 | ovsdb_idl_wait(loop->idl); | |
8ba0e38a BP |
4756 | |
4757 | return retval; | |
a548a764 | 4758 | } |