Skip to content
Merged
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
2 changes: 1 addition & 1 deletion SQLite.Net.Tests/SQLite.Net2.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.3.1" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.8" />
</ItemGroup>

<ItemGroup>
Expand Down
45 changes: 40 additions & 5 deletions SQLite.Net.Tests/SerializeTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NUnit.Framework;

using System.Threading.Tasks;

namespace SQLite.Net2.Tests
{
[TestFixture]
Expand Down Expand Up @@ -32,7 +31,7 @@ public SerializeTestDb(String path) : base(path)
}

[Test]
public async Task SerializeRoundTrip()
public void SerializeRoundTrip()
{
var obj1 = new SerializeTestObj
{
Expand All @@ -41,15 +40,15 @@ public async Task SerializeRoundTrip()

SQLiteConnection srcDb = new SerializeTestDb(":memory:");

int numIn1 = srcDb.Insert(obj1);
var numIn1 = srcDb.Insert(obj1);
Assert.AreEqual(1, numIn1);

List<SerializeTestObj> result1 = srcDb.Query<SerializeTestObj>("select * from SerializeTestObj").ToList();
Assert.AreEqual(numIn1, result1.Count);
Assert.AreEqual(obj1.Text, result1.First().Text);


byte[] serialized = srcDb.Serialize();
var serialized = srcDb.Serialize();
srcDb.Close();

SQLiteConnection destDb = new SerializeTestDb(":memory");
Expand All @@ -61,5 +60,41 @@ public async Task SerializeRoundTrip()

destDb.Close();
}

[Test]
public void SerializeRoundTripStreams()
{
var obj1 = new SerializeTestObj
{
Text = "GLaDOS loves testing!"
};

SQLiteConnection srcDb = new SerializeTestDb(":memory:");

var numIn1 = srcDb.Insert(obj1);
Assert.AreEqual(1, numIn1);

List<SerializeTestObj> result1 = srcDb.Query<SerializeTestObj>("select * from SerializeTestObj").ToList();
Assert.AreEqual(numIn1, result1.Count);
Assert.AreEqual(obj1.Text, result1.First().Text);


MemoryStream stream = new();
var size = srcDb.Serialize(stream);
Assert.That(size, Is.GreaterThan(0));
srcDb.Close();

stream.Seek(0, SeekOrigin.Begin);

SQLiteConnection destDb = new SerializeTestDb(":memory");
destDb.Deserialize(stream);
Assert.That(stream.Position, Is.EqualTo(size));

result1 = destDb.Query<SerializeTestObj>("select * from SerializeTestObj").ToList();
Assert.AreEqual(numIn1, result1.Count);
Assert.AreEqual(obj1.Text, result1.First().Text);

destDb.Close();
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 0 additions & 6 deletions nuget.config

This file was deleted.

Binary file modified src/Plugins/SQLitePCLRaw/SQLitePCLRaw.core.dll
Binary file not shown.
Binary file modified src/Plugins/SQLitePCLRaw/SQLitePCLRaw.provider.internal.dll
Binary file not shown.
Binary file modified src/Plugins/SQLitePCLRaw/SQLitePCLRaw.provider.sqlite3.dll
Binary file not shown.
63 changes: 46 additions & 17 deletions src/SQLite.Net/Interop/SQLiteApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,34 +315,63 @@ public bool Equals(IDbBackupHandle other)

#region Serialize

public byte[] Serialize(IDbHandle db, string schema)
/// <summary>
/// Serialize the database, passing the memory to the given function to be copied as appropriate.
/// <see cref="copy"/> will be called with a pointer to the serialized data and its size, and its
/// return value or any exceptions will be passed through. The serialized memory is only valid
/// during the callback, and will be freed immediately after regardless of success or failure.
/// </summary>
public T Serialize<T>(IDbHandle db, string schema, Func<IntPtr, long, T> copy)
{
var internalDbHandle = (DbHandle)db;
return raw.sqlite3_serialize(internalDbHandle.DbPtr, schema);
}

public long Serialize(IDbHandle db, string schema, System.IO.Stream stream)
{
var internalDbHandle = (DbHandle)db;
return raw.sqlite3_serialize(internalDbHandle.DbPtr, schema, stream);
}
var bytesPtr = raw.sqlite3_serialize(internalDbHandle.DbPtr, schema, out var size, 0);
if (bytesPtr == IntPtr.Zero)
{
throw new SQLiteException(Result.NoMem, Errmsg16(internalDbHandle));
}

public void Deserialize(IDbHandle db, string schema, byte[] data)
{
var internalDbHandle = (DbHandle)db;
var r = (Result)raw.sqlite3_deserialize(internalDbHandle.DbPtr, schema, data);
if (r != Result.OK)
try
{
throw new SQLiteException(r, Errmsg16(internalDbHandle));
return copy(bytesPtr, size);
}
finally
{
raw.sqlite3_free(bytesPtr);
}
}

public void Deserialize(IDbHandle db, string schema, System.IO.Stream stream)
/// <summary>
/// Deserialize a database. A buffer of the requested <see cref="size"/> will be allocated and
/// passed with the size to the given function to be filled as appropriate. Any exceptions will
/// be passed through. The buffer is only valid during the callback, and will be freed or passed
/// to sqlite immediately after depending on success.
/// </summary>
public void Deserialize(IDbHandle db, string schema, long size, Action<IntPtr, long> copy)
{
var internalDbHandle = (DbHandle)db;
var r = (Result)raw.sqlite3_deserialize(internalDbHandle.DbPtr, schema, stream);

var bytesPtr = raw.sqlite3_malloc64(size);
if (bytesPtr == IntPtr.Zero)
{
throw new SQLiteException(Result.NoMem, Errmsg16(internalDbHandle));
}

try
{
copy(bytesPtr, size);
}
catch
{
raw.sqlite3_free(bytesPtr);
throw;
}

const int flags = raw.SQLITE_DESERIALIZE_RESIZEABLE | raw.SQLITE_DESERIALIZE_FREEONCLOSE;
var r = (Result)raw.sqlite3_deserialize(internalDbHandle.DbPtr, schema, bytesPtr, size, size, flags);
if (r != Result.OK)
{
// note: sqlite will free the buffer on error, thanks to SQLITE_DESERIALIZE_FREEONCLOSE
throw new SQLiteException(r, Errmsg16(internalDbHandle));
}
}
Expand Down Expand Up @@ -380,4 +409,4 @@ public bool Equals(IDbStatement other)
}
}
}
}
}
5 changes: 3 additions & 2 deletions src/SQLite.Net/SQLite.Net2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<AssemblyCopyright>Copyright ©2022 Benjamin Mayrargue</AssemblyCopyright>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<!-- nuget configurable properties -->
Expand All @@ -31,7 +32,7 @@
<Title>sqlite-net2 light ORM for SQLite</Title>
<Description>sqlite-net2 allows applications to manage data in SQLite databases using Entity Framework like queries, but much lighter</Description>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<VersionSuffix>.7</VersionSuffix>
<VersionSuffix>.8</VersionSuffix>
<PackageVersion>$(Version)$(VersionSuffix)</PackageVersion>
<Authors>Benjamin Mayrargue</Authors>
<Owners>Benjamin Mayrargue</Owners>
Expand All @@ -47,7 +48,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.3.1" />
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
<None Include="..\..\LICENSE.md" Pack="true" PackagePath="" />
</ItemGroup>

Expand Down
53 changes: 47 additions & 6 deletions src/SQLite.Net/SQLiteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -1806,24 +1807,64 @@ public async Task<string> CreateDatabaseBackup()

#region Serialize

/// <summary>
/// Get the serialization of the database.
/// (This is the same sequence of bytes as would be written to a .db file.)
/// </summary>
public byte[] Serialize(string databaseName = "main")
{
return sqlite.Serialize(Handle, databaseName);
return sqlite.Serialize(Handle, databaseName, (bytesPtr, size) =>
{
unsafe
{
return new ReadOnlySpan<byte>(bytesPtr.ToPointer(), checked((int)size)).ToArray();
}
});
}

public long Serialize(System.IO.Stream stream, string databaseName = "main")
/// <summary>
/// Write the serialization of the database to the given stream.
/// (This is the same sequence of bytes as would be written to a .db file.)
/// </summary>
/// <returns>The number of bytes written to the stream.</returns>
public long Serialize(Stream stream, string databaseName = "main")
{
return sqlite.Serialize(Handle, databaseName, stream);
return sqlite.Serialize(Handle, databaseName, (bytesPtr, size) =>
{
unsafe
{
new UnmanagedMemoryStream((byte*)bytesPtr, size).CopyTo(stream);
}
return size;
});
}

/// <summary>
/// Load the serialization (.db file contents) of a database from the given byte array.
/// </summary>
public void Deserialize(byte[] dbData, string databaseName = "main")
{
sqlite.Deserialize(Handle, databaseName, dbData);
sqlite.Deserialize(Handle, databaseName, dbData.Length, (bytesPtr, size) =>
{
unsafe
{
dbData.CopyTo(new Span<byte>(bytesPtr.ToPointer(), checked((int)size)));
}
});
}

public void Deserialize(System.IO.Stream stream, string databaseName = "main")
/// <summary>
/// Load the serialization (.db file contents) of a database from the given stream.
/// </summary>
public void Deserialize(Stream stream, string databaseName = "main")
{
sqlite.Deserialize(Handle, databaseName, stream);
sqlite.Deserialize(Handle, databaseName, stream.Length, (bytesPtr, size) =>
{
unsafe
{
stream.CopyTo(new UnmanagedMemoryStream((byte*)bytesPtr, size, size, FileAccess.Write));
}
});
}

#endregion
Expand Down