ovsdb: Rename ovsdb_file to ovsdb_log.
[sliver-openvswitch.git] / tests / test-ovsdb.c
1 /*
2  * Copyright (c) 2009 Nicira Networks.
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 <assert.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <inttypes.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "command-line.h"
27 #include "json.h"
28 #include "ovsdb-data.h"
29 #include "ovsdb-error.h"
30 #include "ovsdb-types.h"
31 #include "ovsdb/column.h"
32 #include "ovsdb/condition.h"
33 #include "ovsdb/log.h"
34 #include "ovsdb/ovsdb.h"
35 #include "ovsdb/query.h"
36 #include "ovsdb/row.h"
37 #include "ovsdb/table.h"
38 #include "ovsdb/transaction.h"
39 #include "ovsdb/trigger.h"
40 #include "poll-loop.h"
41 #include "svec.h"
42 #include "timeval.h"
43 #include "util.h"
44 #include "vlog.h"
45
46 static struct command all_commands[];
47
48 static void usage(void) NO_RETURN;
49 static void parse_options(int argc, char *argv[]);
50
51 int
52 main(int argc, char *argv[])
53 {
54     set_program_name(argv[0]);
55     time_init();
56     vlog_init();
57     parse_options(argc, argv);
58     run_command(argc - optind, argv + optind, all_commands);
59     return 0;
60 }
61
62 static void
63 parse_options(int argc, char *argv[])
64 {
65     static struct option long_options[] = {
66         {"verbose", optional_argument, 0, 'v'},
67         {"help", no_argument, 0, 'h'},
68         {0, 0, 0, 0},
69     };
70     char *short_options = long_options_to_short_options(long_options);
71
72     for (;;) {
73         int c = getopt_long(argc, argv, short_options, long_options, NULL);
74         if (c == -1) {
75             break;
76         }
77
78         switch (c) {
79         case 'h':
80             usage();
81
82         case 'v':
83             vlog_set_verbosity(optarg);
84             break;
85
86         case '?':
87             exit(EXIT_FAILURE);
88
89         default:
90             abort();
91         }
92     }
93     free(short_options);
94 }
95
96 static void
97 usage(void)
98 {
99     printf("%s: Open vSwitch database test utility\n"
100            "usage: %s [OPTIONS] COMMAND [ARG...]\n\n"
101            "  log-io FILE FLAGS COMMAND...\n"
102            "    open FILE with FLAGS, run COMMANDs\n"
103            "  parse-atomic-type TYPE\n"
104            "    parse TYPE as OVSDB atomic type, and re-serialize\n"
105            "  parse-type JSON\n"
106            "    parse JSON as OVSDB type, and re-serialize\n"
107            "  parse-atoms TYPE ATOM...\n"
108            "    parse ATOMs as atoms of given TYPE, and re-serialize\n"
109            "  sort-atoms TYPE ATOM...\n"
110            "    print ATOMs in sorted order, and re-serialize\n"
111            "  parse-data TYPE DATUM...\n"
112            "    parse DATUMs as data of given TYPE, and re-serialize\n"
113            "  parse-column NAME OBJECT\n"
114            "    parse column NAME with info OBJECT, and re-serialize\n"
115            "  parse-table NAME OBJECT\n"
116            "    parse table NAME with info OBJECT\n"
117            "  parse-row TABLE ROW..., and re-serialize\n"
118            "    parse each ROW of defined TABLE\n"
119            "  compare-row TABLE ROW...\n"
120            "    mutually compare all of the ROWs, print those that are equal\n"
121            "  parse-conditions TABLE CONDITION...\n"
122            "    parse each CONDITION on TABLE, and re-serialize\n"
123            "  evaluate-conditions TABLE [CONDITION,...] [ROW,...]\n"
124            "    test CONDITIONS on TABLE against each ROW, print results\n"
125            "  query TABLE [ROW,...] [CONDITION,...]\n"
126            "    add each ROW to TABLE, then query and print the rows that\n"
127            "    satisfy each CONDITION.\n"
128            "  query-distinct TABLE [ROW,...] [CONDITION,...] COLUMNS\n"
129            "    add each ROW to TABLE, then query and print the rows that\n"
130            "    satisfy each CONDITION and have distinct COLUMNS.\n"
131            "  transact COMMAND\n"
132            "    execute each specified transactional COMMAND:\n"
133            "      commit\n"
134            "      abort\n"
135            "      insert UUID I J\n"
136            "      delete UUID\n"
137            "      modify UUID I J\n"
138            "      print\n"
139            "  execute SCHEMA TRANSACTION...\n"
140            "    executes each TRANSACTION on an initially empty database\n"
141            "    the specified SCHEMA\n"
142            "  trigger SCHEMA TRANSACTION...\n"
143            "    executes each TRANSACTION on an initially empty database\n"
144            "    the specified SCHEMA.   A TRANSACTION of the form\n"
145            "    [\"advance\", NUMBER] advances NUMBER milliseconds in\n"
146            "    simulated time, for causing triggers to time out.\n",
147            program_name, program_name);
148     vlog_usage();
149     printf("\nOther options:\n"
150            "  -h, --help                  display this help message\n");
151     exit(EXIT_SUCCESS);
152 }
153 \f
154 /* Command helper functions. */
155
156 static struct json *
157 parse_json(const char *s)
158 {
159     struct json *json = json_from_string(s);
160     if (json->type == JSON_STRING) {
161         ovs_fatal(0, "\"%s\": %s", s, json->u.string);
162     }
163     return json;
164 }
165
166 static struct json *
167 unbox_json(struct json *json)
168 {
169     if (json->type == JSON_ARRAY && json->u.array.n == 1) {
170         struct json *inner = json->u.array.elems[0];
171         json->u.array.elems[0] = NULL;
172         json_destroy(json);
173         return inner;
174     } else {
175         return json;
176     }
177 }
178
179 static void
180 print_and_free_json(struct json *json)
181 {
182     char *string = json_to_string(json, JSSF_SORT);
183     json_destroy(json);
184     puts(string);
185     free(string);
186 }
187
188 static void
189 check_ovsdb_error(struct ovsdb_error *error)
190 {
191     if (error) {
192         ovs_fatal(0, "%s", ovsdb_error_to_string(error));
193     }
194 }
195 \f
196 /* Command implementations. */
197
198 static void
199 do_log_io(int argc, char *argv[])
200 {
201     const char *name = argv[1];
202     char *mode = argv[2];
203
204     struct ovsdb_error *error;
205     struct ovsdb_log *log;
206     char *save_ptr = NULL;
207     const char *token;
208     int flags;
209     int i;
210
211     for (flags = 0, token = strtok_r(mode, " |", &save_ptr); token != NULL;
212          token = strtok_r(NULL, " |", &save_ptr))
213     {
214         if (!strcmp(token, "O_RDONLY")) {
215             flags |= O_RDONLY;
216         } else if (!strcmp(token, "O_RDWR")) {
217             flags |= O_RDWR;
218         } else if (!strcmp(token, "O_TRUNC")) {
219             flags |= O_TRUNC;
220         } else if (!strcmp(token, "O_CREAT")) {
221             flags |= O_CREAT;
222         } else if (!strcmp(token, "O_EXCL")) {
223             flags |= O_EXCL;
224         } else if (!strcmp(token, "O_TRUNC")) {
225             flags |= O_TRUNC;
226         }
227     }
228
229     check_ovsdb_error(ovsdb_log_open(name, flags, &log));
230     printf("%s: open successful\n", name);
231
232     for (i = 3; i < argc; i++) {
233         const char *command = argv[i];
234         if (!strcmp(command, "read")) {
235             struct json *json;
236
237             error = ovsdb_log_read(log, &json);
238             if (!error) {
239                 printf("%s: read: ", name);
240                 if (json) {
241                     print_and_free_json(json);
242                 } else {
243                     printf("end of log\n");
244                 }
245                 continue;
246             }
247         } else if (!strncmp(command, "write:", 6)) {
248             struct json *json = parse_json(command + 6);
249             error = ovsdb_log_write(log, json);
250             json_destroy(json);
251         } else if (!strcmp(command, "commit")) {
252             error = ovsdb_log_commit(log);
253         } else {
254             ovs_fatal(0, "unknown log-io command \"%s\"", command);
255         }
256         if (error) {
257             char *s = ovsdb_error_to_string(error);
258             printf("%s: %s failed: %s\n", name, command, s);
259             free(s);
260         } else {
261             printf("%s: %s successful\n", name, command);
262         }
263     }
264
265     ovsdb_log_close(log);
266 }
267
268 static void
269 do_parse_atomic_type(int argc UNUSED, char *argv[])
270 {
271     enum ovsdb_atomic_type type;
272     struct json *json;
273
274     json = unbox_json(parse_json(argv[1]));
275     check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
276     json_destroy(json);
277     print_and_free_json(ovsdb_atomic_type_to_json(type));
278 }
279
280 static void
281 do_parse_type(int argc UNUSED, char *argv[])
282 {
283     struct ovsdb_type type;
284     struct json *json;
285
286     json = unbox_json(parse_json(argv[1]));
287     check_ovsdb_error(ovsdb_type_from_json(&type, json));
288     json_destroy(json);
289     print_and_free_json(ovsdb_type_to_json(&type));
290 }
291
292 static void
293 do_parse_atoms(int argc, char *argv[])
294 {
295     enum ovsdb_atomic_type type;
296     struct json *json;
297     int i;
298
299     json = unbox_json(parse_json(argv[1]));
300     check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
301     json_destroy(json);
302
303     for (i = 2; i < argc; i++) {
304         union ovsdb_atom atom;
305
306         json = unbox_json(parse_json(argv[i]));
307         check_ovsdb_error(ovsdb_atom_from_json(&atom, type, json, NULL));
308         json_destroy(json);
309
310         print_and_free_json(ovsdb_atom_to_json(&atom, type));
311
312         ovsdb_atom_destroy(&atom, type);
313     }
314 }
315
316 static void
317 do_parse_data(int argc, char *argv[])
318 {
319     struct ovsdb_type type;
320     struct json *json;
321     int i;
322
323     json = unbox_json(parse_json(argv[1]));
324     check_ovsdb_error(ovsdb_type_from_json(&type, json));
325     json_destroy(json);
326
327     for (i = 2; i < argc; i++) {
328         struct ovsdb_datum datum;
329
330         json = unbox_json(parse_json(argv[i]));
331         check_ovsdb_error(ovsdb_datum_from_json(&datum, &type, json, NULL));
332         json_destroy(json);
333
334         print_and_free_json(ovsdb_datum_to_json(&datum, &type));
335
336         ovsdb_datum_destroy(&datum, &type);
337     }
338 }
339
340 static enum ovsdb_atomic_type compare_atoms_atomic_type;
341
342 static int
343 compare_atoms(const void *a_, const void *b_)
344 {
345     const union ovsdb_atom *a = a_;
346     const union ovsdb_atom *b = b_;
347
348     return ovsdb_atom_compare_3way(a, b, compare_atoms_atomic_type);
349 }
350
351 static void
352 do_sort_atoms(int argc UNUSED, char *argv[])
353 {
354     enum ovsdb_atomic_type type;
355     union ovsdb_atom *atoms;
356     struct json *json, **json_atoms;
357     size_t n_atoms;
358     int i;
359
360     json = unbox_json(parse_json(argv[1]));
361     check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json));
362     json_destroy(json);
363
364     json = unbox_json(parse_json(argv[2]));
365     if (json->type != JSON_ARRAY) {
366         ovs_fatal(0, "second argument must be array");
367     }
368
369     /* Convert JSON atoms to internal representation. */
370     n_atoms = json->u.array.n;
371     atoms = xmalloc(n_atoms * sizeof *atoms);
372     for (i = 0; i < n_atoms; i++) {
373         check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], type,
374                                                json->u.array.elems[i], NULL));
375     }
376     json_destroy(json);
377
378     /* Sort atoms. */
379     compare_atoms_atomic_type = type;
380     qsort(atoms, n_atoms, sizeof *atoms, compare_atoms);
381
382     /* Convert internal representation back to JSON. */
383     json_atoms = xmalloc(n_atoms * sizeof *json_atoms);
384     for (i = 0; i < n_atoms; i++) {
385         json_atoms[i] = ovsdb_atom_to_json(&atoms[i], type);
386         ovsdb_atom_destroy(&atoms[i], type);
387     }
388     print_and_free_json(json_array_create(json_atoms, n_atoms));
389 }
390
391 static void
392 do_parse_column(int argc UNUSED, char *argv[])
393 {
394     struct ovsdb_column *column;
395     struct json *json;
396
397     json = parse_json(argv[2]);
398     check_ovsdb_error(ovsdb_column_from_json(json, argv[1], &column));
399     json_destroy(json);
400     print_and_free_json(ovsdb_column_to_json(column));
401     ovsdb_column_destroy(column);
402 }
403
404 static void
405 do_parse_table(int argc UNUSED, char *argv[])
406 {
407     struct ovsdb_table_schema *ts;
408     struct json *json;
409
410     json = parse_json(argv[2]);
411     check_ovsdb_error(ovsdb_table_schema_from_json(json, argv[1], &ts));
412     json_destroy(json);
413     print_and_free_json(ovsdb_table_schema_to_json(ts));
414     ovsdb_table_schema_destroy(ts);
415 }
416
417 static void
418 do_parse_rows(int argc, char *argv[])
419 {
420     struct ovsdb_column_set all_columns;
421     struct ovsdb_table_schema *ts;
422     struct ovsdb_table *table;
423     struct json *json;
424     int i;
425
426     json = unbox_json(parse_json(argv[1]));
427     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
428     json_destroy(json);
429
430     table = ovsdb_table_create(ts);
431     ovsdb_column_set_init(&all_columns);
432     ovsdb_column_set_add_all(&all_columns, table);
433
434     for (i = 2; i < argc; i++) {
435         struct ovsdb_column_set columns;
436         struct ovsdb_row *row;
437
438         ovsdb_column_set_init(&columns);
439         row = ovsdb_row_create(table);
440
441         json = unbox_json(parse_json(argv[i]));
442         check_ovsdb_error(ovsdb_row_from_json(row, json, NULL, &columns));
443         json_destroy(json);
444
445         print_and_free_json(ovsdb_row_to_json(row, &all_columns));
446
447         if (columns.n_columns) {
448             struct svec names;
449             size_t j;
450             char *s;
451
452             svec_init(&names);
453             for (j = 0; j < columns.n_columns; j++) {
454                 svec_add(&names, columns.columns[j]->name);
455             }
456             svec_sort(&names);
457             s = svec_join(&names, ", ", "");
458             puts(s);
459             free(s);
460             svec_destroy(&names);
461         } else {
462             printf("<none>\n");
463         }
464
465         ovsdb_column_set_destroy(&columns);
466         ovsdb_row_destroy(row);
467     }
468
469     ovsdb_column_set_destroy(&all_columns);
470     ovsdb_table_destroy(table); /* Also destroys 'ts'. */
471 }
472
473 static void
474 do_compare_rows(int argc, char *argv[])
475 {
476     struct ovsdb_column_set all_columns;
477     struct ovsdb_table_schema *ts;
478     struct ovsdb_table *table;
479     struct ovsdb_row **rows;
480     struct json *json;
481     char **names;
482     int n_rows;
483     int i, j;
484
485     json = unbox_json(parse_json(argv[1]));
486     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
487     json_destroy(json);
488
489     table = ovsdb_table_create(ts);
490     ovsdb_column_set_init(&all_columns);
491     ovsdb_column_set_add_all(&all_columns, table);
492
493     n_rows = argc - 2;
494     rows = xmalloc(sizeof *rows * n_rows);
495     names = xmalloc(sizeof *names * n_rows);
496     for (i = 0; i < n_rows; i++) {
497         rows[i] = ovsdb_row_create(table);
498
499         json = parse_json(argv[i + 2]);
500         if (json->type != JSON_ARRAY || json->u.array.n != 2
501             || json->u.array.elems[0]->type != JSON_STRING) {
502             ovs_fatal(0, "\"%s\" does not have expected form "
503                       "[\"name\", {data}]", argv[i]);
504         }
505         names[i] = xstrdup(json->u.array.elems[0]->u.string);
506         check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[1],
507                                               NULL, NULL));
508         json_destroy(json);
509     }
510     for (i = 0; i < n_rows; i++) {
511         uint32_t i_hash = ovsdb_row_hash_columns(rows[i], &all_columns, 0);
512         for (j = i + 1; j < n_rows; j++) {
513             uint32_t j_hash = ovsdb_row_hash_columns(rows[j], &all_columns, 0);
514             if (ovsdb_row_equal_columns(rows[i], rows[j], &all_columns)) {
515                 printf("%s == %s\n", names[i], names[j]);
516                 if (i_hash != j_hash) {
517                     printf("but hash(%s) != hash(%s)\n", names[i], names[j]);
518                     abort();
519                 }
520             } else if (i_hash == j_hash) {
521                 printf("hash(%s) == hash(%s)\n", names[i], names[j]);
522             }
523         }
524     }
525     free(rows);
526     free(names);
527
528     ovsdb_column_set_destroy(&all_columns);
529     ovsdb_table_destroy(table); /* Also destroys 'ts'. */
530 }
531
532 static void
533 do_parse_conditions(int argc, char *argv[])
534 {
535     struct ovsdb_table_schema *ts;
536     struct json *json;
537     int exit_code = 0;
538     int i;
539
540     json = unbox_json(parse_json(argv[1]));
541     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
542     json_destroy(json);
543
544     for (i = 2; i < argc; i++) {
545         struct ovsdb_condition cnd;
546         struct ovsdb_error *error;
547
548         json = parse_json(argv[i]);
549         error = ovsdb_condition_from_json(ts, json, NULL, &cnd);
550         if (!error) {
551             print_and_free_json(ovsdb_condition_to_json(&cnd));
552         } else {
553             char *s = ovsdb_error_to_string(error);
554             ovs_error(0, "%s", s);
555             free(s);
556             ovsdb_error_destroy(error);
557             exit_code = 1;
558         }
559         json_destroy(json);
560
561         ovsdb_condition_destroy(&cnd);
562     }
563     ovsdb_table_schema_destroy(ts);
564
565     exit(exit_code);
566 }
567
568 static void
569 do_evaluate_conditions(int argc UNUSED, char *argv[])
570 {
571     struct ovsdb_table_schema *ts;
572     struct ovsdb_table *table;
573     struct ovsdb_condition *conditions;
574     size_t n_conditions;
575     struct ovsdb_row **rows;
576     size_t n_rows;
577     struct json *json;
578     size_t i, j;
579
580     /* Parse table schema, create table. */
581     json = unbox_json(parse_json(argv[1]));
582     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
583     json_destroy(json);
584
585     table = ovsdb_table_create(ts);
586
587     /* Parse conditions. */
588     json = parse_json(argv[2]);
589     if (json->type != JSON_ARRAY) {
590         ovs_fatal(0, "CONDITION argument is not JSON array");
591     }
592     n_conditions = json->u.array.n;
593     conditions = xmalloc(n_conditions * sizeof *conditions);
594     for (i = 0; i < n_conditions; i++) {
595         check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
596                                                     NULL, &conditions[i]));
597     }
598     json_destroy(json);
599
600     /* Parse rows. */
601     json = parse_json(argv[3]);
602     if (json->type != JSON_ARRAY) {
603         ovs_fatal(0, "ROW argument is not JSON array");
604     }
605     n_rows = json->u.array.n;
606     rows = xmalloc(n_rows * sizeof *rows);
607     for (i = 0; i < n_rows; i++) {
608         rows[i] = ovsdb_row_create(table);
609         check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i],
610                                               NULL, NULL));
611     }
612     json_destroy(json);
613
614     for (i = 0; i < n_conditions; i++) {
615         printf("condition %2d:", i);
616         for (j = 0; j < n_rows; j++) {
617             bool result = ovsdb_condition_evaluate(rows[j], &conditions[i]);
618             if (j % 5 == 0) {
619                 putchar(' ');
620             }
621             putchar(result ? 'T' : '-');
622         }
623         printf("\n");
624     }
625
626     for (i = 0; i < n_conditions; i++) {
627         ovsdb_condition_destroy(&conditions[i]);
628     }
629     for (i = 0; i < n_rows; i++) {
630         ovsdb_row_destroy(rows[i]);
631     }
632     ovsdb_table_destroy(table); /* Also destroys 'ts'. */
633 }
634
635 struct do_query_cbdata {
636     struct uuid *row_uuids;
637     int *counts;
638     size_t n_rows;
639 };
640
641 static bool
642 do_query_cb(const struct ovsdb_row *row, void *cbdata_)
643 {
644     struct do_query_cbdata *cbdata = cbdata_;
645     size_t i;
646
647     for (i = 0; i < cbdata->n_rows; i++) {
648         if (uuid_equals(ovsdb_row_get_uuid(row), &cbdata->row_uuids[i])) {
649             cbdata->counts[i]++;
650         }
651     }
652
653     return true;
654 }
655
656 static void
657 do_query(int argc UNUSED, char *argv[])
658 {
659     struct do_query_cbdata cbdata;
660     struct ovsdb_table_schema *ts;
661     struct ovsdb_table *table;
662     struct json *json;
663     int exit_code = 0;
664     size_t i;
665
666     /* Parse table schema, create table. */
667     json = unbox_json(parse_json(argv[1]));
668     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
669     json_destroy(json);
670
671     table = ovsdb_table_create(ts);
672
673     /* Parse rows, add to table. */
674     json = parse_json(argv[2]);
675     if (json->type != JSON_ARRAY) {
676         ovs_fatal(0, "ROW argument is not JSON array");
677     }
678     cbdata.n_rows = json->u.array.n;
679     cbdata.row_uuids = xmalloc(cbdata.n_rows * sizeof *cbdata.row_uuids);
680     cbdata.counts = xmalloc(cbdata.n_rows * sizeof *cbdata.counts);
681     for (i = 0; i < cbdata.n_rows; i++) {
682         struct ovsdb_row *row = ovsdb_row_create(table);
683         uuid_generate(ovsdb_row_get_uuid_rw(row));
684         check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i],
685                                               NULL, NULL));
686         if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) {
687             ovs_fatal(0, "duplicate UUID "UUID_FMT" in table",
688                       UUID_ARGS(ovsdb_row_get_uuid(row)));
689         }
690         cbdata.row_uuids[i] = *ovsdb_row_get_uuid(row);
691         ovsdb_table_put_row(table, row);
692     }
693     json_destroy(json);
694
695     /* Parse conditions and execute queries. */
696     json = parse_json(argv[3]);
697     if (json->type != JSON_ARRAY) {
698         ovs_fatal(0, "CONDITION argument is not JSON array");
699     }
700     for (i = 0; i < json->u.array.n; i++) {
701         struct ovsdb_condition cnd;
702         size_t j;
703
704         check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
705                                                     NULL, &cnd));
706
707         memset(cbdata.counts, 0, cbdata.n_rows * sizeof *cbdata.counts);
708         ovsdb_query(table, &cnd, do_query_cb, &cbdata);
709
710         printf("query %2d:", i);
711         for (j = 0; j < cbdata.n_rows; j++) {
712             if (j % 5 == 0) {
713                 putchar(' ');
714             }
715             if (cbdata.counts[j]) {
716                 printf("%d", cbdata.counts[j]);
717                 if (cbdata.counts[j] > 1) {
718                     /* Dup! */
719                     exit_code = 1;
720                 }
721             } else {
722                 putchar('-');
723             }
724         }
725         putchar('\n');
726
727         ovsdb_condition_destroy(&cnd);
728     }
729     json_destroy(json);
730
731     ovsdb_table_destroy(table); /* Also destroys 'ts'. */
732
733     exit(exit_code);
734 }
735
736 struct do_query_distinct_class {
737     struct ovsdb_row *example;
738     int count;
739 };
740
741 struct do_query_distinct_row {
742     struct uuid uuid;
743     struct do_query_distinct_class *class;
744 };
745
746 static void
747 do_query_distinct(int argc UNUSED, char *argv[])
748 {
749     struct ovsdb_column_set columns;
750     struct ovsdb_table_schema *ts;
751     struct ovsdb_table *table;
752     struct do_query_distinct_row *rows;
753     size_t n_rows;
754     struct do_query_distinct_class *classes;
755     size_t n_classes;
756     struct json *json;
757     int exit_code = 0;
758     size_t i, j, k;
759
760     /* Parse table schema, create table. */
761     json = unbox_json(parse_json(argv[1]));
762     check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts));
763     json_destroy(json);
764
765     table = ovsdb_table_create(ts);
766
767     /* Parse column set. */
768     json = parse_json(argv[4]);
769     ovsdb_column_set_from_json(json, table, &columns);
770     json_destroy(json);
771
772     /* Parse rows, add to table. */
773     json = parse_json(argv[2]);
774     if (json->type != JSON_ARRAY) {
775         ovs_fatal(0, "ROW argument is not JSON array");
776     }
777     n_rows = json->u.array.n;
778     rows = xmalloc(n_rows * sizeof *rows);
779     classes = xmalloc(n_rows * sizeof *classes);
780     n_classes = 0;
781     for (i = 0; i < n_rows; i++) {
782         struct ovsdb_row *row;
783         size_t j;
784
785         /* Parse row. */
786         row = ovsdb_row_create(table);
787         uuid_generate(ovsdb_row_get_uuid_rw(row));
788         check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i],
789                                               NULL, NULL));
790
791         /* Initialize row and find equivalence class. */
792         rows[i].uuid = *ovsdb_row_get_uuid(row);
793         rows[i].class = NULL;
794         for (j = 0; j < n_classes; j++) {
795             if (ovsdb_row_equal_columns(row, classes[j].example, &columns)) {
796                 rows[i].class = &classes[j];
797                 break;
798             }
799         }
800         if (!rows[i].class) {
801             rows[i].class = &classes[n_classes];
802             classes[n_classes].example = ovsdb_row_clone(row);
803             n_classes++;
804         }
805
806         /* Add row to table. */
807         if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) {
808             ovs_fatal(0, "duplicate UUID "UUID_FMT" in table",
809                       UUID_ARGS(ovsdb_row_get_uuid(row)));
810         }
811         ovsdb_table_put_row(table, row);
812
813     }
814     json_destroy(json);
815
816     /* Parse conditions and execute queries. */
817     json = parse_json(argv[3]);
818     if (json->type != JSON_ARRAY) {
819         ovs_fatal(0, "CONDITION argument is not JSON array");
820     }
821     for (i = 0; i < json->u.array.n; i++) {
822         struct ovsdb_row_set results;
823         struct ovsdb_condition cnd;
824
825         check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i],
826                                                     NULL, &cnd));
827
828         for (j = 0; j < n_classes; j++) {
829             classes[j].count = 0;
830         }
831         ovsdb_row_set_init(&results);
832         ovsdb_query_distinct(table, &cnd, &columns, &results);
833         for (j = 0; j < results.n_rows; j++) {
834             for (k = 0; k < n_rows; k++) {
835                 if (uuid_equals(ovsdb_row_get_uuid(results.rows[j]),
836                                 &rows[k].uuid)) {
837                     rows[k].class->count++;
838                 }
839             }
840         }
841         ovsdb_row_set_destroy(&results);
842
843         printf("query %2d:", i);
844         for (j = 0; j < n_rows; j++) {
845             int count = rows[j].class->count;
846
847             if (j % 5 == 0) {
848                 putchar(' ');
849             }
850             if (count > 1) {
851                 /* Dup! */
852                 printf("%d", count);
853                 exit_code = 1;
854             } else if (count == 1) {
855                 putchar("abcdefghijklmnopqrstuvwxyz"[rows[j].class - classes]);
856             } else {
857                 putchar('-');
858             }
859         }
860         putchar('\n');
861
862         ovsdb_condition_destroy(&cnd);
863     }
864     json_destroy(json);
865
866     ovsdb_table_destroy(table); /* Also destroys 'ts'. */
867
868     exit(exit_code);
869 }
870
871 static void
872 do_execute(int argc UNUSED, char *argv[])
873 {
874     struct ovsdb_schema *schema;
875     struct json *json;
876     struct ovsdb *db;
877     int i;
878
879     /* Create database. */
880     json = parse_json(argv[1]);
881     check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
882     json_destroy(json);
883     db = ovsdb_create(NULL, schema);
884
885     for (i = 2; i < argc; i++) {
886         struct json *params, *result;
887         char *s;
888
889         params = parse_json(argv[i]);
890         result = ovsdb_execute(db, params, 0, NULL);
891         s = json_to_string(result, JSSF_SORT);
892         printf("%s\n", s);
893         json_destroy(params);
894         json_destroy(result);
895     }
896
897     ovsdb_destroy(db);
898 }
899
900 struct test_trigger {
901     struct ovsdb_trigger trigger;
902     int number;
903 };
904
905 static void
906 do_trigger_dump(struct test_trigger *t, long long int now, const char *title)
907 {
908     struct json *result;
909     char *s;
910
911     result = ovsdb_trigger_steal_result(&t->trigger);
912     s = json_to_string(result, JSSF_SORT);
913     printf("t=%lld: trigger %d (%s): %s\n", now, t->number, title, s);
914     json_destroy(result);
915     ovsdb_trigger_destroy(&t->trigger);
916     free(t);
917 }
918
919 static void
920 do_trigger(int argc UNUSED, char *argv[])
921 {
922     struct ovsdb_schema *schema;
923     struct list completions;
924     struct json *json;
925     struct ovsdb *db;
926     long long int now;
927     int number;
928     int i;
929
930     /* Create database. */
931     json = parse_json(argv[1]);
932     check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
933     json_destroy(json);
934     db = ovsdb_create(NULL, schema);
935
936     list_init(&completions);
937     now = 0;
938     number = 0;
939     for (i = 2; i < argc; i++) {
940         struct json *params = parse_json(argv[i]);
941         if (params->type == JSON_ARRAY
942             && json_array(params)->n == 2
943             && json_array(params)->elems[0]->type == JSON_STRING
944             && !strcmp(json_string(json_array(params)->elems[0]), "advance")
945             && json_array(params)->elems[1]->type == JSON_INTEGER) {
946             now += json_integer(json_array(params)->elems[1]);
947             json_destroy(params);
948         } else {
949             struct test_trigger *t = xmalloc(sizeof *t);
950             ovsdb_trigger_init(db, &t->trigger, params, &completions, now);
951             t->number = number++;
952             if (ovsdb_trigger_is_complete(&t->trigger)) {
953                 do_trigger_dump(t, now, "immediate");
954             } else {
955                 printf("t=%lld: new trigger %d\n", now, t->number);
956             }
957         }
958
959         ovsdb_trigger_run(db, now);
960         while (!list_is_empty(&completions)) {
961             do_trigger_dump(CONTAINER_OF(list_pop_front(&completions),
962                                          struct test_trigger, trigger.node),
963                             now, "delayed");
964         }
965
966         ovsdb_trigger_wait(db, now);
967         poll_immediate_wake();
968         poll_block();
969     }
970
971     ovsdb_destroy(db);
972 }
973
974 static void
975 do_help(int argc UNUSED, char *argv[] UNUSED)
976 {
977     usage();
978 }
979 \f
980 /* "transact" command. */
981
982 static struct ovsdb *do_transact_db;
983 static struct ovsdb_txn *do_transact_txn;
984 static struct ovsdb_table *do_transact_table;
985
986 static void
987 do_transact_commit(int argc UNUSED, char *argv[] UNUSED)
988 {
989     ovsdb_txn_commit(do_transact_txn);
990     do_transact_txn = NULL;
991 }
992
993 static void
994 do_transact_abort(int argc UNUSED, char *argv[] UNUSED)
995 {
996     ovsdb_txn_abort(do_transact_txn);
997     do_transact_txn = NULL;
998 }
999
1000 static void
1001 uuid_from_integer(int integer, struct uuid *uuid)
1002 {
1003     uuid_zero(uuid);
1004     uuid->parts[3] = integer;
1005 }
1006
1007 static const struct ovsdb_row *
1008 do_transact_find_row(const char *uuid_string)
1009 {
1010     const struct ovsdb_row *row;
1011     struct uuid uuid;
1012
1013     uuid_from_integer(atoi(uuid_string), &uuid);
1014     row = ovsdb_table_get_row(do_transact_table, &uuid);
1015     if (!row) {
1016         ovs_fatal(0, "table does not contain row with UUID "UUID_FMT,
1017                   UUID_ARGS(&uuid));
1018     }
1019     return row;
1020 }
1021
1022 static void
1023 do_transact_set_integer(struct ovsdb_row *row, const char *column_name,
1024                         int integer)
1025 {
1026     if (integer != -1) {
1027         const struct ovsdb_column *column;
1028
1029         column = ovsdb_table_schema_get_column(do_transact_table->schema,
1030                                                column_name);
1031         row->fields[column->index].keys[0].integer = integer;
1032     }
1033 }
1034
1035 static int
1036 do_transact_get_integer(const struct ovsdb_row *row, const char *column_name)
1037 {
1038     const struct ovsdb_column *column;
1039
1040     column = ovsdb_table_schema_get_column(do_transact_table->schema,
1041                                            column_name);
1042     return row->fields[column->index].keys[0].integer;
1043 }
1044
1045 static void
1046 do_transact_set_i_j(struct ovsdb_row *row,
1047                     const char *i_string, const char *j_string)
1048 {
1049     do_transact_set_integer(row, "i", atoi(i_string));
1050     do_transact_set_integer(row, "j", atoi(j_string));
1051 }
1052
1053 static void
1054 do_transact_insert(int argc UNUSED, char *argv[] UNUSED)
1055 {
1056     struct ovsdb_row *row;
1057     struct uuid *uuid;
1058
1059     row = ovsdb_row_create(do_transact_table);
1060
1061     /* Set UUID. */
1062     uuid = ovsdb_row_get_uuid_rw(row);
1063     uuid_from_integer(atoi(argv[1]), uuid);
1064     if (ovsdb_table_get_row(do_transact_table, uuid)) {
1065         ovs_fatal(0, "table already contains row with UUID "UUID_FMT,
1066                   UUID_ARGS(uuid));
1067     }
1068
1069     do_transact_set_i_j(row, argv[2], argv[3]);
1070
1071     /* Insert row. */
1072     ovsdb_txn_row_insert(do_transact_txn, row);
1073 }
1074
1075 static void
1076 do_transact_delete(int argc UNUSED, char *argv[] UNUSED)
1077 {
1078     const struct ovsdb_row *row = do_transact_find_row(argv[1]);
1079     ovsdb_txn_row_delete(do_transact_txn, row);
1080 }
1081
1082 static void
1083 do_transact_modify(int argc UNUSED, char *argv[] UNUSED)
1084 {
1085     const struct ovsdb_row *row_ro;
1086     struct ovsdb_row *row_rw;
1087
1088     row_ro = do_transact_find_row(argv[1]);
1089     row_rw = ovsdb_txn_row_modify(do_transact_txn, row_ro);
1090     do_transact_set_i_j(row_rw, argv[2], argv[3]);
1091 }
1092
1093 static int
1094 compare_rows_by_uuid(const void *a_, const void *b_)
1095 {
1096     struct ovsdb_row *const *ap = a_;
1097     struct ovsdb_row *const *bp = b_;
1098
1099     return uuid_compare_3way(ovsdb_row_get_uuid(*ap), ovsdb_row_get_uuid(*bp));
1100 }
1101
1102 static void
1103 do_transact_print(int argc UNUSED, char *argv[] UNUSED)
1104 {
1105     const struct ovsdb_row **rows;
1106     const struct ovsdb_row *row;
1107     size_t n_rows;
1108     size_t i;
1109
1110     n_rows = hmap_count(&do_transact_table->rows);
1111     rows = xmalloc(n_rows * sizeof *rows);
1112     i = 0;
1113     HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node,
1114                    &do_transact_table->rows) {
1115         rows[i++] = row;
1116     }
1117     assert(i == n_rows);
1118
1119     qsort(rows, n_rows, sizeof *rows, compare_rows_by_uuid);
1120
1121     for (i = 0; i < n_rows; i++) {
1122         printf("\n%"PRId32": i=%d, j=%d",
1123                ovsdb_row_get_uuid(rows[i])->parts[3],
1124                do_transact_get_integer(rows[i], "i"),
1125                do_transact_get_integer(rows[i], "j"));
1126     }
1127
1128     free(rows);
1129 }
1130
1131 static void
1132 do_transact(int argc, char *argv[])
1133 {
1134     static const struct command do_transact_commands[] = {
1135         { "commit", 0, 0, do_transact_commit },
1136         { "abort", 0, 0, do_transact_abort },
1137         { "insert", 2, 3, do_transact_insert },
1138         { "delete", 1, 1, do_transact_delete },
1139         { "modify", 2, 3, do_transact_modify },
1140         { "print", 0, 0, do_transact_print },
1141         { NULL, 0, 0, NULL },
1142     };
1143
1144     struct ovsdb_schema *schema;
1145     struct json *json;
1146     int i;
1147
1148     /* Create table. */
1149     json = parse_json("{\"name\": \"testdb\", "
1150                       " \"tables\": "
1151                       "  {\"mytable\": "
1152                       "    {\"columns\": "
1153                       "      {\"i\": {\"type\": \"integer\"}, "
1154                       "       \"j\": {\"type\": \"integer\"}}}}}");
1155     check_ovsdb_error(ovsdb_schema_from_json(json, &schema));
1156     json_destroy(json);
1157     do_transact_db = ovsdb_create(NULL, schema);
1158     do_transact_table = ovsdb_get_table(do_transact_db, "mytable");
1159     assert(do_transact_table != NULL);
1160
1161     for (i = 1; i < argc; i++) {
1162         struct json *command;
1163         size_t n_args;
1164         char **args;
1165         int j;
1166
1167         command = parse_json(argv[i]);
1168         if (command->type != JSON_ARRAY) {
1169             ovs_fatal(0, "transaction %d must be JSON array "
1170                       "with at least 1 element", i);
1171         }
1172
1173         n_args = command->u.array.n;
1174         args = xmalloc((n_args + 1) * sizeof *args);
1175         for (j = 0; j < n_args; j++) {
1176             struct json *s = command->u.array.elems[j];
1177             if (s->type != JSON_STRING) {
1178                 ovs_fatal(0, "transaction %d argument %d must be JSON string",
1179                           i, j);
1180             }
1181             args[j] = xstrdup(json_string(s));
1182         }
1183         args[n_args] = NULL;
1184
1185         if (!do_transact_txn) {
1186             do_transact_txn = ovsdb_txn_create(do_transact_db);
1187         }
1188
1189         for (j = 0; j < n_args; j++) {
1190             if (j) {
1191                 putchar(' ');
1192             }
1193             fputs(args[j], stdout);
1194         }
1195         fputs(":", stdout);
1196         run_command(n_args, args, do_transact_commands);
1197         putchar('\n');
1198
1199         for (j = 0; j < n_args; j++) {
1200             free(args[j]);
1201         }
1202         free(args);
1203         json_destroy(command);
1204     }
1205     ovsdb_txn_abort(do_transact_txn);
1206     ovsdb_destroy(do_transact_db); /* Also destroys 'schema'. */
1207 }
1208
1209 static struct command all_commands[] = {
1210     { "log-io", 2, INT_MAX, do_log_io },
1211     { "parse-atomic-type", 1, 1, do_parse_atomic_type },
1212     { "parse-type", 1, 1, do_parse_type },
1213     { "parse-atoms", 2, INT_MAX, do_parse_atoms },
1214     { "parse-data", 2, INT_MAX, do_parse_data },
1215     { "sort-atoms", 2, 2, do_sort_atoms },
1216     { "parse-column", 2, 2, do_parse_column },
1217     { "parse-table", 2, 2, do_parse_table },
1218     { "parse-rows", 2, INT_MAX, do_parse_rows },
1219     { "compare-rows", 2, INT_MAX, do_compare_rows },
1220     { "parse-conditions", 2, INT_MAX, do_parse_conditions },
1221     { "evaluate-conditions", 3, 3, do_evaluate_conditions },
1222     { "query", 3, 3, do_query },
1223     { "query-distinct", 4, 4, do_query_distinct },
1224     { "transact", 1, INT_MAX, do_transact },
1225     { "execute", 2, INT_MAX, do_execute },
1226     { "trigger", 2, INT_MAX, do_trigger },
1227     { "help", 0, INT_MAX, do_help },
1228     { NULL, 0, 0, NULL },
1229 };