Skip to content

Commit

Permalink
Added missing migration methods (#2635)
Browse files Browse the repository at this point in the history
  • Loading branch information
papafe authored Sep 29, 2021
1 parent 3d6b730 commit 9006b89
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## vNext (TBD)

### Enhancements
* Added two new methods on `Migration` (Issue [#2543](https://github.com/realm/realm-dotnet/issues/2543)):
* `RemoveType(typeName)` allows to completely remove a type and its schema from a realm during a migration.
* `RenameProperty(typeName, oldPropertyName, newPropertyName)` allows to rename a property during a migration.
* A Realm Schema can now be constructed at runtime as opposed to generated automatically from the model classes. The automatic generation continues to work and should cover the needs of the vast majority of Realm users. Manually constructing the schema may be required when the shape of the objects depends on some information only known at runtime or in very rare cases where it may provide performance benefits by representing a collection of known size as properties on the class. (Issue [#824](https://github.com/realm/realm-dotnet/issues/824))
* `RealmConfiguration.ObjectClasses` has now been deprecated in favor of `RealmConfiguration.Schema`. `RealmSchema` has an implicit conversion operator from `Type[]` so code that previously looked like `ObjectClasses = new[] { typeof(Foo), typeof(Bar) }` can be trivially updated to `Schema = new[] { typeof(Foo), typeof(Bar) }`.
* `Property` has been converted to a read-only struct by removing the setters from its properties. Those didn't do anything previously, so we don't expect anyone was using them.
Expand Down
33 changes: 30 additions & 3 deletions Realm/Realm/Handles/SharedRealmHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ private static class NativeMethods
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogMessageCallback(PrimitiveValue message, LogLevel level);

// migrationSchema is a special schema that is used only in the context of a migration block.
// It is a pointer because we need to be able to modify this schema in some migration methods directly in core.
[return: MarshalAs(UnmanagedType.U1)]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool MigrationCallback(IntPtr oldRealm, IntPtr newRealm, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle);
internal delegate bool MigrationCallback(IntPtr oldRealm, IntPtr newRealm, IntPtr migrationSchema, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle);

[return: MarshalAs(UnmanagedType.U1)]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
Expand Down Expand Up @@ -181,6 +183,17 @@ public static extern void install_callbacks(
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_create_results", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr create_results(SharedRealmHandle sharedRealm, UInt32 table_key, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_rename_property", CallingConvention = CallingConvention.Cdecl)]
public static extern void rename_property(SharedRealmHandle sharedRealm,
[MarshalAs(UnmanagedType.LPWStr)] string typeName, IntPtr typeNameLength,
[MarshalAs(UnmanagedType.LPWStr)] string oldName, IntPtr oldNameLength,
[MarshalAs(UnmanagedType.LPWStr)] string newName, IntPtr newNameLength,
IntPtr migrationSchema,
out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_remove_type", CallingConvention = CallingConvention.Cdecl)]
public static extern bool remove_type(SharedRealmHandle sharedRealm, [MarshalAs(UnmanagedType.LPWStr)] string typeName, IntPtr typeLength, out NativeException ex);

#pragma warning restore SA1121 // Use built-in type alias
#pragma warning restore IDE0049 // Use built-in type alias
}
Expand Down Expand Up @@ -464,6 +477,20 @@ public bool TryFindObject(TableKey tableKey, in RealmValue id, out ObjectHandle
return true;
}

public void RenameProperty(string typeName, string oldName, string newName, IntPtr migrationSchema)
{
NativeMethods.rename_property(this, typeName, (IntPtr)typeName.Length,
oldName, (IntPtr)oldName.Length, newName, (IntPtr)newName.Length, migrationSchema, out var nativeException);
nativeException.ThrowIfNecessary();
}

public bool RemoveType(string typeName)
{
var result = NativeMethods.remove_type(this, typeName, (IntPtr)typeName.Length, out var nativeException);
nativeException.ThrowIfNecessary();
return result;
}

public ResultsHandle CreateResults(TableKey tableKey)
{
var result = NativeMethods.create_results(this, tableKey.Value, out var nativeException);
Expand Down Expand Up @@ -521,7 +548,7 @@ private static void LogMessage(PrimitiveValue message, LogLevel level)
}

[MonoPInvokeCallback(typeof(NativeMethods.MigrationCallback))]
private static bool OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle)
private static bool OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, IntPtr migrationSchema, Native.Schema oldSchema, ulong schemaVersion, IntPtr managedMigrationHandle)
{
var migrationHandle = GCHandle.FromIntPtr(managedMigrationHandle);
var migration = (Migration)migrationHandle.Target;
Expand All @@ -538,7 +565,7 @@ private static bool OnMigration(IntPtr oldRealmPtr, IntPtr newRealmPtr, Native.S
var newRealmHandle = new UnownedRealmHandle(newRealmPtr);
var newRealm = new Realm(newRealmHandle, migration.Configuration, migration.Schema);

var result = migration.Execute(oldRealm, newRealm);
var result = migration.Execute(oldRealm, newRealm, migrationSchema);

return result;
}
Expand Down
62 changes: 61 additions & 1 deletion Realm/Realm/Migration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

using System;
using System.Runtime.InteropServices;
using Realms.Helpers;
using Realms.Schema;

namespace Realms
Expand All @@ -34,6 +35,7 @@ namespace Realms
public class Migration
{
private GCHandle? _handle;
private IntPtr _migrationSchema;

internal GCHandle MigrationHandle => _handle ?? throw new ObjectDisposedException(nameof(Migration));

Expand Down Expand Up @@ -62,10 +64,11 @@ internal Migration(RealmConfiguration configuration, RealmSchema schema)
_handle = GCHandle.Alloc(this);
}

internal bool Execute(Realm oldRealm, Realm newRealm)
internal bool Execute(Realm oldRealm, Realm newRealm, IntPtr migrationSchema)
{
OldRealm = oldRealm;
NewRealm = newRealm;
_migrationSchema = migrationSchema;

try
{
Expand All @@ -83,11 +86,68 @@ internal bool Execute(Realm oldRealm, Realm newRealm)

NewRealm.Dispose();
NewRealm = null;

_migrationSchema = IntPtr.Zero;
}

return true;
}

/// <summary>
/// Removes a type during a migration. All the data associated with the type, as well as its schema, will be removed from <see cref="Realm"/>.
/// </summary>
/// <param name="typeName">The type that needs to be removed. </param>
/// <remarks>
/// The removed type will still be accessible from <see cref="OldRealm"/> in the migration block.
/// The type must not be present in the new schema. <see cref="Realm.RemoveAll{T}"/> can be used on <see cref="NewRealm"/> if one needs to delete the content of the table.
/// </remarks>
/// <returns><c>true</c> if the type does exist in the old schema, <c>false</c> otherwise.</returns>
public bool RemoveType(string typeName)
{
Argument.NotNullOrEmpty(typeName, nameof(typeName));
return NewRealm.SharedRealmHandle.RemoveType(typeName);
}

/// <summary>
/// Renames a property during a migration.
/// </summary>
/// <param name="typeName">The type for which the property rename needs to be performed. </param>
/// <param name="oldPropertyName">The previous name of the property. </param>
/// <param name="newPropertyName">The new name of the property. </param>
/// <example>
/// <code>
/// // Model in the old schema
/// class Dog : RealmObject
/// {
/// public string DogName { get; set; }
/// }
///
/// // Model in the new schema
/// class Dog : RealmObject
/// {
/// public string Name { get; set; }
/// }
///
/// //After the migration Dog.Name will contain the same values as Dog.DogName from the old realm, without the need to copy them explicitly
/// var config = new RealmConfiguration
/// {
/// SchemaVersion = 1,
/// MigrationCallback = (migration, oldSchemaVersion) =>
/// {
/// migration.RenameProperty("Dog", "DogName", "Name");
/// }
/// };
/// </code>
/// </example>
public void RenameProperty(string typeName, string oldPropertyName, string newPropertyName)
{
Argument.NotNullOrEmpty(typeName, nameof(typeName));
Argument.NotNullOrEmpty(oldPropertyName, nameof(oldPropertyName));
Argument.NotNullOrEmpty(newPropertyName, nameof(newPropertyName));

NewRealm.SharedRealmHandle.RenameProperty(typeName, oldPropertyName, newPropertyName, _migrationSchema);
}

internal void ReleaseHandle()
{
_handle?.Free();
Expand Down
Loading

0 comments on commit 9006b89

Please sign in to comment.