#include "bitmap.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "json.h"
#include "jsonrpc.h"
#include "ovsdb-data.h"
struct ovsdb_idl *idl;
struct hmap txn_rows;
enum ovsdb_idl_txn_status status;
+ char *error;
bool dry_run;
struct ds comment;
if (!ovsdb_idl_row_is_orphan(row)) {
ovsdb_idl_row_unparse(row);
- ovsdb_idl_row_clear_old(row);
}
- hmap_remove(&table->rows, &row->hmap_node);
LIST_FOR_EACH_SAFE (arc, next_arc, struct ovsdb_idl_arc, src_node,
&row->src_arcs) {
free(arc);
/* No need to do anything with dst_arcs: some node has those arcs
* as forward arcs and will destroy them itself. */
- free(row);
+ ovsdb_idl_row_destroy(row);
}
}
json_destroy(idl->monitor_request_id);
msg = jsonrpc_create_request(
- "monitor", json_array_create_2(json_null_create(), monitor_requests),
+ "monitor",
+ json_array_create_3(json_string_create(idl->class->database),
+ json_null_create(), monitor_requests),
&idl->monitor_request_id);
jsonrpc_session_send(idl->session, msg);
}
}
}
+/* When a row A refers to row B through a column with a "refTable" constraint,
+ * but row B does not exist, row B is called an "orphan row". Orphan rows
+ * should not persist, because the database enforces referential integrity, but
+ * they can appear transiently as changes from the database are received (the
+ * database doesn't try to topologically sort them and circular references mean
+ * it isn't always possible anyhow).
+ *
+ * This function returns true if 'row' is an orphan row, otherwise false.
+ */
static bool
ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row)
{
- return !row->old;
+ return !row->old && !row->new;
+}
+
+/* Returns true if 'row' is conceptually part of the database as modified by
+ * the current transaction (if any), false otherwise.
+ *
+ * This function will return true if 'row' is not an orphan (see the comment on
+ * ovsdb_idl_row_is_orphan()) and:
+ *
+ * - 'row' exists in the database and has not been deleted within the
+ * current transaction (if any).
+ *
+ * - 'row' was inserted within the current transaction and has not been
+ * deleted. (In the latter case you should not have passed 'row' in at
+ * all, because ovsdb_idl_txn_delete() freed it.)
+ *
+ * This function will return false if 'row' is an orphan or if 'row' was
+ * deleted within the current transaction.
+ */
+static bool
+ovsdb_idl_row_exists(const struct ovsdb_idl_row *row)
+{
+ return row->new != NULL;
}
static void
/* Force nodes that reference 'row' to reparse. */
static void
-ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row, bool destroy_dsts)
+ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row)
{
struct ovsdb_idl_arc *arc, *next;
struct ovsdb_idl_row *ref = arc->src;
ovsdb_idl_row_unparse(ref);
- ovsdb_idl_row_clear_arcs(ref, destroy_dsts);
+ ovsdb_idl_row_clear_arcs(ref, false);
ovsdb_idl_row_parse(ref);
}
}
ovsdb_idl_row_update(row, row_json);
ovsdb_idl_row_parse(row);
- ovsdb_idl_row_reparse_backrefs(row, false);
+ ovsdb_idl_row_reparse_backrefs(row);
}
static void
if (list_is_empty(&row->dst_arcs)) {
ovsdb_idl_row_destroy(row);
} else {
- ovsdb_idl_row_reparse_backrefs(row, true);
+ ovsdb_idl_row_reparse_backrefs(row);
}
}
struct ovsdb_idl_row *row;
row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node);
- if (row->new || !ovsdb_idl_row_is_orphan(row)) {
+ if (ovsdb_idl_row_exists(row)) {
return row;
}
}
assert(!idl->txn);
idl->txn = txn = xmalloc(sizeof *txn);
+ txn->request_id = NULL;
txn->idl = idl;
- txn->status = TXN_INCOMPLETE;
hmap_init(&txn->txn_rows);
+ txn->status = TXN_INCOMPLETE;
+ txn->error = NULL;
txn->dry_run = false;
ds_init(&txn->comment);
+
txn->inc_table = NULL;
txn->inc_column = NULL;
txn->inc_where = NULL;
+
hmap_init(&txn->inserted_rows);
+
return txn;
}
+/* Appends 's', which is treated as a printf()-type format string, to the
+ * comments that will be passed to the OVSDB server when 'txn' is committed.
+ * (The comment will be committed to the OVSDB log, which "ovsdb-tool
+ * show-log" can print in a relatively human-readable form.) */
void
-ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s)
+ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...)
{
+ va_list args;
+
if (txn->comment.length) {
ds_put_char(&txn->comment, '\n');
}
- ds_put_cstr(&txn->comment, s);
+
+ va_start(args, s);
+ ds_put_format_valist(&txn->comment, s, args);
+ va_end(args);
}
void
{
struct ovsdb_idl_txn_insert *insert, *next;
+ json_destroy(txn->request_id);
if (txn->status == TXN_INCOMPLETE) {
hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node);
}
ovsdb_idl_txn_abort(txn);
ds_destroy(&txn->comment);
+ free(txn->error);
free(txn->inc_table);
free(txn->inc_column);
json_destroy(txn->inc_where);
&txn->inserted_rows) {
free(insert);
}
+ hmap_destroy(&txn->inserted_rows);
free(txn);
}
ovsdb_idl_row_parse(row);
}
} else {
- hmap_remove(&row->table->rows, &row->hmap_node);
+ ovsdb_idl_row_unparse(row);
}
ovsdb_idl_row_clear_new(row);
hmap_remove(&txn->txn_rows, &row->txn_node);
hmap_node_nullify(&row->txn_node);
+ if (!row->old) {
+ hmap_remove(&row->table->rows, &row->hmap_node);
+ free(row);
+ }
}
hmap_destroy(&txn->txn_rows);
hmap_init(&txn->txn_rows);
return txn->status;
}
- operations = json_array_create_empty();
+ operations = json_array_create_1(
+ json_string_create(txn->idl->class->database));
/* Add prerequisites and declarations of new rows. */
HMAP_FOR_EACH (row, struct ovsdb_idl_row, txn_node, &txn->txn_rows) {
&column->type));
}
}
- if (row->new && !row->old) {
- struct json *op;
-
- op = json_object_create();
- json_array_add(operations, op);
- json_object_put_string(op, "op", "declare");
- json_object_put(op, "uuid-name",
- json_string_create_nocopy(
- uuid_name_from_uuid(&row->uuid)));
- }
}
/* Add updates. */
insert = xmalloc(sizeof *insert);
insert->dummy = row->uuid;
- insert->op_index = operations->u.array.n;
+ insert->op_index = operations->u.array.n - 1;
uuid_zero(&insert->real);
hmap_insert(&txn->inserted_rows, &insert->hmap_node,
uuid_hash(&insert->dummy));
if (txn->inc_table && any_updates) {
struct json *op;
- txn->inc_index = operations->u.array.n;
+ txn->inc_index = operations->u.array.n - 1;
op = json_object_create();
json_object_put_string(op, "op", "mutate");
hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node,
json_hash(txn->request_id, 0));
} else {
- txn->status = TXN_INCOMPLETE;
+ txn->status = TXN_TRY_AGAIN;
}
ovsdb_idl_txn_disassemble(txn);
return txn->status;
}
+/* Attempts to commit 'txn', blocking until the commit either succeeds or
+ * fails. Returns the final commit status, which may be any TXN_* value other
+ * than TXN_INCOMPLETE. */
+enum ovsdb_idl_txn_status
+ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn)
+{
+ enum ovsdb_idl_txn_status status;
+
+ fatal_signal_run();
+ while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) {
+ ovsdb_idl_run(txn->idl);
+ ovsdb_idl_wait(txn->idl);
+ ovsdb_idl_txn_wait(txn);
+ poll_block();
+ }
+ return status;
+}
+
int64_t
ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn)
{
}
}
+const char *
+ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn)
+{
+ if (txn->status != TXN_ERROR) {
+ return ovsdb_idl_txn_status_to_string(txn->status);
+ } else if (txn->error) {
+ return txn->error;
+ } else {
+ return "no error details available";
+ }
+}
+
+static void
+ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn,
+ const struct json *json)
+{
+ if (txn->error == NULL) {
+ txn->error = json_to_string(json, JSSF_SORT);
+ }
+}
+
/* For transaction 'txn' that completed successfully, finds and returns the
* permanent UUID that the database assigned to a newly inserted row, given the
* 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row.
size_t column_idx = column - class->columns;
assert(row->new != NULL);
+ assert(column_idx < class->n_columns);
if (hmap_node_is_null(&row->txn_node)) {
hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node,
uuid_hash(&row->uuid));
assert(row->new != NULL);
if (!row->old) {
+ ovsdb_idl_row_unparse(row);
ovsdb_idl_row_clear_new(row);
assert(!row->prereqs);
hmap_remove(&row->table->rows, &row->hmap_node);
ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert,
const struct json_array *results)
{
+ static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT;
struct ovsdb_error *error;
struct json *json_uuid;
union ovsdb_atom uuid;
return false;
}
- error = ovsdb_atom_from_json(&uuid, OVSDB_TYPE_UUID, json_uuid, NULL);
+ error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL);
if (error) {
char *s = ovsdb_error_to_string(error);
VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON "
soft_errors++;
} else if (strcmp(error->u.string, "aborted")) {
hard_errors++;
+ ovsdb_idl_txn_set_error_json(txn, op);
}
} else {
hard_errors++;
+ ovsdb_idl_txn_set_error_json(txn, op);
VLOG_WARN_RL(&syntax_rl,
"\"error\" in reply is not JSON string");
}
}
} else {
hard_errors++;
+ ovsdb_idl_txn_set_error_json(txn, op);
VLOG_WARN_RL(&syntax_rl,
"operation reply is not JSON null or object");
}
assert(txn != NULL);
return txn;
}
+
+struct ovsdb_idl *
+ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn)
+{
+ return txn->idl;
+}
+