Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix wait_for_download behaviour with permission schema #5791

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 @@ -26,6 +26,7 @@ This release is compatible with the Realm Object Server 3.0.0-beta.3 or later.
* `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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -1159,16 +1159,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 @@ -1181,7 +1180,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
15 changes: 15 additions & 0 deletions realm/realm-library/src/main/java/io/realm/DynamicRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public void onResult(int count) {
this.schema = new MutableRealmSchema(this);
}

private DynamicRealm (RealmConfiguration configuration) {
super(configuration, null);
schema = null;
}

private DynamicRealm(OsSharedRealm sharedRealm) {
super(sharedRealm);
this.schema = new MutableRealmSchema(this);
Expand Down Expand Up @@ -267,6 +272,16 @@ public void executeTransaction(Transaction transaction) {
}
}

/**
* Creates a schemaless {@link DynamicRealm} instance.
*
* @param configuration {@link RealmConfiguration} used to open the Realm.
* @return an empty Realm without any schema.
*/
public static DynamicRealm createSchemalessInstance(RealmConfiguration configuration) {
return new DynamicRealm(configuration);
}

/**
* Creates a {@link DynamicRealm} instance without checking the existence in the {@link RealmCache}.
*
Expand Down
44 changes: 5 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,11 @@ 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 (configuration.isSyncConfiguration() && !fileExists) {
ObjectServerFacade.getSyncFacadeIfPossible().downloadRemoteChanges(configuration);
}

if (realmClass == Realm.class) {
// RealmMigrationNeededException might be thrown here.
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