+ transact(false, argc, argv);
+}
+
+static void
+print_db_changes(struct shash *tables, struct shash *names,
+ const struct ovsdb_schema *schema)
+{
+ struct shash_node *n1;
+
+ SHASH_FOR_EACH (n1, tables) {
+ const char *table = n1->name;
+ struct ovsdb_table_schema *table_schema;
+ struct json *rows = n1->data;
+ struct shash_node *n2;
+
+ if (n1->name[0] == '_' || rows->type != JSON_OBJECT) {
+ continue;
+ }
+
+ table_schema = shash_find_data(&schema->tables, table);
+ SHASH_FOR_EACH (n2, json_object(rows)) {
+ const char *row_uuid = n2->name;
+ struct json *columns = n2->data;
+ struct shash_node *n3;
+ char *old_name, *new_name;
+ bool free_new_name = false;
+
+ old_name = new_name = shash_find_data(names, row_uuid);
+ if (columns->type == JSON_OBJECT) {
+ struct json *new_name_json;
+
+ new_name_json = shash_find_data(json_object(columns), "name");
+ if (new_name_json) {
+ new_name = json_to_string(new_name_json, JSSF_SORT);
+ free_new_name = true;
+ }
+ }
+
+ printf("\ttable %s", table);
+
+ if (!old_name) {
+ if (new_name) {
+ printf(" insert row %s (%.8s):\n", new_name, row_uuid);
+ } else {
+ printf(" insert row %.8s:\n", row_uuid);
+ }
+ } else {
+ printf(" row %s (%.8s):\n", old_name, row_uuid);
+ }
+
+ if (columns->type == JSON_OBJECT) {
+ if (show_log_verbosity > 1) {
+ SHASH_FOR_EACH (n3, json_object(columns)) {
+ const char *column = n3->name;
+ const struct ovsdb_column *column_schema;
+ struct json *value = n3->data;
+ char *value_string = NULL;
+
+ column_schema =
+ (table_schema
+ ? shash_find_data(&table_schema->columns, column)
+ : NULL);
+ if (column_schema) {
+ const struct ovsdb_error *error;
+ const struct ovsdb_type *type;
+ struct ovsdb_datum datum;
+
+ type = &column_schema->type;
+ error = ovsdb_datum_from_json(&datum, type,
+ value, NULL);
+ if (!error) {
+ struct ds s;
+
+ ds_init(&s);
+ ovsdb_datum_to_string(&datum, type, &s);
+ value_string = ds_steal_cstr(&s);
+ }
+ }
+ if (!value_string) {
+ value_string = json_to_string(value, JSSF_SORT);
+ }
+ printf("\t\t%s=%s\n", column, value_string);
+ free(value_string);
+ }
+ }
+ if (!old_name
+ || (new_name != old_name && strcmp(old_name, new_name))) {
+ if (old_name) {
+ shash_delete(names, shash_find(names, row_uuid));
+ free(old_name);
+ }
+ shash_add(names, row_uuid, (new_name
+ ? xstrdup(new_name)
+ : xmemdup0(row_uuid, 8)));
+ }
+ } else if (columns->type == JSON_NULL) {
+ struct shash_node *node;
+
+ printf("\t\tdelete row\n");
+ node = shash_find(names, row_uuid);
+ if (node) {
+ shash_delete(names, node);
+ }
+ free(old_name);
+ }
+
+ if (free_new_name) {
+ free(new_name);
+ }
+ }
+ }
+}
+
+static void
+do_show_log(int argc, char *argv[])
+{
+ const char *db_file_name = argc >= 2 ? argv[1] : default_db();
+ struct shash names;
+ struct ovsdb_log *log;
+ struct ovsdb_schema *schema;
+ unsigned int i;
+
+ check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_READ_ONLY,
+ -1, &log));
+ shash_init(&names);
+ schema = NULL;
+ for (i = 0; ; i++) {
+ struct json *json;
+
+ check_ovsdb_error(ovsdb_log_read(log, &json));
+ if (!json) {
+ break;
+ }
+
+ printf("record %u:", i);
+ if (i == 0) {
+ check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
+ printf(" \"%s\" schema, version=\"%s\", cksum=\"%s\"\n",
+ schema->name, schema->version, schema->cksum);
+ } else if (json->type == JSON_OBJECT) {
+ struct json *date, *comment;
+
+ date = shash_find_data(json_object(json), "_date");
+ if (date && date->type == JSON_INTEGER) {
+ time_t t = json_integer(date);
+ char s[128];
+
+ strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t));
+ printf(" %s", s);
+ }
+
+ comment = shash_find_data(json_object(json), "_comment");
+ if (comment && comment->type == JSON_STRING) {
+ printf(" \"%s\"", json_string(comment));
+ }
+
+ if (i > 0 && show_log_verbosity > 0) {
+ putchar('\n');
+ print_db_changes(json_object(json), &names, schema);
+ }
+ }
+ json_destroy(json);
+ putchar('\n');
+ }
+
+ ovsdb_log_close(log);
+ ovsdb_schema_destroy(schema);
+ /* XXX free 'names'. */