const struct json *
ovsdb_parser_member(struct ovsdb_parser *parser, const char *name,
                    enum ovsdb_parser_types types)
{
    struct json *value;

    if (!parser->json) {
        return NULL;
    }

    value = shash_find_data(json_object(parser->json), name);
    if (!value) {
        if (!(types & OP_OPTIONAL)) {
            ovsdb_parser_raise_error(parser,
                                     "Required '%s' member is missing.", name);
        }
        return NULL;
    }

    if (((int) value->type >= 0 && value->type < JSON_N_TYPES
         && types & (1u << value->type))
        || (types & OP_ID && value->type == JSON_STRING
            && ovsdb_parser_is_id(value->u.string)))
    {
        sset_add(&parser->used, name);
        return value;
    } else {
        ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.",
                                 name);
        return NULL;
    }
}
struct ovsdb_error *
ovsdb_parser_finish(struct ovsdb_parser *parser)
{
    if (!parser->error) {
        const struct shash *object = json_object(parser->json);
        size_t n_unused;

        n_unused = shash_count(object) - sset_count(&parser->used);
        if (n_unused) {
            struct shash_node *node;

            SHASH_FOR_EACH (node, object) {
                if (!sset_contains(&parser->used, node->name)) {
                    if (n_unused > 1) {
                        ovsdb_parser_raise_error(
                            parser,
                            "Member '%s' and %zu other member%s "
                            "are present but not allowed here.",
                            node->name, n_unused - 1, n_unused > 2 ? "s" : "");
                    } else {
                        ovsdb_parser_raise_error(
                            parser,
                            "Member '%s' is present but not allowed here.",
                            node->name);
                    }
                    break;
                }
            }
        }
    }

    free(parser->name);
    sset_destroy(&parser->used);

    return parser->error;
}
void
ovsdb_parser_init(struct ovsdb_parser *parser, const struct json *json,
                  const char *name, ...)
{
    va_list args;

    va_start(args, name);
    parser->name = xvasprintf(name, args);
    va_end(args);

    sset_init(&parser->used);
    parser->error = NULL;

    parser->json = (json && json->type == JSON_OBJECT ? json : NULL);
    if (!parser->json) {
        ovsdb_parser_raise_error(parser, "Object expected.");
    }
}
struct json *
ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session,
              const struct json *params,
              long long int elapsed_msec, long long int *timeout_msec)
{
    struct ovsdb_execution x;
    struct ovsdb_error *error;
    struct json *results;
    size_t n_operations;
    size_t i;

    if (params->type != JSON_ARRAY
        || !params->u.array.n
        || params->u.array.elems[0]->type != JSON_STRING
        || strcmp(params->u.array.elems[0]->u.string, db->schema->name)) {
        if (params->type != JSON_ARRAY) {
            error = ovsdb_syntax_error(params, NULL, "array expected");
        } else {
            error = ovsdb_syntax_error(params, NULL, "database name expected "
                                       "as first parameter");
        }

        results = ovsdb_error_to_json(error);
        ovsdb_error_destroy(error);
        return results;
    }

    x.db = db;
    x.session = session;
    x.txn = ovsdb_txn_create(db);
    x.symtab = ovsdb_symbol_table_create();
    x.durable = false;
    x.elapsed_msec = elapsed_msec;
    x.timeout_msec = LLONG_MAX;
    results = NULL;

    results = json_array_create_empty();
    n_operations = params->u.array.n - 1;
    error = NULL;
    for (i = 1; i <= n_operations; i++) {
        struct json *operation = params->u.array.elems[i];
        struct ovsdb_error *parse_error;
        struct ovsdb_parser parser;
        struct json *result;
        const struct json *op;

        /* Parse and execute operation. */
        ovsdb_parser_init(&parser, operation,
                          "ovsdb operation %"PRIuSIZE" of %"PRIuSIZE, i, n_operations);
        op = ovsdb_parser_member(&parser, "op", OP_ID);
        result = json_object_create();
        if (op) {
            const char *op_name = json_string(op);
            ovsdb_operation_executor *executor = lookup_executor(op_name);
            if (executor) {
                error = executor(&x, &parser, result);
            } else {
                ovsdb_parser_raise_error(&parser, "No operation \"%s\"",
                                         op_name);
            }
        } else {
            ovs_assert(ovsdb_parser_has_error(&parser));
        }

        /* A parse error overrides any other error.
         * An error overrides any other result. */
        parse_error = ovsdb_parser_finish(&parser);
        if (parse_error) {
            ovsdb_error_destroy(error);
            error = parse_error;
        }
        if (error) {
            json_destroy(result);
            result = ovsdb_error_to_json(error);
        }
        if (error && !strcmp(ovsdb_error_get_tag(error), "not supported")
            && timeout_msec) {
            ovsdb_txn_abort(x.txn);
            *timeout_msec = x.timeout_msec;

            json_destroy(result);
            json_destroy(results);
            results = NULL;
            goto exit;
        }

        /* Add result to array. */
        json_array_add(results, result);
        if (error) {
            break;
        }
    }

    if (!error) {
        error = ovsdb_txn_commit(x.txn, x.durable);
        if (error) {
            json_array_add(results, ovsdb_error_to_json(error));
        }
    } else {
        ovsdb_txn_abort(x.txn);
    }

    while (json_array(results)->n < n_operations) {
        json_array_add(results, json_null_create());
    }

exit:
    ovsdb_error_destroy(error);
    ovsdb_symbol_table_destroy(x.symtab);

    return results;
}