support exception format strings
This commit is contained in:
parent
3874860d50
commit
26f1a0a4c8
|
@ -55,10 +55,7 @@ ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) {
|
||||||
if (primary_key.length()) {
|
if (primary_key.length()) {
|
||||||
auto primary_key_prop = primary_key_property();
|
auto primary_key_prop = primary_key_property();
|
||||||
if (!primary_key_prop) {
|
if (!primary_key_prop) {
|
||||||
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, {
|
throw ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey, name, primary_key);
|
||||||
{ObjectStoreException::InfoKey::ObjectType, name},
|
|
||||||
{ObjectStoreException::InfoKey::PrimaryKey, ""},
|
|
||||||
{ObjectStoreException::InfoKey::OldPrimaryKey, primary_key}});
|
|
||||||
}
|
}
|
||||||
primary_key_prop->is_primary = true;
|
primary_key_prop->is_primary = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,11 +201,14 @@ std::vector<ObjectStoreException> ObjectStore::validate_schema(Group *group, Obj
|
||||||
|
|
||||||
// check for change to primary key
|
// check for change to primary key
|
||||||
if (table_schema.primary_key != target_schema.primary_key) {
|
if (table_schema.primary_key != target_schema.primary_key) {
|
||||||
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaMismatchedPrimaryKey, {
|
if (table_schema.primary_key.length()) {
|
||||||
{ObjectStoreException::InfoKey::ObjectType, target_schema.name},
|
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaChangedPrimaryKey,
|
||||||
{ObjectStoreException::InfoKey::PrimaryKey, target_schema.primary_key},
|
table_schema.name, table_schema.primary_key));
|
||||||
{ObjectStoreException::InfoKey::OldPrimaryKey, table_schema.primary_key},
|
}
|
||||||
}));
|
else {
|
||||||
|
exceptions.emplace_back(ObjectStoreException(ObjectStoreException::Kind::ObjectSchemaNewPrimaryKey,
|
||||||
|
target_schema.name, target_schema.primary_key));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for new missing properties
|
// check for new missing properties
|
||||||
|
@ -346,9 +349,7 @@ bool ObjectStore::create_tables(Group *group, ObjectStore::Schema &target_schema
|
||||||
bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) {
|
bool ObjectStore::is_schema_at_version(Group *group, uint64_t version) {
|
||||||
uint64_t old_version = get_schema_version(group);
|
uint64_t old_version = get_schema_version(group);
|
||||||
if (old_version > version && old_version != NotVersioned) {
|
if (old_version > version && old_version != NotVersioned) {
|
||||||
throw ObjectStoreException(ObjectStoreException::Kind::RealmVersionGreaterThanSchemaVersion, {
|
throw ObjectStoreException(old_version, version);
|
||||||
{ObjectStoreException::InfoKey::OldVersion, to_string(old_version)},
|
|
||||||
{ObjectStoreException::InfoKey::NewVersion, to_string(version)}});
|
|
||||||
}
|
}
|
||||||
return old_version != version;
|
return old_version != version;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,99 +20,108 @@
|
||||||
#include "property.hpp"
|
#include "property.hpp"
|
||||||
|
|
||||||
#include <realm/util/assert.hpp>
|
#include <realm/util/assert.hpp>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
using namespace realm;
|
using namespace realm;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ObjectStoreException::CustomWhat ObjectStoreException::s_custom_what = nullptr;
|
ObjectStoreException::ObjectStoreException(Kind kind, Info info) : m_kind(kind), m_info(info), m_what(generate_what()) {}
|
||||||
string ObjectStoreException::s_property_string = "property";
|
|
||||||
string ObjectStoreException::s_property_string_upper = "Property";
|
|
||||||
|
|
||||||
ObjectStoreException::ObjectStoreException(Kind kind, Info info) : m_kind(kind), m_info(info) {
|
|
||||||
set_what();
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) {
|
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop) : m_kind(kind) {
|
||||||
m_info[InfoKey::ObjectType] = object_type;
|
m_info[InfoKeyObjectType] = object_type;
|
||||||
m_info[InfoKey::PropertyName] = prop.name;
|
m_info[InfoKeyPropertyName] = prop.name;
|
||||||
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type);
|
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||||
m_info[InfoKey::PropertyObjectType] = prop.object_type;
|
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||||
set_what();
|
m_what = generate_what();
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) :
|
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp) :
|
||||||
m_kind(kind) {
|
m_kind(kind) {
|
||||||
m_info[InfoKey::ObjectType] = object_type;
|
m_info[InfoKeyObjectType] = object_type;
|
||||||
m_info[InfoKey::PropertyName] = prop.name;
|
m_info[InfoKeyPropertyName] = prop.name;
|
||||||
m_info[InfoKey::PropertyType] = string_for_property_type(prop.type);
|
m_info[InfoKeyPropertyType] = string_for_property_type(prop.type);
|
||||||
m_info[InfoKey::OldPropertyType] = string_for_property_type(oldProp.type);
|
m_info[InfoKeyOldPropertyType] = string_for_property_type(oldProp.type);
|
||||||
m_info[InfoKey::PropertyObjectType] = prop.object_type;
|
m_info[InfoKeyPropertyObjectType] = prop.object_type;
|
||||||
m_info[InfoKey::OldPropertyObjectType] = oldProp.object_type;
|
m_info[InfoKeyOldPropertyObjectType] = oldProp.object_type;
|
||||||
set_what();
|
m_what = generate_what();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectStoreException::set_what() {
|
ObjectStoreException::ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key) : m_kind(kind) {
|
||||||
if (s_custom_what) {
|
m_info[InfoKeyObjectType] = object_type;
|
||||||
string custom = s_custom_what(*this);
|
m_info[InfoKeyPrimaryKey] = primary_key;
|
||||||
if (custom.length()) {
|
m_what = generate_what();
|
||||||
m_what = custom;
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (m_kind) {
|
ObjectStoreException::ObjectStoreException(uint64_t old_version, uint64_t new_version) : m_kind(Kind::RealmVersionGreaterThanSchemaVersion) {
|
||||||
case Kind::RealmVersionGreaterThanSchemaVersion:
|
m_info[InfoKeyOldVersion] = to_string(old_version);
|
||||||
m_what = "Provided schema version " + m_info[InfoKey::OldVersion] +
|
m_info[InfoKeyNewVersion] = to_string(new_version);
|
||||||
" is less than last set version " + m_info[InfoKey::NewVersion] + ".";
|
m_what = generate_what();
|
||||||
break;
|
|
||||||
case Kind::RealmPropertyTypeNotIndexable:
|
|
||||||
m_what = "Can't index " + s_property_string + " '" + m_info[InfoKey::ObjectType] + "." + m_info[InfoKey::PropertyName] + "': " +
|
|
||||||
"indexing a " + s_property_string + " of type '" + m_info[InfoKey::PropertyType] + "' is currently not supported";
|
|
||||||
break;
|
|
||||||
case Kind::RealmDuplicatePrimaryKeyValue:
|
|
||||||
m_what = "Primary key " + s_property_string + " '" + m_info[InfoKey::PropertyType] + "' has duplicate values after migration.";
|
|
||||||
break;
|
|
||||||
case Kind::ObjectSchemaMissingProperty:
|
|
||||||
m_what = s_property_string_upper + " '" + m_info[InfoKey::PropertyName] + "' is missing from latest object model.";
|
|
||||||
break;
|
|
||||||
case Kind::ObjectSchemaNewProperty:
|
|
||||||
m_what = s_property_string_upper + " '" + m_info[InfoKey::PropertyName] + "' has been added to latest object model.";
|
|
||||||
break;
|
|
||||||
case Kind::ObjectSchemaMismatchedTypes:
|
|
||||||
m_what = s_property_string_upper + " types for '" + m_info[InfoKey::PropertyName] + "' " + s_property_string + " do not match. " +
|
|
||||||
"Old type '" + m_info[InfoKey::OldPropertyType] + "', new type '" + m_info[InfoKey::PropertyType] + "'";
|
|
||||||
break;
|
|
||||||
case Kind::ObjectSchemaMismatchedObjectTypes:
|
|
||||||
m_what = "Target object type for " + s_property_string + " '" + m_info[InfoKey::PropertyName] + "' does not match. " +
|
|
||||||
"Old type '" + m_info[InfoKey::OldPropertyObjectType] + "', new type '" + m_info[InfoKey::PropertyObjectType] + "'.";
|
|
||||||
break;
|
|
||||||
case Kind::ObjectSchemaMismatchedPrimaryKey:
|
|
||||||
if (!m_info[InfoKey::PrimaryKey].length()) {
|
|
||||||
m_what = s_property_string_upper + " '" + m_info[InfoKey::OldPrimaryKey] + "' is no longer a primary key.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_what = s_property_string_upper + " '" + m_info[InfoKey::PrimaryKey] + "' has been made a primary key.";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Kind::ObjectStoreValidationFailure:
|
|
||||||
m_what = "Migration is required for object type '" + info().at(InfoKey::ObjectType) + "' due to the following errors:";
|
|
||||||
for (auto error : m_validation_errors) {
|
|
||||||
m_what += string("\n- ") + error.what();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectStoreException::ObjectStoreException(vector<ObjectStoreException> validation_errors, const string &object_type) :
|
ObjectStoreException::ObjectStoreException(vector<ObjectStoreException> validation_errors, const string &object_type) :
|
||||||
m_validation_errors(validation_errors), m_kind(Kind::ObjectStoreValidationFailure), m_info({{InfoKey::ObjectType, object_type}}) {
|
m_validation_errors(validation_errors),
|
||||||
set_what();
|
m_kind(Kind::ObjectStoreValidationFailure),
|
||||||
|
m_info({{InfoKeyObjectType, object_type}}),
|
||||||
|
m_what(generate_what()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectStoreException::set_property_string(std::string property_string) {
|
string ObjectStoreException::generate_what() const {
|
||||||
s_property_string = s_property_string_upper = property_string;
|
auto format_string = s_custom_format_strings.find(m_kind);
|
||||||
s_property_string[0] = tolower(s_property_string[0]);
|
if (format_string != s_custom_format_strings.end()) {
|
||||||
s_property_string_upper[0] = toupper(s_property_string_upper[0]);
|
return populate_format_string(format_string->second);
|
||||||
|
}
|
||||||
|
return populate_format_string(s_default_format_strings.at(m_kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string ObjectStoreException::validation_errors_string() const {
|
||||||
|
string errors_string;
|
||||||
|
for (auto error : m_validation_errors) {
|
||||||
|
errors_string += string("\n- ") + error.what();
|
||||||
|
}
|
||||||
|
return errors_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ObjectStoreException::populate_format_string(const std::string & format_string) const {
|
||||||
|
string out_string, current(format_string);
|
||||||
|
smatch sm;
|
||||||
|
regex re("\\{(\\w+)\\}");
|
||||||
|
while(regex_search(current, sm, re)) {
|
||||||
|
out_string += sm.prefix();
|
||||||
|
const string &key = sm[1];
|
||||||
|
if (key == "ValidationString") {
|
||||||
|
out_string += validation_errors_string();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out_string += m_info.at(key);
|
||||||
|
}
|
||||||
|
current = sm.suffix();
|
||||||
|
}
|
||||||
|
out_string += current;
|
||||||
|
return out_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectStoreException::FormatStrings ObjectStoreException::s_custom_format_strings;
|
||||||
|
const ObjectStoreException::FormatStrings ObjectStoreException::s_default_format_strings = {
|
||||||
|
{Kind::RealmVersionGreaterThanSchemaVersion,
|
||||||
|
"Provided schema version {InfoKeyOldVersion} is less than last set version {InfoKeyNewVersion}."},
|
||||||
|
{Kind::RealmPropertyTypeNotIndexable,
|
||||||
|
"Can't index property {InfoKeyObjectType}.{InfoKeyPropertyName}: indexing a property of type '{InfoKeyPropertyType}' is currently not supported"},
|
||||||
|
{Kind::RealmDuplicatePrimaryKeyValue,
|
||||||
|
"Primary key property '{InfoKeyPropertyType}' has duplicate values after migration."},
|
||||||
|
{Kind::ObjectSchemaMissingProperty,
|
||||||
|
"Property '{InfoKeyPropertyName}' is missing from latest object model."},
|
||||||
|
{Kind::ObjectSchemaNewProperty,
|
||||||
|
"Property '{InfoKeyPropertyName}' has been added to latest object model."},
|
||||||
|
{Kind::ObjectSchemaMismatchedTypes,
|
||||||
|
"Property types for '{InfoKeyPropertyName}' property do not match. Old type '{InfoKeyOldPropertyType}', new type '{InfoKeyPropertyType}'"},
|
||||||
|
{Kind::ObjectSchemaMismatchedObjectTypes,
|
||||||
|
"Target object type for property '{InfoKeyPropertyName}' does not match. Old type '{InfoKeyOldPropertyObjectType}', new type '{InfoKeyPropertyObjectType}'."},
|
||||||
|
{Kind::ObjectSchemaChangedPrimaryKey,
|
||||||
|
"Property '{InfoKeyPrimaryKey}' is no longer a primary key."},
|
||||||
|
{Kind::ObjectSchemaNewPrimaryKey,
|
||||||
|
"Property '{InfoKeyPrimaryKey}' has been made a primary key."},
|
||||||
|
{Kind::ObjectStoreValidationFailure,
|
||||||
|
"Migration is required for object type '{InfoKeyObjectType}' due to the following errors: {ValidationErrors}"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,14 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define INFO_KEY(key) InfoKey InfoKey##key = "InfoKey" #key;
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
class Property;
|
class Property;
|
||||||
|
|
||||||
class ObjectStoreException : public std::exception {
|
class ObjectStoreException : public std::exception {
|
||||||
public:
|
public:
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
// thrown when calling update_realm_to_schema and the realm version is greater than the given version
|
|
||||||
RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion
|
RealmVersionGreaterThanSchemaVersion, // OldVersion, NewVersion
|
||||||
RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType
|
RealmPropertyTypeNotIndexable, // ObjectType, PropertyName, PropertyType
|
||||||
RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType
|
RealmDuplicatePrimaryKeyValue, // ObjectType, PropertyName, PropertyType
|
||||||
|
@ -37,55 +38,62 @@ namespace realm {
|
||||||
ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType
|
ObjectSchemaNewProperty, // ObjectType, PropertyName, PropertyType
|
||||||
ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType
|
ObjectSchemaMismatchedTypes, // ObjectType, PropertyName, PropertyType, OldPropertyType
|
||||||
ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType
|
ObjectSchemaMismatchedObjectTypes, // ObjectType, PropertyName, PropertyType, ObjectType, OldObjectType
|
||||||
ObjectSchemaMismatchedPrimaryKey, // ObjectType, PrimaryKey, OldPrimaryKey
|
ObjectSchemaChangedPrimaryKey, // ObjectType, PrimaryKey
|
||||||
|
ObjectSchemaNewPrimaryKey, // ObjectType, PrimaryKey
|
||||||
ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException>
|
ObjectStoreValidationFailure, // ObjectType, vector<ObjectStoreException>
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InfoKey {
|
typedef const std::string InfoKey;
|
||||||
OldVersion,
|
|
||||||
NewVersion,
|
|
||||||
ObjectType,
|
|
||||||
PropertyName,
|
|
||||||
PropertyType,
|
|
||||||
OldPropertyType,
|
|
||||||
PropertyObjectType,
|
|
||||||
OldPropertyObjectType,
|
|
||||||
PrimaryKey,
|
|
||||||
OldPrimaryKey,
|
|
||||||
};
|
|
||||||
typedef std::map<InfoKey, std::string> Info;
|
typedef std::map<InfoKey, std::string> Info;
|
||||||
|
|
||||||
ObjectStoreException(Kind kind, Info info = Info());
|
|
||||||
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop);
|
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop);
|
||||||
|
|
||||||
|
// ObjectSchemaMismatchedTypes, ObjectSchemaMismatchedObjectTypes
|
||||||
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp);
|
ObjectStoreException(Kind kind, const std::string &object_type, const Property &prop, const Property &oldProp);
|
||||||
|
|
||||||
|
// ObjectSchemaChangedPrimaryKey, ObjectSchemaNewPrimaryKey
|
||||||
|
ObjectStoreException(Kind kind, const std::string &object_type, const std::string primary_key);
|
||||||
|
|
||||||
|
// RealmVersionGreaterThanSchemaVersion
|
||||||
|
ObjectStoreException(uint64_t old_version, uint64_t new_version);
|
||||||
|
|
||||||
// ObjectStoreValidationFailure
|
// ObjectStoreValidationFailure
|
||||||
ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type);
|
ObjectStoreException(std::vector<ObjectStoreException> validation_errors, const std::string &object_type);
|
||||||
|
|
||||||
ObjectStoreException::Kind kind() const { return m_kind; }
|
ObjectStoreException::Kind kind() const { return m_kind; }
|
||||||
const ObjectStoreException::Info &info() const { return m_info; }
|
const ObjectStoreException::Info &info() const { return m_info; }
|
||||||
const std::vector<ObjectStoreException> &validation_errors() { return m_validation_errors; }
|
|
||||||
|
|
||||||
const char *what() const noexcept override { return m_what.c_str(); }
|
const char *what() const noexcept override { return m_what.c_str(); }
|
||||||
|
|
||||||
// implement CustomWhat to customize exception messages per platform/language
|
// implement CustomWhat to customize exception messages per platform/language
|
||||||
typedef std::string (*CustomWhat)(ObjectStoreException &);
|
typedef std::map<Kind, std::string> FormatStrings;
|
||||||
static void set_custom_what(CustomWhat message_generator) { s_custom_what = message_generator; }
|
static void set_custom_format_strings(FormatStrings custom_format_strings) { s_custom_format_strings = custom_format_strings; }
|
||||||
|
|
||||||
// set the string used in defualt messages to represent the property object - defaults to 'property'
|
|
||||||
static void set_property_string(std::string property_string);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ObjectStoreException(Kind kind, Info info = Info());
|
||||||
|
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
Info m_info;
|
Info m_info;
|
||||||
std::vector<ObjectStoreException> m_validation_errors;
|
std::vector<ObjectStoreException> m_validation_errors;
|
||||||
|
|
||||||
std::string m_what;
|
std::string m_what;
|
||||||
void set_what();
|
std::string generate_what() const;
|
||||||
|
std::string validation_errors_string() const;
|
||||||
|
std::string populate_format_string(const std::string &format_string) const;
|
||||||
|
|
||||||
static CustomWhat s_custom_what;
|
static const FormatStrings s_default_format_strings;
|
||||||
static std::string s_property_string;
|
static FormatStrings s_custom_format_strings;
|
||||||
static std::string s_property_string_upper;
|
|
||||||
|
public:
|
||||||
|
INFO_KEY(OldVersion);
|
||||||
|
INFO_KEY(NewVersion);
|
||||||
|
INFO_KEY(ObjectType);
|
||||||
|
INFO_KEY(PropertyName);
|
||||||
|
INFO_KEY(PropertyType);
|
||||||
|
INFO_KEY(OldPropertyType);
|
||||||
|
INFO_KEY(PropertyObjectType);
|
||||||
|
INFO_KEY(OldPropertyObjectType);
|
||||||
|
INFO_KEY(PrimaryKey);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue