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-09-04 21:03:58 +00:00
# include "schema.hpp"
2015-06-10 21:21:43 +00:00
# include <realm/group.hpp>
2015-06-05 01:14:53 +00:00
# include <realm/link_view.hpp>
2015-09-04 16:53:51 +00:00
# include <realm/table.hpp>
2015-06-05 01:14:53 +00:00
# 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-09-04 16:40:29 +00:00
bool ObjectStore : : has_metadata_tables ( const 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-09-04 16:40:29 +00:00
uint64_t ObjectStore : : get_schema_version ( const Group * group ) {
ConstTableRef 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-09-04 16:40:29 +00:00
StringData ObjectStore : : get_primary_key_for_object ( const Group * group , StringData object_type ) {
ConstTableRef 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-09-04 16:40:29 +00:00
ConstTableRef ObjectStore : : table_for_object_type ( const Group * group , StringData object_type ) {
return group - > get_table ( table_name_for_object_type ( object_type ) ) ;
}
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-10-20 01:52:56 +00:00
static inline bool property_can_be_migrated_to_nullable ( const Property & old_property , const Property & new_property ) {
return old_property . type = = new_property . type
& & ! old_property . is_nullable
& & new_property . is_nullable
& & new_property . name = = old_property . name ;
}
2015-09-04 16:40:29 +00:00
void ObjectStore : : verify_schema ( Schema const & actual_schema , Schema & target_schema , bool allow_missing_tables ) {
2015-07-28 18:25:04 +00:00
std : : vector < ObjectSchemaValidationException > errors ;
for ( auto & object_schema : target_schema ) {
2015-09-04 21:03:58 +00:00
auto matching_schema = actual_schema . find ( object_schema ) ;
if ( matching_schema = = actual_schema . end ( ) ) {
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-04 21:03:58 +00:00
auto more_errors = verify_object_schema ( * matching_schema , object_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-04 16:40:29 +00:00
std : : vector < ObjectSchemaValidationException > ObjectStore : : verify_object_schema ( ObjectSchema const & table_schema ,
2015-09-04 21:03:58 +00:00
ObjectSchema & target_schema ) {
2015-07-27 19:42:55 +00:00
std : : vector < ObjectSchemaValidationException > exceptions ;
2015-06-04 00:27:21 +00:00
// check to see if properties are the same
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
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-10-20 01:52:56 +00:00
template < typename T >
static void copy_property_values ( const Property & old_property , const Property & new_property , Table & table ,
T ( Table : : * getter ) ( std : : size_t , std : : size_t ) const noexcept ,
void ( Table : : * setter ) ( std : : size_t , std : : size_t , T ) ) {
size_t old_column = old_property . table_column , new_column = new_property . table_column ;
size_t count = table . size ( ) ;
for ( size_t i = 0 ; i < count ; i + + ) {
( table . * setter ) ( new_column , i , ( table . * getter ) ( old_column , i ) ) ;
}
}
static void copy_property_values ( const Property & source , const Property & destination , Table & table ) {
switch ( destination . type ) {
case PropertyTypeInt :
copy_property_values ( source , destination , table , & Table : : get_int , & Table : : set_int ) ;
break ;
case PropertyTypeBool :
copy_property_values ( source , destination , table , & Table : : get_bool , & Table : : set_bool ) ;
break ;
case PropertyTypeFloat :
copy_property_values ( source , destination , table , & Table : : get_float , & Table : : set_float ) ;
break ;
case PropertyTypeDouble :
copy_property_values ( source , destination , table , & Table : : get_double , & Table : : set_double ) ;
break ;
case PropertyTypeString :
copy_property_values ( source , destination , table , & Table : : get_string , & Table : : set_string ) ;
break ;
case PropertyTypeData :
copy_property_values ( source , destination , table , & Table : : get_binary , & Table : : set_binary ) ;
break ;
case PropertyTypeDate :
copy_property_values ( source , destination , table , & Table : : get_datetime , & Table : : set_datetime ) ;
break ;
default :
break ;
}
}
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-10-20 01:52:56 +00:00
// handle columns changing from required to optional
for ( auto & current_prop : current_schema . properties ) {
auto target_prop = target_object_schema - > property_for_name ( current_prop . name ) ;
if ( ! target_prop | | ! property_can_be_migrated_to_nullable ( current_prop , * target_prop ) )
continue ;
target_prop - > table_column = current_prop . table_column ;
current_prop . table_column = current_prop . table_column + 1 ;
table - > insert_column ( target_prop - > table_column , DataType ( target_prop - > type ) , target_prop - > name , target_prop - > is_nullable ) ;
copy_property_values ( current_prop , * target_prop , * table ) ;
table - > remove_column ( current_prop . table_column ) ;
current_prop . table_column = target_prop - > table_column ;
changed = true ;
}
2015-10-20 01:53:09 +00:00
bool inserted_placeholder_column = false ;
2015-09-03 17:37:24 +00:00
// remove extra columns
size_t deleted = 0 ;
for ( auto & current_prop : current_schema . properties ) {
2015-09-04 21:24:11 +00:00
current_prop . table_column - = deleted ;
2015-09-03 17:37:24 +00:00
auto target_prop = target_object_schema - > property_for_name ( current_prop . name ) ;
2015-10-20 01:52:56 +00:00
if ( ! target_prop | | ( property_has_changed ( current_prop , * target_prop )
& & ! property_can_be_migrated_to_nullable ( current_prop , * target_prop ) ) ) {
2015-10-20 01:53:09 +00:00
if ( deleted = = current_schema . properties . size ( ) - 1 ) {
// We're about to remove the last column from the table. Insert a placeholder column to preserve
// the number of rows in the table for the addition of new columns below.
table - > add_column ( type_Bool , " placeholder " ) ;
inserted_placeholder_column = true ;
}
2015-09-04 21:24:11 +00:00
table - > remove_column ( current_prop . table_column ) ;
2015-09-03 17:37:24 +00:00
+ + deleted ;
current_prop . table_column = npos ;
changed = true ;
}
}
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
2015-09-04 17:10:18 +00:00
// add any new properties (no old column or old column was removed due to not matching)
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-09-03 21:48:24 +00:00
target_prop . table_column = table - > add_column ( DataType ( target_prop . type ) ,
target_prop . name ,
target_prop . is_nullable ) ;
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
}
}
2015-10-20 01:53:09 +00:00
if ( inserted_placeholder_column ) {
// We inserted a placeholder due to removing all columns from the table. Remove it, and update the indices
// of any columns that we inserted after it.
table - > remove_column ( 0 ) ;
for ( auto & target_prop : target_props ) {
target_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-09-04 16:40:29 +00:00
bool ObjectStore : : is_schema_at_version ( const 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-04 16:40:29 +00:00
bool ObjectStore : : needs_update ( Schema const & old_schema , Schema const & schema ) {
2015-09-01 23:21:59 +00:00
for ( auto const & target_schema : schema ) {
2015-09-04 21:03:58 +00:00
auto matching_schema = old_schema . find ( target_schema ) ;
if ( matching_schema = = end ( old_schema ) ) {
2015-09-03 17:37:24 +00:00
// 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-09-04 16:40:29 +00:00
Schema ObjectStore : : schema_from_group ( const Group * group ) {
2015-09-04 21:03:58 +00:00
std : : vector < ObjectSchema > 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
}
}
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-09-04 16:40:29 +00:00
void ObjectStore : : validate_primary_column_uniqueness ( const Group * group , Schema const & 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-09-04 16:40:29 +00:00
ConstTableRef 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-04 16:53:51 +00:00
DuplicatePrimaryKeyValueException : : DuplicatePrimaryKeyValueException ( std : : string const & 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-09-04 16:53:51 +00:00
SchemaValidationException : : SchemaValidationException ( std : : vector < ObjectSchemaValidationException > const & errors ) :
2015-07-28 18:25:04 +00:00
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-09-04 16:53:51 +00:00
for ( auto const & error : errors ) {
2015-07-27 19:42:55 +00:00
m_what + = std : : string ( " \n - " ) + error . what ( ) ;
}
}
2015-09-04 16:53:51 +00:00
PropertyTypeNotIndexableException : : PropertyTypeNotIndexableException ( std : : string const & 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-04 16:53:51 +00:00
ExtraPropertyException : : ExtraPropertyException ( std : : string const & 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-04 16:53:51 +00:00
MissingPropertyException : : MissingPropertyException ( std : : string const & 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-04 16:53:51 +00:00
InvalidNullabilityException : : InvalidNullabilityException ( std : : string const & object_type , Property const & property ) :
2015-07-30 19:22:01 +00:00
ObjectSchemaPropertyException ( object_type , property )
{
if ( property . type = = PropertyTypeObject ) {
2015-09-04 17:02:29 +00:00
m_what = " 'Object' property ' " + property . name + " ' must be nullable. " ;
2015-07-30 19:22:01 +00:00
}
else {
2015-09-04 17:02:29 +00:00
m_what = " Array or Mixed property ' " + property . name + " ' cannot be nullable " ;
}
2015-07-30 19:22:01 +00:00
}
2015-09-04 16:53:51 +00:00
MissingObjectTypeException : : MissingObjectTypeException ( std : : string const & 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-04 16:53:51 +00:00
MismatchedPropertiesException : : MismatchedPropertiesException ( std : : string const & 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 ) + " '. " ;
}
}
2015-09-04 16:53:51 +00:00
ChangedPrimaryKeyException : : ChangedPrimaryKeyException ( std : : string const & object_type , std : : string const & old_primary , std : : string const & new_primary ) : ObjectSchemaValidationException ( object_type ) , m_old_primary ( old_primary ) , m_new_primary ( new_primary )
2015-07-27 19:42:55 +00:00
{
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. " ;
}
}
2015-09-04 16:53:51 +00:00
InvalidPrimaryKeyException : : InvalidPrimaryKeyException ( std : : string const & object_type , std : : string const & 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
2015-09-04 16:53:51 +00:00
DuplicatePrimaryKeysException : : DuplicatePrimaryKeysException ( std : : string const & object_type ) : ObjectSchemaValidationException ( object_type )
2015-07-30 19:22:01 +00:00
{
m_what = " Duplicate primary keys for object ' " + object_type + " '. " ;
}