PrimitiveListNotifier::PrimitiveListNotifier(TableRef table, std::shared_ptr<Realm> realm) : CollectionNotifier(std::move(realm)) , m_prev_size(table->size()) { set_table(*table->get_parent_table()); m_table_handover = source_shared_group().export_table_for_handover(table); }
void ObjectStore::delete_data_for_object(Group *group, 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, ""); } }
void Permissions::get_permissions(std::shared_ptr<SyncUser> user, PermissionResultsCallback callback, const ConfigMaker& make_config) { auto realm = Permissions::permission_realm(user, make_config); auto table = ObjectStore::table_for_object_type(realm->read_group(), "Permission"); auto results = std::make_shared<NotificationWrapper<Results>>(std::move(realm), *table); // `get_permissions` works by temporarily adding an async notifier to the permission Realm. // This notifier will run the `async` callback until the Realm contains permissions or // an error happens. When either of these two things happen, the notifier will be // unregistered by nulling out the `results_wrapper` container. auto async = [results, callback=std::move(callback)](CollectionChangeSet, std::exception_ptr ex) mutable { if (ex) { callback(Results(), ex); results.reset(); return; } if (results->size() > 0) { // We monitor the raw results. The presence of a `__management` Realm indicates // that the permissions have been downloaded (hence, we wait until size > 0). TableRef table = ObjectStore::table_for_object_type(results->get_realm()->read_group(), "Permission"); size_t col_idx = table->get_descriptor()->get_column_index("path"); auto query = !(table->column<StringData>(col_idx).ends_with("/__permission") || table->column<StringData>(col_idx).ends_with("/__management")); // Call the callback with our new permissions object. This object will exclude the // private Realms. callback(results->filter(std::move(query)), nullptr); results.reset(); } }; results->add_notification_callback(std::move(async)); }
bool ObjectStore::update_indexes(Group *group, Schema &schema) { bool changed = false; for (auto& object_schema : schema) { TableRef table = table_for_object_type(group, object_schema.name); if (!table) { continue; } for (auto& property : object_schema.properties) { if (property.requires_index() == table->has_search_index(property.table_column)) { continue; } changed = true; if (property.requires_index()) { try { table->add_search_index(property.table_column); } catch (LogicError const&) { throw ObjectStoreException(ObjectStoreException::Kind::RealmPropertyTypeNotIndexable, { {"object_type", object_schema.name}, {"property_name", property.name}, {"property_type", string_for_property_type(property.type)} }); } } else { table->remove_search_index(property.table_column); } } } return changed; }
SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const { SharedRealm realm = Realm::get_shared_realm(get_configuration()); TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); Results results(realm, table->where()); return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema); }
bool ObjectStore::update_indexes(Group *group, Schema &schema) { bool changed = false; for (auto& object_schema : schema) { TableRef table = table_for_object_type(group, object_schema.name); if (!table) { continue; } for (auto& property : object_schema.persisted_properties) { if (property.requires_index() == table->has_search_index(property.table_column)) { continue; } changed = true; if (property.requires_index()) { try { table->add_search_index(property.table_column); } catch (LogicError const&) { throw PropertyTypeNotIndexableException(object_schema.name, property); } } else { table->remove_search_index(property.table_column); } } } return changed; }
ValueType Object::get_property_value_impl(ContextType& ctx, const Property &property) { verify_attached(); size_t column = property.table_column; if (is_nullable(property.type) && m_row.is_null(column)) return ctx.null_value(); if (is_array(property.type) && property.type != PropertyType::LinkingObjects) return ctx.box(List(m_realm, *m_row.get_table(), column, m_row.get_index())); switch (property.type & ~PropertyType::Flags) { case PropertyType::Bool: return ctx.box(m_row.get_bool(column)); case PropertyType::Int: return ctx.box(m_row.get_int(column)); case PropertyType::Float: return ctx.box(m_row.get_float(column)); case PropertyType::Double: return ctx.box(m_row.get_double(column)); case PropertyType::String: return ctx.box(m_row.get_string(column)); case PropertyType::Data: return ctx.box(m_row.get_binary(column)); case PropertyType::Date: return ctx.box(m_row.get_timestamp(column)); case PropertyType::Any: return ctx.box(m_row.get_mixed(column)); case PropertyType::Object: { auto linkObjectSchema = m_realm->schema().find(property.object_type); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), property.object_type); return ctx.box(Object(m_realm, *linkObjectSchema, table->get(m_row.get_link(column)))); } case PropertyType::LinkingObjects: { auto target_object_schema = m_realm->schema().find(property.object_type); auto link_property = target_object_schema->property_for_name(property.link_origin_property_name); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), target_object_schema->name); auto tv = m_row.get_table()->get_backlink_view(m_row.get_index(), table.get(), link_property->table_column); return ctx.box(Results(m_realm, std::move(tv))); } default: REALM_UNREACHABLE(); } }
ObjectSchema::ObjectSchema(Group *group, const std::string &name) : name(name) { TableRef tableRef = ObjectStore::table_for_object_type(group, name); Table *table = tableRef.get(); size_t count = table->get_column_count(); properties.reserve(count); for (size_t col = 0; col < count; col++) { Property property; property.name = table->get_column_name(col).data(); property.type = (PropertyType)table->get_column_type(col); property.is_indexed = table->has_search_index(col); property.is_primary = false; property.table_column = col; if (property.type == PropertyTypeObject || property.type == PropertyTypeArray) { // set link type for objects and arrays realm::TableRef linkTable = table->get_link_target(col); property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); } properties.push_back(move(property)); } primary_key = realm::ObjectStore::get_primary_key_for_object(group, name); if (primary_key.length()) { auto primary_key_prop = primary_key_property(); if (!primary_key_prop) { throw ObjectStoreValidationException({"No property matching primary key '" + primary_key + "'"}, name); } primary_key_prop->is_primary = true; } }
uint64_t ObjectStore::get_schema_version(Group *group) { TableRef table = group->get_table(c_metadataTableName); if (!table || table->get_column_count() == 0) { return ObjectStore::NotVersioned; } return table->get_int(c_versionColumnIndex, c_zeroRowIndex); }
void SyncUserMetadata::remove() { m_invalid = true; m_realm->begin_transaction(); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); table->move_last_over(m_row.get_index()); m_realm->commit_transaction(); m_realm = nullptr; }
void SyncFileActionMetadata::remove() { REALM_ASSERT(m_realm); m_realm->verify_thread(); m_realm->begin_transaction(); TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); table->move_last_over(m_row.get_index()); m_realm->commit_transaction(); m_realm = nullptr; }
SyncFileActionMetadata::SyncFileActionMetadata(const SyncMetadataManager& manager, Action action, const std::string& original_name, const std::string& url, const std::string& user_identity, util::Optional<std::string> new_name) : m_schema(manager.m_file_action_schema) { size_t raw_action = static_cast<size_t>(action); // Open the Realm. m_realm = Realm::get_shared_realm(manager.get_configuration()); // Retrieve or create the row for this object. TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata); m_realm->begin_transaction(); size_t row_idx = table->find_first_string(m_schema.idx_original_name, original_name); if (row_idx == not_found) { row_idx = table->add_empty_row(); table->set_string(m_schema.idx_original_name, row_idx, original_name); } table->set_string(m_schema.idx_new_name, row_idx, new_name); table->set_int(m_schema.idx_action, row_idx, raw_action); table->set_string(m_schema.idx_url, row_idx, url); table->set_string(m_schema.idx_user_identity, row_idx, user_identity); m_realm->commit_transaction(); m_row = table->get(row_idx); }
StringData ObjectStore::get_primary_key_for_object(Group *group, StringData object_type) { TableRef table = group->get_table(c_primaryKeyTableName); if (!table) { return ""; } size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); if (row == not_found) { return ""; } return table->get_string(c_primaryKeyPropertyNameColumnIndex, row); }
util::Optional<SyncFileActionMetadata> SyncFileActionMetadata::metadata_for_path(const std::string& original_name, const SyncMetadataManager& manager) { auto realm = Realm::get_shared_realm(manager.get_configuration()); auto schema = manager.m_file_action_schema; TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata); size_t row_idx = table->find_first_string(schema.idx_original_name, original_name); if (row_idx == not_found) { return none; } return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx)); }
SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const { // Open the Realm. SharedRealm realm = Realm::get_shared_realm(get_configuration()); TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked); Results results(realm, std::move(query)); return SyncUserMetadataResults(std::move(results), std::move(realm), m_user_schema); }
void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) { TableRef table = group.get_table(c_primaryKeyTableName); size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); #if REALM_ENABLE_SYNC // sync::create_table* functions should have already updated the pk table. if (sync::has_object_ids(group)) { if (primary_key.size() == 0) REALM_ASSERT(row == not_found); else { REALM_ASSERT(row != not_found); REALM_ASSERT(table->get_string(c_primaryKeyPropertyNameColumnIndex, row) == primary_key); } return; } #endif // REALM_ENABLE_SYNC if (row == not_found && primary_key.size()) { row = table->add_empty_row(); table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type); table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); return; } // set if changing, or remove if setting to nil if (primary_key.size() == 0) { if (row != not_found) { table->move_last_over(row); } } else { table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); } }
void ObjectStore::validate_primary_column_uniqueness(Group *group, Schema &schema) { for (auto& object_schema : schema) { auto primary_prop = object_schema.primary_key_property(); if (!primary_prop) { continue; } TableRef table = table_for_object_type(group, object_schema.name); if (table->get_distinct_view(primary_prop->table_column).size() != table->size()) { throw ObjectStoreException(ObjectStoreException::Kind::RealmDuplicatePrimaryKeyValue, {{"object_type", object_schema.name}, {"property_name", primary_prop->name}}); } } }
void ObjectStore::rename_property(Group *group, Schema& passed_schema, StringData object_type, StringData old_name, StringData new_name) { TableRef table = table_for_object_type(group, object_type); if (!table) { throw PropertyRenameMissingNewObjectTypeException(object_type); } ObjectSchema matching_schema(group, object_type); Property *old_property = matching_schema.property_for_name(old_name); if (old_property == nullptr) { throw PropertyRenameMissingOldPropertyException(old_name, new_name); } auto passed_object_schema = passed_schema.find(object_type); if (passed_object_schema == passed_schema.end()) { throw PropertyRenameMissingNewObjectTypeException(object_type); } Property *new_property = matching_schema.property_for_name(new_name); if (new_property == nullptr) { // new property not in new schema, which means we're probably renaming // to an intermediate property in a multi-version migration. // this is safe because the migration will fail schema validation unless // this property is renamed again. new_property = matching_schema.property_for_name(old_name); new_property->name = new_name; table->rename_column(old_property->table_column, new_name); return; } if (old_property->type != new_property->type || old_property->object_type != new_property->object_type) { throw PropertyRenameTypeMismatchException(*old_property, *new_property); } if (passed_object_schema->property_for_name(old_name) != nullptr) { throw PropertyRenameOldStillExistsException(old_name, new_name); } size_t column_to_remove = new_property->table_column; table->rename_column(old_property->table_column, new_name); table->remove_column(column_to_remove); // update table_column for each property since it may have shifted for (auto& current_prop : passed_object_schema->persisted_properties) { auto target_prop = matching_schema.property_for_name(current_prop.name); current_prop.table_column = target_prop->table_column; } // update index for new column if (new_property->requires_index() && !old_property->requires_index()) { table->add_search_index(old_property->table_column); } else if (!new_property->requires_index() && old_property->requires_index()) { table->remove_search_index(old_property->table_column); } old_property->name = new_name; // update nullability for column if (property_can_be_migrated_to_nullable(*old_property, *new_property)) { new_property->table_column = old_property->table_column; old_property->table_column = old_property->table_column + 1; table->insert_column(new_property->table_column, DataType(new_property->type), new_property->name, new_property->is_nullable); copy_property_values(*old_property, *new_property, *table); table->remove_column(old_property->table_column); old_property->table_column = new_property->table_column; } }
bool ObjectStore::indexes_are_up_to_date(Group *group, Schema &schema) { for (auto &object_schema : schema) { TableRef table = table_for_object_type(group, object_schema.name); if (!table) { continue; } update_column_mapping(group, object_schema); for (auto& property : object_schema.properties) { if (property.requires_index() != table->has_search_index(property.table_column)) { return false; } } } return true; }
static void copy_property_to_property(Property &old_property, Property &new_property, TableRef table) { 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++) { T old_value = get_value<T>(table, i, old_column); set_value(table, i, new_column, old_value); } }
void Stack::pushTable(const TableRef &value) { const int ref = value.getRef(); if (ref != LUA_REFNIL) { lua_getref(&_luaState, ref); } else { lua_pushnil(&_luaState); } }
inline void Descriptor::attach(Table* table, DescriptorRef parent, Spec* spec) noexcept { REALM_ASSERT(!is_attached()); REALM_ASSERT(!table->has_shared_type()); m_root_table.reset(table); m_parent = parent; m_spec = spec; }
void ObjectStore::remove_properties(Group *group, Schema &target_schema, std::vector<std::pair<std::string, Property>> to_delete) { for (auto& target_object_schema : target_schema) { TableRef table = table_for_object_type(group, target_object_schema.name); ObjectSchema current_schema(group, target_object_schema.name); size_t deleted = 0; for (auto& current_prop : current_schema.persisted_properties) { current_prop.table_column -= deleted; for (auto& single_to_delete : to_delete) { if (target_object_schema.name == single_to_delete.first && current_prop.name == single_to_delete.second.name && current_prop.object_type == single_to_delete.second.object_type) { table->remove_column(current_prop.table_column); ++deleted; current_prop.table_column = npos; } } } } }
void ObjectStore::create_metadata_tables(Group *group) { TableRef table = group->get_or_add_table(c_primaryKeyTableName); if (table->get_column_count() == 0) { table->add_column(type_String, c_primaryKeyObjectClassColumnName); table->add_column(type_String, c_primaryKeyPropertyNameColumnName); } table = group->get_or_add_table(c_metadataTableName); if (table->get_column_count() == 0) { table->add_column(type_Int, c_versionColumnName); // set initial version table->add_empty_row(); table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned); } }
Variables ScriptManager::callFunction(const Common::UString &name, const Variables ¶ms) { assert(!name.empty()); assert(_luaState && _regNestingLevel == 0); std::vector<Common::UString> parts; Common::UString::split(name, '.', parts); if (parts.empty()) { error("Lua call \"%s\" failed: bad name", name.c_str()); return Variables(); } const Common::UString funcName = parts.back(); parts.pop_back(); if (parts.empty()) { return getGlobalFunction(funcName).call(params); } TableRef table = getGlobalTable(parts[0]); for (uint32 i = 1; i < parts.size(); ++i) { table = table.getTableAt(parts[i]); } return table.getFunctionAt(funcName).call(params); }
REALM_EXPORT Results* object_get_backlinks(Object& object, size_t property_ndx, NativeException::Marshallable& ex) { return handle_errors(ex, [&] { verify_can_get(object); const Property& prop = object.get_object_schema().computed_properties[property_ndx]; REALM_ASSERT_DEBUG(prop.type == PropertyType::LinkingObjects); const ObjectSchema& relationship = *object.realm()->schema().find(prop.object_type); const TableRef table = ObjectStore::table_for_object_type(object.realm()->read_group(), relationship.name); const Property& link = *relationship.property_for_name(prop.link_origin_property_name); TableView backlink_view = object.row().get_table()->get_backlink_view(object.row().get_index(), table.get(), link.table_column); return new Results(object.realm(), backlink_view); }); }
SyncUserMetadata::SyncUserMetadata(const SyncMetadataManager& manager, std::string identity, bool make_if_absent) : m_schema(manager.m_user_schema) { // Open the Realm. m_realm = Realm::get_shared_realm(manager.get_configuration()); // Retrieve or create the row for this object. TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); size_t row_idx = table->find_first_string(m_schema.idx_identity, identity); if (row_idx == not_found) { if (!make_if_absent) { m_invalid = true; m_realm = nullptr; return; } m_realm->begin_transaction(); row_idx = table->find_first_string(m_schema.idx_identity, identity); if (row_idx == not_found) { row_idx = table->add_empty_row(); table->set_string(m_schema.idx_identity, row_idx, identity); table->set_bool(m_schema.idx_user_is_admin, row_idx, false); m_realm->commit_transaction(); } else { // Someone beat us to adding this user. m_realm->cancel_transaction(); } } m_row = table->get(row_idx); if (make_if_absent) { // User existed in the table, but had been marked for deletion. Unmark it. m_realm->begin_transaction(); table->set_bool(m_schema.idx_marked_for_removal, row_idx, false); m_realm->commit_transaction(); m_invalid = false; } else { m_invalid = m_row.get_bool(m_schema.idx_marked_for_removal); } }
void ObjectStore::set_primary_key_for_object(Group *group, StringData object_type, StringData primary_key) { TableRef table = group->get_table(c_primaryKeyTableName); // get row or create if new object and populate size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); if (row == not_found && primary_key.size()) { row = table->add_empty_row(); table->set_string(c_primaryKeyObjectClassColumnIndex, row, object_type); } // set if changing, or remove if setting to nil if (primary_key.size() == 0) { if (row != not_found) { table->remove(row); } } else { table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); } }
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); }
// 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 std::vector<std::pair<std::string, Property>> ObjectStore::create_tables(Group *group, Schema &target_schema, bool update_existing) { // properties to delete std::vector<std::pair<std::string,Property>> to_delete; // first pass to create missing tables std::vector<ObjectSchema *> to_update; for (auto& object_schema : target_schema) { bool created = false; ObjectStore::table_for_object_type_create_if_needed(group, object_schema.name, created); // we will modify tables for any new objectSchema (table was created) or for all if update_existing is true if (update_existing || created) { to_update.push_back(&object_schema); } } // second pass adds/removes columns for out of date tables for (auto& target_object_schema : to_update) { TableRef table = table_for_object_type(group, target_object_schema->name); ObjectSchema current_schema(group, target_object_schema->name); std::vector<Property> &target_props = target_object_schema->persisted_properties; // handle columns changing from required to optional for (auto& current_prop : current_schema.persisted_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; } bool inserted_placeholder_column = false; // remove extra columns size_t deleted = 0; for (auto& current_prop : current_schema.persisted_properties) { current_prop.table_column -= deleted; auto target_prop = target_object_schema->property_for_name(current_prop.name); // mark object name & property pairs for deletion if (!target_prop) { to_delete.push_back(std::make_pair(current_schema.name, current_prop)); } else if ((property_has_changed(current_prop, *target_prop) && !property_can_be_migrated_to_nullable(current_prop, *target_prop))) { if (deleted == current_schema.persisted_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; } table->remove_column(current_prop.table_column); ++deleted; current_prop.table_column = npos; } } // add missing columns for (auto& target_prop : target_props) { auto current_prop = current_schema.property_for_name(target_prop.name); // add any new properties (no old column or old column was removed due to not matching) if (!current_prop || current_prop->table_column == npos) { switch (target_prop.type) { // for objects and arrays, we have to specify target table case PropertyType::Object: case PropertyType::Array: { TableRef link_table = ObjectStore::table_for_object_type(group, target_prop.object_type); REALM_ASSERT(link_table); target_prop.table_column = table->add_column_link(DataType(target_prop.type), target_prop.name, *link_table); break; } default: target_prop.table_column = table->add_column(DataType(target_prop.type), target_prop.name, target_prop.is_nullable); break; } } else { target_prop.table_column = current_prop->table_column; } } 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--; } } // update table metadata if (target_object_schema->primary_key.length()) { // if there is a primary key set, check if it is the same as the old key 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); } } else if (current_schema.primary_key.length()) { // there is no primary key, so if there was one nil out set_primary_key_for_object(group, target_object_schema->name, ""); } } return to_delete; }