2015-05-28 23:53:35 +00:00
////////////////////////////////////////////////////////////////////////////
//
2015-06-11 17:33:46 +00:00
// Copyright 2015 Realm Inc.
2015-05-28 23:53:35 +00:00
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
# include "object_store.hpp"
2015-06-10 21:21:43 +00:00
# include <realm/group.hpp>
# include <realm/table.hpp>
2015-06-05 01:14:53 +00:00
# include <realm/link_view.hpp>
# include <realm/table_view.hpp>
2015-06-05 20:13:08 +00:00
# include <realm/util/assert.hpp>
2015-06-05 01:14:53 +00:00
2015-06-11 17:33:46 +00:00
# include <string.h>
2015-06-10 21:53:24 +00:00
2015-05-28 23:53:35 +00:00
using namespace realm ;
const char * const c_metadataTableName = " metadata " ;
const char * const c_versionColumnName = " version " ;
const size_t c_versionColumnIndex = 0 ;
const char * const c_primaryKeyTableName = " pk " ;
const char * const c_primaryKeyObjectClassColumnName = " pk_table " ;
const size_t c_primaryKeyObjectClassColumnIndex = 0 ;
const char * const c_primaryKeyPropertyNameColumnName = " pk_property " ;
const size_t c_primaryKeyPropertyNameColumnIndex = 1 ;
2015-06-11 17:33:46 +00:00
const size_t c_zeroRowIndex = 0 ;
2015-07-20 23:11:15 +00:00
const std : : string c_object_table_prefix = " class_ " ;
2015-06-11 17:33:46 +00:00
const size_t c_object_table_prefix_length = c_object_table_prefix . length ( ) ;
2015-06-04 00:27:21 +00:00
2015-07-20 23:11:15 +00:00
const uint64_t ObjectStore : : NotVersioned = std : : numeric_limits < uint64_t > : : max ( ) ;
2015-05-28 23:53:35 +00:00
2015-06-11 17:33:46 +00:00
bool ObjectStore : : has_metadata_tables ( Group * group ) {
2015-05-28 23:53:35 +00:00
return group - > get_table ( c_primaryKeyTableName ) & & group - > get_table ( c_metadataTableName ) ;
}
2015-06-11 17:33:46 +00:00
bool ObjectStore : : create_metadata_tables ( Group * group ) {
2015-05-28 23:53:35 +00:00
bool changed = false ;
2015-06-11 17:33:46 +00:00
TableRef table = group - > get_or_add_table ( c_primaryKeyTableName ) ;
2015-05-28 23:53:35 +00:00
if ( table - > get_column_count ( ) = = 0 ) {
2015-06-11 17:33:46 +00:00
table - > add_column ( type_String , c_primaryKeyObjectClassColumnName ) ;
table - > add_column ( type_String , c_primaryKeyPropertyNameColumnName ) ;
2015-05-28 23:53:35 +00:00
changed = true ;
}
table = group - > get_or_add_table ( c_metadataTableName ) ;
if ( table - > get_column_count ( ) = = 0 ) {
2015-06-11 17:33:46 +00:00
table - > add_column ( type_Int , c_versionColumnName ) ;
2015-05-28 23:53:35 +00:00
// set initial version
table - > add_empty_row ( ) ;
2015-06-11 17:33:46 +00:00
table - > set_int ( c_versionColumnIndex , c_zeroRowIndex , ObjectStore : : NotVersioned ) ;
2015-05-28 23:53:35 +00:00
changed = true ;
}
return changed ;
}
2015-06-11 17:33:46 +00:00
uint64_t ObjectStore : : get_schema_version ( Group * group ) {
TableRef table = group - > get_table ( c_metadataTableName ) ;
2015-05-28 23:53:35 +00:00
if ( ! table | | table - > get_column_count ( ) = = 0 ) {
2015-06-11 17:33:46 +00:00
return ObjectStore : : NotVersioned ;
2015-05-28 23:53:35 +00:00
}
2015-06-11 17:33:46 +00:00
return table - > get_int ( c_versionColumnIndex , c_zeroRowIndex ) ;
2015-05-28 23:53:35 +00:00
}
2015-06-11 17:33:46 +00:00
void ObjectStore : : set_schema_version ( Group * group , uint64_t version ) {
TableRef table = group - > get_or_add_table ( c_metadataTableName ) ;
table - > set_int ( c_versionColumnIndex , c_zeroRowIndex , version ) ;
2015-05-28 23:53:35 +00:00
}
2015-06-11 17:33:46 +00:00
StringData ObjectStore : : get_primary_key_for_object ( Group * group , StringData object_type ) {
TableRef table = group - > get_table ( c_primaryKeyTableName ) ;
2015-05-28 23:53:35 +00:00
if ( ! table ) {
return " " ;
}
size_t row = table - > find_first_string ( c_primaryKeyObjectClassColumnIndex , object_type ) ;
2015-06-11 17:33:46 +00:00
if ( row = = not_found ) {
2015-05-28 23:53:35 +00:00
return " " ;
}
return table - > get_string ( c_primaryKeyPropertyNameColumnIndex , row ) ;
}
2015-06-11 17:33:46 +00:00
void ObjectStore : : set_primary_key_for_object ( Group * group , StringData object_type , StringData primary_key ) {
TableRef table = group - > get_table ( c_primaryKeyTableName ) ;
2015-05-28 23:53:35 +00:00
// get row or create if new object and populate
size_t row = table - > find_first_string ( c_primaryKeyObjectClassColumnIndex , object_type ) ;
2015-06-11 17:33:46 +00:00
if ( row = = not_found & & primary_key . size ( ) ) {
2015-05-28 23:53:35 +00:00
row = table - > add_empty_row ( ) ;
table - > set_string ( c_primaryKeyObjectClassColumnIndex , row , object_type ) ;
}
// set if changing, or remove if setting to nil
2015-06-10 21:53:24 +00:00
if ( primary_key . size ( ) = = 0 ) {
2015-06-11 17:33:46 +00:00
if ( row ! = not_found ) {
2015-06-10 21:53:24 +00:00
table - > remove ( row ) ;
}
2015-05-28 23:53:35 +00:00
}
else {
table - > set_string ( c_primaryKeyPropertyNameColumnIndex , row , primary_key ) ;
}
}
2015-07-20 23:11:15 +00:00
std : : string ObjectStore : : object_type_for_table_name ( const std : : string & table_name ) {
2015-06-11 17:33:46 +00:00
if ( table_name . size ( ) > = c_object_table_prefix_length & & table_name . compare ( 0 , c_object_table_prefix_length , c_object_table_prefix ) = = 0 ) {
return table_name . substr ( c_object_table_prefix_length , table_name . length ( ) - c_object_table_prefix_length ) ;
2015-06-04 00:27:21 +00:00
}
2015-07-20 23:11:15 +00:00
return std : : string ( ) ;
2015-06-04 00:27:21 +00:00
}
2015-07-20 23:11:15 +00:00
std : : string ObjectStore : : table_name_for_object_type ( const std : : string & object_type ) {
2015-06-11 17:33:46 +00:00
return c_object_table_prefix + object_type ;
2015-06-04 00:27:21 +00:00
}
2015-06-11 17:33:46 +00:00
TableRef ObjectStore : : table_for_object_type ( Group * group , StringData object_type ) {
2015-06-04 01:04:21 +00:00
return group - > get_table ( table_name_for_object_type ( object_type ) ) ;
2015-06-04 00:27:21 +00:00
}
2015-06-11 17:33:46 +00:00
TableRef ObjectStore : : table_for_object_type_create_if_needed ( Group * group , const StringData & object_type , bool & created ) {
2015-06-04 01:04:21 +00:00
return group - > get_or_add_table ( table_name_for_object_type ( object_type ) , & created ) ;
2015-06-04 00:27:21 +00:00
}
2015-09-03 17:37:24 +00:00
static inline bool property_has_changed ( Property const & p1 , Property const & p2 ) {
return p1 . type ! = p2 . type
| | p1 . name ! = p2 . name
| | p1 . object_type ! = p2 . object_type
| | p1 . is_nullable ! = p2 . is_nullable ;
2015-07-27 19:42:55 +00:00
}
2015-08-26 22:07:15 +00:00
static bool compare_by_name ( ObjectSchema const & lft , ObjectSchema const & rgt ) {
return lft . name < rgt . name ;
}
2015-09-03 17:37:24 +00:00
void ObjectStore : : verify_schema ( Schema const & actual_schema , Schema & target_schema , bool allow_missing_tables ) {
2015-08-26 22:07:15 +00:00
std : : sort ( begin ( target_schema ) , end ( target_schema ) , compare_by_name ) ;
2015-07-28 18:25:04 +00:00
std : : vector < ObjectSchemaValidationException > errors ;
for ( auto & object_schema : target_schema ) {
2015-09-03 17:37:24 +00:00
auto matching_schema = std : : lower_bound ( begin ( actual_schema ) , end ( actual_schema ) , object_schema , compare_by_name ) ;
if ( matching_schema = = end ( actual_schema ) | | matching_schema - > name ! = object_schema . name ) {
2015-07-28 18:25:04 +00:00
if ( ! allow_missing_tables ) {
2015-08-26 22:07:15 +00:00
errors . emplace_back ( ObjectSchemaValidationException ( object_schema . name ,
" Missing table for object type ' " + object_schema . name + " '. " ) ) ;
2015-07-28 18:25:04 +00:00
}
continue ;
}
2015-09-03 17:37:24 +00:00
auto more_errors = verify_object_schema ( * matching_schema , object_schema , target_schema ) ;
2015-07-28 18:25:04 +00:00
errors . insert ( errors . end ( ) , more_errors . begin ( ) , more_errors . end ( ) ) ;
}
if ( errors . size ( ) ) {
throw SchemaValidationException ( errors ) ;
}
}
2015-09-03 17:37:24 +00:00
std : : vector < ObjectSchemaValidationException > ObjectStore : : verify_object_schema ( ObjectSchema const & table_schema , ObjectSchema & target_schema , Schema & schema ) {
2015-07-27 19:42:55 +00:00
std : : vector < ObjectSchemaValidationException > exceptions ;
2015-06-04 00:27:21 +00:00
2015-08-26 22:07:15 +00:00
ObjectSchema cmp ;
auto schema_contains_table = [ & ] ( std : : string const & name ) {
cmp . name = name ;
return std : : binary_search ( begin ( schema ) , end ( schema ) , cmp , compare_by_name ) ;
} ;
2015-06-04 00:27:21 +00:00
// check to see if properties are the same
2015-09-03 17:37:24 +00:00
const Property * primary = nullptr ;
2015-06-10 21:21:43 +00:00
for ( auto & current_prop : table_schema . properties ) {
2015-06-05 02:24:01 +00:00
auto target_prop = target_schema . property_for_name ( current_prop . name ) ;
2015-06-04 00:27:21 +00:00
2015-06-05 02:24:01 +00:00
if ( ! target_prop ) {
2015-07-27 19:42:55 +00:00
exceptions . emplace_back ( MissingPropertyException ( table_schema . name , current_prop ) ) ;
2015-06-04 00:27:21 +00:00
continue ;
}
2015-07-27 19:42:55 +00:00
if ( property_has_changed ( current_prop , * target_prop ) ) {
exceptions . emplace_back ( MismatchedPropertiesException ( table_schema . name , current_prop , * target_prop ) ) ;
2015-06-04 00:27:21 +00:00
continue ;
}
2015-07-28 18:45:19 +00:00
// check object_type existence
2015-08-26 22:07:15 +00:00
if ( current_prop . object_type . length ( ) & & ! schema_contains_table ( current_prop . object_type ) ) {
2015-07-30 19:22:01 +00:00
exceptions . emplace_back ( MissingObjectTypeException ( table_schema . name , current_prop ) ) ;
2015-07-28 18:45:19 +00:00
}
// check nullablity
2015-07-30 19:22:01 +00:00
if ( current_prop . type = = PropertyTypeObject ) {
if ( ! current_prop . is_nullable ) {
exceptions . emplace_back ( InvalidNullabilityException ( table_schema . name , current_prop ) ) ;
2015-07-28 18:45:19 +00:00
}
}
2015-07-30 19:22:01 +00:00
else {
if ( current_prop . is_nullable ) {
exceptions . emplace_back ( InvalidNullabilityException ( table_schema . name , current_prop ) ) ;
}
2015-07-28 18:45:19 +00:00
}
// check primary keys
if ( current_prop . is_primary ) {
if ( primary ) {
2015-07-30 19:22:01 +00:00
exceptions . emplace_back ( DuplicatePrimaryKeysException ( table_schema . name ) ) ;
2015-07-28 18:45:19 +00:00
}
primary = & current_prop ;
}
// check indexable
if ( current_prop . is_indexed ) {
if ( current_prop . type ! = PropertyTypeString & & current_prop . type ! = PropertyTypeInt ) {
exceptions . emplace_back ( PropertyTypeNotIndexableException ( table_schema . name , current_prop ) ) ;
}
2015-07-28 18:25:04 +00:00
}
2015-06-04 00:27:21 +00:00
// create new property with aligned column
2015-06-05 02:24:01 +00:00
target_prop - > table_column = current_prop . table_column ;
2015-06-04 00:27:21 +00:00
}
2015-06-16 17:07:40 +00:00
// check for change to primary key
if ( table_schema . primary_key ! = target_schema . primary_key ) {
2015-07-27 19:42:55 +00:00
exceptions . emplace_back ( ChangedPrimaryKeyException ( table_schema . name , table_schema . primary_key , target_schema . primary_key ) ) ;
2015-06-16 17:07:40 +00:00
}
2015-06-04 00:27:21 +00:00
// check for new missing properties
2015-06-10 21:21:43 +00:00
for ( auto & target_prop : target_schema . properties ) {
2015-06-05 02:24:01 +00:00
if ( ! table_schema . property_for_name ( target_prop . name ) ) {
2015-07-27 19:42:55 +00:00
exceptions . emplace_back ( ExtraPropertyException ( table_schema . name , target_prop ) ) ;
2015-06-04 00:27:21 +00:00
}
}
2015-06-16 17:07:40 +00:00
return exceptions ;
2015-06-04 00:27:21 +00:00
}
2015-06-05 20:13:08 +00:00
void ObjectStore : : update_column_mapping ( Group * group , ObjectSchema & target_schema ) {
ObjectSchema table_schema ( group , target_schema . name ) ;
2015-06-10 21:21:43 +00:00
for ( auto & target_prop : target_schema . properties ) {
2015-06-05 20:13:08 +00:00
auto table_prop = table_schema . property_for_name ( target_prop . name ) ;
2015-08-03 17:11:43 +00:00
if ( table_prop ) {
// Update target property column to match what's in the realm if it exists
target_prop . table_column = table_prop - > table_column ;
}
2015-06-05 20:13:08 +00:00
}
}
2015-06-04 00:27:21 +00:00
// set references to tables on targetSchema and create/update any missing or out-of-date tables
// if update existing is true, updates existing tables, otherwise validates existing tables
// NOTE: must be called from within write transaction
2015-07-27 19:42:55 +00:00
bool ObjectStore : : create_tables ( Group * group , Schema & target_schema , bool update_existing ) {
2015-06-04 01:04:21 +00:00
bool changed = false ;
2015-06-04 00:27:21 +00:00
// first pass to create missing tables
2015-07-20 23:11:15 +00:00
std : : vector < ObjectSchema * > to_update ;
2015-06-10 21:21:43 +00:00
for ( auto & object_schema : target_schema ) {
2015-06-04 00:27:21 +00:00
bool created = false ;
2015-08-26 22:07:15 +00:00
ObjectStore : : table_for_object_type_create_if_needed ( group , object_schema . name , created ) ;
2015-06-04 00:27:21 +00:00
// we will modify tables for any new objectSchema (table was created) or for all if update_existing is true
if ( update_existing | | created ) {
2015-08-26 22:07:15 +00:00
to_update . push_back ( & object_schema ) ;
2015-06-04 00:27:21 +00:00
changed = true ;
}
}
// second pass adds/removes columns for out of date tables
2015-06-10 21:53:24 +00:00
for ( auto & target_object_schema : to_update ) {
2015-06-06 01:47:19 +00:00
TableRef table = table_for_object_type ( group , target_object_schema - > name ) ;
2015-06-11 17:33:46 +00:00
ObjectSchema current_schema ( group , target_object_schema - > name ) ;
2015-07-20 23:11:15 +00:00
std : : vector < Property > & target_props = target_object_schema - > properties ;
2015-06-04 00:27:21 +00:00
2015-09-03 17:37:24 +00:00
// remove extra columns
size_t deleted = 0 ;
for ( auto & current_prop : current_schema . properties ) {
auto target_prop = target_object_schema - > property_for_name ( current_prop . name ) ;
if ( ! target_prop | | property_has_changed ( current_prop , * target_prop ) ) {
table - > remove_column ( current_prop . table_column - deleted ) ;
+ + deleted ;
current_prop . table_column = npos ;
changed = true ;
}
else {
current_prop . table_column - = deleted ;
}
}
2015-06-04 00:27:21 +00:00
// add missing columns
2015-06-10 21:53:24 +00:00
for ( auto & target_prop : target_props ) {
2015-06-05 02:24:01 +00:00
auto current_prop = current_schema . property_for_name ( target_prop . name ) ;
2015-06-04 00:27:21 +00:00
// add any new properties (new name or different type)
2015-09-03 17:37:24 +00:00
if ( ! current_prop | | current_prop - > table_column = = npos ) {
2015-06-05 02:24:01 +00:00
switch ( target_prop . type ) {
2015-06-04 00:27:21 +00:00
// for objects and arrays, we have to specify target table
case PropertyTypeObject :
case PropertyTypeArray : {
2015-06-11 17:33:46 +00:00
TableRef link_table = ObjectStore : : table_for_object_type ( group , target_prop . object_type ) ;
target_prop . table_column = table - > add_column_link ( DataType ( target_prop . type ) , target_prop . name , * link_table ) ;
2015-06-04 00:27:21 +00:00
break ;
}
default :
2015-06-18 21:46:37 +00:00
target_prop . table_column = table - > add_column ( DataType ( target_prop . type ) , target_prop . name ) ;
2015-06-04 00:27:21 +00:00
break ;
}
2015-04-20 20:16:30 +00:00
2015-06-04 00:27:21 +00:00
changed = true ;
}
2015-09-03 17:37:24 +00:00
else {
target_prop . table_column = current_prop - > table_column ;
2015-06-04 00:27:21 +00:00
}
}
// update table metadata
2015-06-06 01:47:19 +00:00
if ( target_object_schema - > primary_key . length ( ) ) {
2015-06-04 00:27:21 +00:00
// if there is a primary key set, check if it is the same as the old key
2015-06-10 21:21:43 +00:00
if ( current_schema . primary_key ! = target_object_schema - > primary_key ) {
set_primary_key_for_object ( group , target_object_schema - > name , target_object_schema - > primary_key ) ;
2015-06-04 00:27:21 +00:00
changed = true ;
}
}
else if ( current_schema . primary_key . length ( ) ) {
// there is no primary key, so if there was one nil out
2015-06-10 21:21:43 +00:00
set_primary_key_for_object ( group , target_object_schema - > name , " " ) ;
2015-06-04 00:27:21 +00:00
changed = true ;
}
}
return changed ;
}
2015-06-11 17:33:46 +00:00
bool ObjectStore : : is_schema_at_version ( Group * group , uint64_t version ) {
2015-06-04 00:27:21 +00:00
uint64_t old_version = get_schema_version ( group ) ;
2015-06-11 17:33:46 +00:00
if ( old_version > version & & old_version ! = NotVersioned ) {
2015-07-27 19:42:55 +00:00
throw InvalidSchemaVersionException ( old_version , version ) ;
2015-06-04 00:27:21 +00:00
}
2015-06-22 17:32:31 +00:00
return old_version = = version ;
2015-06-04 00:27:21 +00:00
}
2015-09-03 17:37:24 +00:00
bool ObjectStore : : needs_update ( Schema const & old_schema , Schema & schema ) {
2015-09-01 23:21:59 +00:00
for ( auto const & target_schema : schema ) {
2015-09-03 17:37:24 +00:00
auto matching_schema = std : : lower_bound ( begin ( old_schema ) , end ( old_schema ) , target_schema , compare_by_name ) ;
if ( matching_schema = = end ( old_schema ) | | matching_schema - > name ! = target_schema . name ) {
// Table doesn't exist
2015-07-27 19:42:55 +00:00
return true ;
}
2015-09-01 23:21:59 +00:00
2015-09-03 17:37:24 +00:00
if ( matching_schema - > properties . size ( ) ! = target_schema . properties . size ( ) ) {
// If the number of properties don't match then a migration is required
return false ;
}
2015-09-01 23:21:59 +00:00
// Check that all of the property indexes are up to date
2015-09-03 17:37:24 +00:00
for ( size_t i = 0 , count = target_schema . properties . size ( ) ; i < count ; + + i ) {
if ( target_schema . properties [ i ] . is_indexed ! = matching_schema - > properties [ i ] . is_indexed ) {
2015-09-01 23:21:59 +00:00
return true ;
}
}
2015-07-27 19:42:55 +00:00
}
2015-09-01 23:21:59 +00:00
2015-07-27 19:42:55 +00:00
return false ;
}
2015-06-04 00:27:21 +00:00
2015-09-03 17:37:24 +00:00
bool ObjectStore : : update_realm_with_schema ( Group * group , Schema const & old_schema ,
uint64_t version , Schema & schema ,
2015-06-04 00:27:21 +00:00
MigrationFunction migration ) {
// Recheck the schema version after beginning the write transaction as
// another process may have done the migration after we opened the read
// transaction
2015-06-22 17:32:31 +00:00
bool migrating = ! is_schema_at_version ( group , version ) ;
2015-06-04 00:27:21 +00:00
// create tables
2015-06-06 01:47:19 +00:00
bool changed = create_metadata_tables ( group ) ;
2015-06-10 21:21:43 +00:00
changed = create_tables ( group , schema , migrating ) | | changed ;
2015-06-04 00:27:21 +00:00
2015-09-03 17:37:24 +00:00
if ( ! migrating ) {
// If we aren't migrating, then verify that all of the tables which
// were already present are valid (newly created ones always are)
verify_schema ( old_schema , schema , true ) ;
}
2015-06-04 00:27:21 +00:00
2015-06-10 21:21:43 +00:00
changed = update_indexes ( group , schema ) | | changed ;
2015-06-05 00:44:06 +00:00
2015-06-04 00:27:21 +00:00
if ( ! migrating ) {
return changed ;
}
// apply the migration block if provided and there's any old data
2015-06-11 17:33:46 +00:00
if ( get_schema_version ( group ) ! = ObjectStore : : NotVersioned ) {
2015-06-06 01:47:19 +00:00
migration ( group , schema ) ;
2015-06-04 00:27:21 +00:00
2015-09-02 16:31:06 +00:00
validate_primary_column_uniqueness ( group , schema ) ;
}
2015-06-05 01:14:53 +00:00
2015-06-04 00:27:21 +00:00
set_schema_version ( group , version ) ;
return true ;
}
2015-07-27 19:42:55 +00:00
Schema ObjectStore : : schema_from_group ( Group * group ) {
Schema schema ;
2015-06-05 02:24:01 +00:00
for ( size_t i = 0 ; i < group - > size ( ) ; i + + ) {
2015-07-20 23:11:15 +00:00
std : : string object_type = object_type_for_table_name ( group - > get_table_name ( i ) ) ;
2015-06-04 02:14:39 +00:00
if ( object_type . length ( ) ) {
2015-08-26 22:07:15 +00:00
schema . emplace_back ( group , object_type ) ;
2015-06-04 02:14:39 +00:00
}
}
2015-09-03 17:37:24 +00:00
std : : sort ( begin ( schema ) , end ( schema ) , compare_by_name ) ;
2015-06-04 02:14:39 +00:00
return schema ;
}
2015-06-05 00:44:06 +00:00
bool ObjectStore : : update_indexes ( Group * group , Schema & schema ) {
bool changed = false ;
2015-06-10 21:21:43 +00:00
for ( auto & object_schema : schema ) {
2015-08-26 22:07:15 +00:00
TableRef table = table_for_object_type ( group , object_schema . name ) ;
2015-06-05 00:44:06 +00:00
if ( ! table ) {
continue ;
}
2015-08-26 22:07:15 +00:00
for ( auto & property : object_schema . properties ) {
2015-06-05 01:14:53 +00:00
if ( property . requires_index ( ) = = table - > has_search_index ( property . table_column ) ) {
2015-06-05 00:44:06 +00:00
continue ;
}
changed = true ;
2015-06-05 01:14:53 +00:00
if ( property . requires_index ( ) ) {
2015-06-05 00:44:06 +00:00
try {
table - > add_search_index ( property . table_column ) ;
}
2015-06-11 17:33:46 +00:00
catch ( LogicError const & ) {
2015-08-26 22:07:15 +00:00
throw PropertyTypeNotIndexableException ( object_schema . name , property ) ;
2015-06-05 00:44:06 +00:00
}
}
else {
table - > remove_search_index ( property . table_column ) ;
}
}
}
return changed ;
}
2015-06-05 01:14:53 +00:00
void ObjectStore : : validate_primary_column_uniqueness ( Group * group , Schema & schema ) {
2015-06-10 21:21:43 +00:00
for ( auto & object_schema : schema ) {
2015-08-26 22:07:15 +00:00
auto primary_prop = object_schema . primary_key_property ( ) ;
2015-06-05 02:24:01 +00:00
if ( ! primary_prop ) {
2015-06-05 01:14:53 +00:00
continue ;
}
2015-08-26 22:07:15 +00:00
TableRef table = table_for_object_type ( group , object_schema . name ) ;
2015-06-05 01:14:53 +00:00
if ( table - > get_distinct_view ( primary_prop - > table_column ) . size ( ) ! = table - > size ( ) ) {
2015-08-26 22:07:15 +00:00
throw DuplicatePrimaryKeyValueException ( object_schema . name , * primary_prop ) ;
2015-06-05 01:14:53 +00:00
}
}
2015-06-06 01:47:19 +00:00
}
2015-06-11 19:17:55 +00:00
void ObjectStore : : delete_data_for_object ( Group * group , const StringData & object_type ) {
TableRef table = table_for_object_type ( group , object_type ) ;
if ( table ) {
group - > remove_table ( table - > get_index_in_group ( ) ) ;
set_primary_key_for_object ( group , object_type , " " ) ;
}
}
2015-07-27 19:42:55 +00:00
InvalidSchemaVersionException : : InvalidSchemaVersionException ( uint64_t old_version , uint64_t new_version ) :
m_old_version ( old_version ) , m_new_version ( new_version )
{
m_what = " Provided schema version " + std : : to_string ( old_version ) + " is less than last set version " + std : : to_string ( new_version ) + " . " ;
}
2015-09-03 17:37:24 +00:00
DuplicatePrimaryKeyValueException : : DuplicatePrimaryKeyValueException ( std : : string object_type , Property const & property ) :
2015-07-27 19:42:55 +00:00
m_object_type ( object_type ) , m_property ( property )
{
m_what = " Primary key property ' " + property . name + " ' has duplicate values after migration. " ;
} ;
2015-07-28 18:25:04 +00:00
SchemaValidationException : : SchemaValidationException ( std : : vector < ObjectSchemaValidationException > errors ) :
m_validation_errors ( errors )
2015-07-27 19:42:55 +00:00
{
2015-07-28 18:25:04 +00:00
m_what = " Migration is required due to the following errors: " ;
2015-07-27 19:42:55 +00:00
for ( auto error : errors ) {
m_what + = std : : string ( " \n - " ) + error . what ( ) ;
}
}
2015-09-03 17:37:24 +00:00
PropertyTypeNotIndexableException : : PropertyTypeNotIndexableException ( std : : string object_type , Property const & property ) :
2015-07-28 18:45:19 +00:00
ObjectSchemaPropertyException ( object_type , property )
2015-07-27 19:42:55 +00:00
{
m_what = " Can't index property " + object_type + " . " + property . name + " : indexing a property of type ' " + string_for_property_type ( property . type ) + " ' is currently not supported " ;
}
2015-09-03 17:37:24 +00:00
ExtraPropertyException : : ExtraPropertyException ( std : : string object_type , Property const & property ) :
2015-07-28 18:45:19 +00:00
ObjectSchemaPropertyException ( object_type , property )
2015-07-27 19:42:55 +00:00
{
m_what = " Property ' " + property . name + " ' has been added to latest object model. " ;
}
2015-09-03 17:37:24 +00:00
MissingPropertyException : : MissingPropertyException ( std : : string object_type , Property const & property ) :
2015-07-28 18:45:19 +00:00
ObjectSchemaPropertyException ( object_type , property )
2015-07-27 19:42:55 +00:00
{
m_what = " Property ' " + property . name + " ' is missing from latest object model. " ;
}
2015-09-03 17:37:24 +00:00
InvalidNullabilityException : : InvalidNullabilityException ( std : : string object_type , Property const & property ) :
2015-07-30 19:22:01 +00:00
ObjectSchemaPropertyException ( object_type , property )
{
if ( property . type = = PropertyTypeObject ) {
if ( ! property . is_nullable ) {
m_what = " 'Object' property ' " + property . name + " ' must be nullable. " ;
}
}
else {
if ( property . is_nullable ) {
m_what = " Only 'Object' property types are nullable " ;
}
}
}
2015-09-03 17:37:24 +00:00
MissingObjectTypeException : : MissingObjectTypeException ( std : : string object_type , Property const & property ) :
2015-07-30 19:22:01 +00:00
ObjectSchemaPropertyException ( object_type , property )
{
m_what = " Target type ' " + property . object_type + " ' doesn't exist for property ' " + property . name + " '. " ;
}
2015-09-03 17:37:24 +00:00
MismatchedPropertiesException : : MismatchedPropertiesException ( std : : string object_type , Property const & old_property , Property const & new_property ) :
2015-07-27 19:42:55 +00:00
ObjectSchemaValidationException ( object_type ) , m_old_property ( old_property ) , m_new_property ( new_property )
{
if ( new_property . type ! = old_property . type ) {
m_what = " Property types for ' " + old_property . name + " ' property do not match. Old type ' " + string_for_property_type ( old_property . type ) +
" ', new type ' " + string_for_property_type ( new_property . type ) + " ' " ;
}
else if ( new_property . object_type ! = old_property . object_type ) {
m_what = " Target object type for property ' " + old_property . name + " ' do not match. Old type ' " + old_property . object_type + " ', new type ' " + new_property . object_type + " ' " ;
}
else if ( new_property . is_nullable ! = old_property . is_nullable ) {
m_what = " Nullability for property ' " + old_property . name + " ' has changed from ' " + std : : to_string ( old_property . is_nullable ) + " ' to ' " + std : : to_string ( new_property . is_nullable ) + " '. " ;
}
}
ChangedPrimaryKeyException : : ChangedPrimaryKeyException ( std : : string object_type , std : : string old_primary , std : : string new_primary ) : ObjectSchemaValidationException ( object_type ) , m_old_primary ( old_primary ) , m_new_primary ( new_primary )
{
if ( old_primary . size ( ) ) {
m_what = " Property ' " + old_primary + " ' is no longer a primary key. " ;
}
else {
m_what = " Property ' " + new_primary + " ' has been made a primary key. " ;
}
}
InvalidPrimaryKeyException : : InvalidPrimaryKeyException ( std : : string object_type , std : : string primary ) :
2015-07-28 18:45:19 +00:00
ObjectSchemaValidationException ( object_type ) , m_primary_key ( primary )
2015-07-27 19:42:55 +00:00
{
m_what = " Specified primary key property ' " + primary + " ' does not exist. " ;
}
2015-07-30 19:22:01 +00:00
DuplicatePrimaryKeysException : : DuplicatePrimaryKeysException ( std : : string object_type ) : ObjectSchemaValidationException ( object_type )
{
m_what = " Duplicate primary keys for object ' " + object_type + " '. " ;
}