+ OVS_NOT_REACHED();
+ }
+}
+
+/* Appends 'atom' (which has the given 'type') to 'out', in a bare string
+ * format that cannot be parsed uniformly back into a datum but is easier for
+ * shell scripts, etc., to deal with. */
+void
+ovsdb_atom_to_bare(const union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ struct ds *out)
+{
+ if (type == OVSDB_TYPE_STRING) {
+ ds_put_cstr(out, atom->string);
+ } else {
+ ovsdb_atom_to_string(atom, type, out);
+ }
+}
+
+static struct ovsdb_error *
+check_string_constraints(const char *s,
+ const struct ovsdb_string_constraints *c)
+{
+ size_t n_chars;
+ char *msg;
+
+ msg = utf8_validate(s, &n_chars);
+ if (msg) {
+ struct ovsdb_error *error;
+
+ error = ovsdb_error("constraint violation",
+ "not a valid UTF-8 string: %s", msg);
+ free(msg);
+ return error;
+ }
+
+ if (n_chars < c->minLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %"PRIuSIZE" is less than minimum allowed "
+ "length %u", s, n_chars, c->minLen);
+ } else if (n_chars > c->maxLen) {
+ return ovsdb_error(
+ "constraint violation",
+ "\"%s\" length %"PRIuSIZE" is greater than maximum allowed "
+ "length %u", s, n_chars, c->maxLen);
+ }
+
+ return NULL;
+}
+
+/* Checks whether 'atom' meets the constraints (if any) defined in 'base'.
+ * (base->type must specify 'atom''s type.) Returns a null pointer if the
+ * constraints are met, otherwise an error that explains the violation.
+ *
+ * Checking UUID constraints is deferred to transaction commit time, so this
+ * function does nothing for UUID constraints. */
+struct ovsdb_error *
+ovsdb_atom_check_constraints(const union ovsdb_atom *atom,
+ const struct ovsdb_base_type *base)
+{
+ if (base->enum_
+ && ovsdb_datum_find_key(base->enum_, atom, base->type) == UINT_MAX) {
+ struct ovsdb_error *error;
+ struct ds actual = DS_EMPTY_INITIALIZER;
+ struct ds valid = DS_EMPTY_INITIALIZER;
+
+ ovsdb_atom_to_string(atom, base->type, &actual);
+ ovsdb_datum_to_string(base->enum_,
+ ovsdb_base_type_get_enum_type(base->type),
+ &valid);
+ error = ovsdb_error("constraint violation",
+ "%s is not one of the allowed values (%s)",
+ ds_cstr(&actual), ds_cstr(&valid));
+ ds_destroy(&actual);
+ ds_destroy(&valid);
+
+ return error;
+ }
+
+ switch (base->type) {
+ case OVSDB_TYPE_VOID:
+ OVS_NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ if (atom->integer >= base->u.integer.min
+ && atom->integer <= base->u.integer.max) {
+ return NULL;
+ } else if (base->u.integer.min != INT64_MIN) {
+ if (base->u.integer.max != INT64_MAX) {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is not in the valid range "
+ "%"PRId64" to %"PRId64" (inclusive)",
+ atom->integer,
+ base->u.integer.min, base->u.integer.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is less than minimum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%"PRId64" is greater than maximum allowed "
+ "value %"PRId64,
+ atom->integer, base->u.integer.max);
+ }
+ OVS_NOT_REACHED();
+
+ case OVSDB_TYPE_REAL:
+ if (atom->real >= base->u.real.min && atom->real <= base->u.real.max) {
+ return NULL;
+ } else if (base->u.real.min != -DBL_MAX) {
+ if (base->u.real.max != DBL_MAX) {
+ return ovsdb_error("constraint violation",
+ "%.*g is not in the valid range "
+ "%.*g to %.*g (inclusive)",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min,
+ DBL_DIG, base->u.real.max);
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is less than minimum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.min);
+ }
+ } else {
+ return ovsdb_error("constraint violation",
+ "%.*g is greater than maximum allowed "
+ "value %.*g",
+ DBL_DIG, atom->real,
+ DBL_DIG, base->u.real.max);
+ }
+ OVS_NOT_REACHED();
+
+ case OVSDB_TYPE_BOOLEAN:
+ return NULL;
+
+ case OVSDB_TYPE_STRING:
+ return check_string_constraints(atom->string, &base->u.string);
+
+ case OVSDB_TYPE_UUID:
+ return NULL;
+
+ case OVSDB_N_TYPES:
+ default:
+ OVS_NOT_REACHED();