Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* `realm.subscribeForObjects()` have been removed. Use `RealmQuery.findAllAsync(String subscriptionName)` and `RealmQuery.findAllAsync()` instead.
* Removed previously deprecated `RealmQuery.findAllSorted()`, `RealmQuery.findAllSortedAsync()` `RealmQuery.distinct() and `RealmQuery.distinctAsync()`.
* Renamed `RealmQuery.distinctValues()` to `RealmQuery.distinct()`
* Removing workarounds for old Realms versions [0.80.1](https://github.com/realm/realm-java/issues/1059), [0.84.1](https://github.com/realm/realm-java/issues/1703) and [2.0.0](https://github.com/realm/realm-java/pull/3488).

### Enhancements

Expand Down
2 changes: 1 addition & 1 deletion dependencies.list
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Realm Sync Core release used by Realm Java
# https://github.com/realm/realm-sync/releases
REALM_SYNC_VERSION=3.0.0-beta.10
REALM_SYNC_SHA256=7c36a38c0e5c0a46b22d5eee2b494bd9cc0219a526087a040ada86332f13401d
REALM_SYNC_SHA256=d5f2b1639efb5d64369d628c38e6d0698a1fbe6c2112b0f3321a85e170d6824e
c
# Object Server Release used by Integration tests. Installed using NPM.
# Use `npm view realm-object-server versions` to get a list of available versions.
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -1144,16 +1144,15 @@ public boolean shouldCompact(long totalBytes, long usedBytes) {
assertEquals(1, compactOnLaunchCount.get());

realm = Realm.getInstance(realmConfig);
// Called 2 more times. The PK table migration logic (the old PK bug) needs to open/close the Realm once.
assertEquals(3, compactOnLaunchCount.get());
assertEquals(2, compactOnLaunchCount.get());

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Realm bgRealm = Realm.getInstance(realmConfig);
bgRealm.close();
// compactOnLaunch should not be called anymore!
assertEquals(3, compactOnLaunchCount.get());
assertEquals(2, compactOnLaunchCount.get());
}
});
thread.start();
Expand All @@ -1166,7 +1165,7 @@ public void run() {

realm.close();

assertEquals(3, compactOnLaunchCount.get());
assertEquals(2, compactOnLaunchCount.get());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import io.realm.DynamicRealm;
import io.realm.DynamicRealmObject;
import io.realm.FieldAttribute;
Expand All @@ -38,9 +34,7 @@
import io.realm.RealmSchema;
import io.realm.rule.TestRealmConfigurationFactory;

import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

@RunWith(AndroidJUnit4.class)
Expand Down Expand Up @@ -170,92 +164,4 @@ public void addEmptyRowWithPrimaryKeyLong() {
assertEquals(42L, row.getLong(0));
sharedRealm.cancelTransaction();
}

@Test
public void migratePrimaryKeyTableIfNeeded_first() throws IOException {
configFactory.copyRealmFromAssets(context, "080_annotationtypes.realm", "default.realm");
sharedRealm = OsSharedRealm.getInstance(config);
Table.migratePrimaryKeyTableIfNeeded(sharedRealm);
Table t = sharedRealm.getTable("class_AnnotationTypes");
assertEquals("id", OsObjectStore.getPrimaryKeyForObject(sharedRealm, "AnnotationTypes"));
assertEquals(RealmFieldType.STRING, sharedRealm.getTable("pk").getColumnType(0));
}

@Test
public void migratePrimaryKeyTableIfNeeded_second() throws IOException {
configFactory.copyRealmFromAssets(context, "0841_annotationtypes.realm", "default.realm");
sharedRealm = OsSharedRealm.getInstance(config);
Table.migratePrimaryKeyTableIfNeeded(sharedRealm);
Table t = sharedRealm.getTable("class_AnnotationTypes");
assertEquals("id", OsObjectStore.getPrimaryKeyForObject(sharedRealm, "AnnotationTypes"));
assertEquals("AnnotationTypes", sharedRealm.getTable("pk").getString(0, 0));
}

// See https://github.com/realm/realm-java/issues/1775
// Before 0.84.2, pk table added prefix "class_" to every class's name.
// After 0.84.2, the pk table should be migrated automatically to remove the "class_".
// In 0.84.2, the class names in pk table has been renamed to some incorrect names like "Thclass", "Mclass",
// "NClass", "Meclass" and etc..
// The 0841_pk_migration.realm is made to produce the issue.
@Test
public void migratePrimaryKeyTableIfNeeded_primaryKeyTableMigratedWithRightName() throws IOException {
List<String> tableNames = Arrays.asList(
"ChatList", "Drafts", "Member", "Message", "Notifs", "NotifyLink", "PopularPost",
"Post", "Tags", "Threads", "User");

configFactory.copyRealmFromAssets(context, "0841_pk_migration.realm", "default.realm");
sharedRealm = OsSharedRealm.getInstance(config);
Table.migratePrimaryKeyTableIfNeeded(sharedRealm);

Table table = sharedRealm.getTable("pk");
for (int i = 0; i < table.size(); i++) {
UncheckedRow row = table.getUncheckedRow(i);
// io_realm_internal_Table_PRIMARY_KEY_CLASS_COLUMN_INDEX 0LL
assertTrue(tableNames.contains(row.getString(0)));
}
}

// PK table's column 'pk_table' needs search index in order to use set_string_unique.
// See https://github.com/realm/realm-java/pull/3488
@Test
public void migratePrimaryKeyTableIfNeeded_primaryKeyTableNeedSearchIndex() {
sharedRealm = OsSharedRealm.getInstance(config);
sharedRealm.beginTransaction();
OsObjectStore.setSchemaVersion(sharedRealm,0); // Create meta table
Table table = sharedRealm.createTable(Table.getTableNameForClass("TestTable"));
long column = table.addColumn(RealmFieldType.INTEGER, "PKColumn");
table.addSearchIndex(column);
OsObjectStore.setPrimaryKeyForObject(sharedRealm, "TestTable", "PKColumn");
sharedRealm.commitTransaction();

assertEquals("PKColumn", OsObjectStore.getPrimaryKeyForObject(sharedRealm, "TestTable"));
// Now we have a pk table with search index.

sharedRealm.beginTransaction();
Table pkTable = sharedRealm.getTable("pk");
long classColumn = pkTable.getColumnIndex("pk_table");
pkTable.removeSearchIndex(classColumn);

// Tries to add a pk for another table.
Table table2 = sharedRealm.createTable(Table.getTableNameForClass("TestTable2"));
long column2 = table2.addColumn(RealmFieldType.INTEGER, "PKColumn");
table2.addSearchIndex(column2);
try {
OsObjectStore.setPrimaryKeyForObject(sharedRealm, "TestTable2", "PKColumn");
} catch (IllegalStateException ignored) {
// Column has no search index.
}
sharedRealm.commitTransaction();

assertFalse(pkTable.hasSearchIndex(classColumn));

Table.migratePrimaryKeyTableIfNeeded(sharedRealm);
assertTrue(pkTable.hasSearchIndex(classColumn));

sharedRealm.beginTransaction();
// Now it works.
table2.addSearchIndex(column2);
OsObjectStore.setPrimaryKeyForObject(sharedRealm, "TestTable2", "PKColumn");
sharedRealm.commitTransaction();
}
}
114 changes: 0 additions & 114 deletions realm/realm-library/src/main/cpp/io_realm_internal_Table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,120 +1197,6 @@ JNIEXPORT jboolean JNICALL Java_io_realm_internal_Table_nativeIsValid(JNIEnv*, j
return to_jbool(TBL(nativeTablePtr)->is_attached()); // noexcept
}

static bool pk_table_needs_migration(ConstTableRef pk_table)
{
// Fix wrong types (string, int) -> (string, string)
if (pk_table->get_column_type(FIELD_COLUMN_INDEX) == type_Int) {
return true;
}

// If needed remove "class_" prefix from class names
size_t number_of_rows = pk_table->size();
for (size_t row_ndx = 0; row_ndx < number_of_rows; row_ndx++) {
StringData table_name = pk_table->get_string(CLASS_COLUMN_INDEX, row_ndx);
if (table_name.begins_with(TABLE_PREFIX)) {
return true;
}
}
// From realm-java 2.0.0, pk table's class column requires a search index.
if (!pk_table->has_search_index(CLASS_COLUMN_INDEX)) {
return true;
}
return false;
}

// 1) Fixes interop issue with Cocoa Realm where the Primary Key table had different types.
// This affects:
// - All Realms created by Cocoa and used by Realm-android up to 0.80.1
// - All Realms created by Realm-Android 0.80.1 and below
// See https://github.com/realm/realm-java/issues/1059
//
// 2) Fix interop issue with Cocoa Realm where primary key tables on Cocoa doesn't have the "class_" prefix.
// This affects:
// - All Realms created by Cocoa and used by Realm-android up to 0.84.1
// - All Realms created by Realm-Android 0.84.1 and below
// See https://github.com/realm/realm-java/issues/1703
//
// 3> PK table's column 'pk_table' needs search index in order to use set_string_unique.
// This affects:
// - All Realms created by Cocoa and used by Realm-java before 2.0.0
// See https://github.com/realm/realm-java/pull/3488

// This methods converts the old (wrong) table format (string, integer) to the right (string,string) format and strips
// any class names in the col[0] of their "class_" prefix
static bool migrate_pk_table(const Group& group, TableRef pk_table)
{
bool changed = false;

// Fix wrong types (string, int) -> (string, string)
if (pk_table->get_column_type(FIELD_COLUMN_INDEX) == type_Int) {
StringData tmp_col_name = StringData("tmp_field_name");
size_t tmp_col_ndx = pk_table->add_column(DataType(type_String), tmp_col_name);

// Create tmp string column with field name instead of column index
size_t number_of_rows = pk_table->size();
for (size_t row_ndx = 0; row_ndx < number_of_rows; row_ndx++) {
StringData table_name = pk_table->get_string(CLASS_COLUMN_INDEX, row_ndx);
size_t col_ndx = static_cast<size_t>(pk_table->get_int(FIELD_COLUMN_INDEX, row_ndx));
StringData col_name = group.get_table(table_name)->get_column_name(col_ndx);
// Make a copy of the string
pk_table->set_string(tmp_col_ndx, row_ndx, col_name);
}

// Delete old int column, and rename tmp column to same name
// The column index for the renamed column will then be the same as the deleted old column
pk_table->remove_column(FIELD_COLUMN_INDEX);
pk_table->rename_column(pk_table->get_column_index(tmp_col_name), StringData("pk_property"));
changed = true;
}

// If needed remove "class_" prefix from class names
size_t number_of_rows = pk_table->size();
for (size_t row_ndx = 0; row_ndx < number_of_rows; row_ndx++) {
StringData table_name = pk_table->get_string(CLASS_COLUMN_INDEX, row_ndx);
if (table_name.begins_with(TABLE_PREFIX)) {
// New string copy is needed, since the original memory will be changed.
std::string str(table_name.substr(TABLE_PREFIX.length()));
StringData sd(str);
pk_table->set_string(CLASS_COLUMN_INDEX, row_ndx, sd);
changed = true;
}
}

// From realm-java 2.0.0, pk table's class column requires a search index.
if (!pk_table->has_search_index(CLASS_COLUMN_INDEX)) {
pk_table->add_search_index(CLASS_COLUMN_INDEX);
changed = true;
}
return changed;
}

JNIEXPORT void JNICALL Java_io_realm_internal_Table_nativeMigratePrimaryKeyTableIfNeeded(JNIEnv* env, jclass,
jlong shared_realm_ptr)
{
TR_ENTER_PTR(shared_realm_ptr)
auto& shared_realm = *reinterpret_cast<SharedRealm*>(shared_realm_ptr);
try {
if (!shared_realm->read_group().has_table(PK_TABLE_NAME)) {
return;
}

auto pk_table = shared_realm->read_group().get_table(PK_TABLE_NAME);
if (!pk_table_needs_migration(pk_table)) {
return;
}

shared_realm->begin_transaction();
if (migrate_pk_table(shared_realm->read_group(), pk_table)) {
shared_realm->commit_transaction();
}
else {
shared_realm->cancel_transaction();
}
}
CATCH_STD()
}

JNIEXPORT jboolean JNICALL Java_io_realm_internal_Table_nativeHasSameSchema(JNIEnv*, jobject, jlong thisTablePtr,
jlong otherTablePtr)
{
Expand Down
56 changes: 17 additions & 39 deletions realm/realm-library/src/main/java/io/realm/RealmCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
import io.realm.internal.Capabilities;
import io.realm.internal.ObjectServerFacade;
import io.realm.internal.OsObjectStore;
import io.realm.internal.OsSharedRealm;
import io.realm.internal.RealmNotifier;
import io.realm.internal.Table;
import io.realm.internal.Util;
import io.realm.internal.android.AndroidCapabilities;
import io.realm.internal.android.AndroidRealmNotifier;
Expand Down Expand Up @@ -289,43 +287,6 @@ private synchronized <E extends BaseRealm> E doCreateRealmOrGetFromCache(RealmCo

if (getTotalGlobalRefCount() == 0) {
copyAssetFileIfNeeded(configuration);
boolean fileExists = configuration.realmExists();

OsSharedRealm sharedRealm = null;
try {
if (configuration.isSyncConfiguration()) {
// If waitForInitialRemoteData() was enabled, we need to make sure that all data is downloaded
// before proceeding. We need to open the Realm instance first to start any potential underlying
// SyncSession so this will work. TODO: This needs to be decoupled.
if (!fileExists) {
sharedRealm = OsSharedRealm.getInstance(configuration);
try {
ObjectServerFacade.getSyncFacadeIfPossible().downloadRemoteChanges(configuration);
} catch (Throwable t) {
// If an error happened while downloading initial data, we need to reset the file so we can
// download it again on the next attempt.
sharedRealm.close();
sharedRealm = null;
// FIXME: We don't have a way to ensure that the Realm instance on client thread has been
// closed for now.
// https://github.com/realm/realm-java/issues/5416
BaseRealm.deleteRealm(configuration);
throw t;
}
}
} else {
if (fileExists) {
// Primary key problem only exists before we release sync.
sharedRealm = OsSharedRealm.getInstance(configuration);
Table.migratePrimaryKeyTableIfNeeded(sharedRealm);
}
}
} finally {
if (sharedRealm != null) {
sharedRealm.close();
}
}

// We are holding the lock, and we can set the invalidated configuration since there is no global ref to it.
this.configuration = configuration;
} else {
Expand All @@ -336,6 +297,7 @@ private synchronized <E extends BaseRealm> E doCreateRealmOrGetFromCache(RealmCo
if (refAndCount.localRealm.get() == null) {
// Creates a new local Realm instance
BaseRealm realm;
boolean fileExists = configuration.realmExists();

if (realmClass == Realm.class) {
// RealmMigrationNeededException might be thrown here.
Expand All @@ -346,6 +308,22 @@ private synchronized <E extends BaseRealm> E doCreateRealmOrGetFromCache(RealmCo
throw new IllegalArgumentException(WRONG_REALM_CLASS_MESSAGE);
}

if (configuration.isSyncConfiguration() && !fileExists) {
try {
ObjectServerFacade.getSyncFacadeIfPossible().downloadRemoteChanges(configuration);
realm.refresh();
} catch (Throwable t) {
// If an error happened while downloading initial data, we need to reset the file so we can
// download it again on the next attempt.
realm.close();
// FIXME: We don't have a way to ensure that the Realm instance on client thread has been
// closed for now.
// https://github.com/realm/realm-java/issues/5416
BaseRealm.deleteRealm(configuration);
throw t;
}
}

// The Realm instance has been created without exceptions. Cache and reference count can be updated now.
refAndCount.localRealm.set(realm);
refAndCount.localCount.set(0);
Expand Down
18 changes: 0 additions & 18 deletions realm/realm-library/src/main/java/io/realm/internal/Table.java
Original file line number Diff line number Diff line change
Expand Up @@ -513,22 +513,6 @@ public void removeSearchIndex(long columnIndex) {
nativeRemoveSearchIndex(nativePtr, columnIndex);
}

/*
* 1) Migration required to fix https://github.com/realm/realm-java/issues/1059
* This will convert INTEGER column to the corresponding STRING column if needed.
* Any database created on Realm-Java 0.80.1 and below will have this error.
*
* 2) Migration required to fix: https://github.com/realm/realm-java/issues/1703
* This will remove the prefix "class_" from all table names in the pk_column
* Any database created on Realm-Java 0.84.1 and below will have this error.
*
* The native method will begin a transaction and make the migration if needed.
* This function should not be called in a transaction.
*/
public static void migratePrimaryKeyTableIfNeeded(OsSharedRealm sharedRealm) {
nativeMigratePrimaryKeyTableIfNeeded(sharedRealm.getNativePtr());
}

public boolean hasSearchIndex(long columnIndex) {
return nativeHasSearchIndex(nativePtr, columnIndex);
}
Expand Down Expand Up @@ -780,8 +764,6 @@ public static String getTableNameForClass(String name) {

public static native void nativeSetLink(long nativeTablePtr, long columnIndex, long rowIndex, long value, boolean isDefault);

private static native void nativeMigratePrimaryKeyTableIfNeeded(long sharedRealmPtr);

private native void nativeAddSearchIndex(long nativePtr, long columnIndex);

private native void nativeRemoveSearchIndex(long nativePtr, long columnIndex);
Expand Down
Loading