Skip to content

Commit

Permalink
Fixing #1585 expression cache problem
Browse files Browse the repository at this point in the history
  • Loading branch information
mbdavid committed Mar 24, 2020
1 parent b5180b6 commit 4fddc91
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 33 deletions.
3 changes: 2 additions & 1 deletion LiteDB.Tests/Database/Document_Size_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ public void Very_Large_Single_Document_Support_With_Partial_Load_Memory_Usage()
var memoryFullDocument = Process.GetCurrentProcess().WorkingSet64;

// memory after full document must be at least 10Mb more than with name only
// using 50% of array size because there are no precise value when using memory usage

memoryFullDocument.Should().BeGreaterOrEqualTo(memoryForNameOnly + ARRAY_SIZE);
memoryFullDocument.Should().BeGreaterOrEqualTo(memoryForNameOnly + (ARRAY_SIZE / 2));
}
}
}
Expand Down
104 changes: 104 additions & 0 deletions LiteDB.Tests/Issue1585.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Linq;
using FluentAssertions;
using LiteDB.Engine;
using Xunit;

namespace LiteDB.Issue1585
{
public class Issue1585_Tests
{
public class PlayerDto
{
[BsonId]
public Guid Id { get; }
public string Name { get; }

public PlayerDto(Guid id, string name)
{
Id = id;
Name = name;
}
}

[Fact]
public void Dto_Read()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

[Fact]
public void Dto_Read1()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

[Fact]
public void Dto_Read2()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

[Fact]
public void Dto_Read3()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

[Fact]
public void Dto_Read4()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

[Fact]
public void Dto_Read5()
{
using (var db = new LiteDatabase(new MemoryStream()))
{
var id = Guid.NewGuid();
var col = db.GetCollection<PlayerDto>();
col.Insert(new PlayerDto(id, "Bob"));
var player = col.FindOne(x => x.Id == id);
Assert.NotNull(player);
}
}

}
}
2 changes: 1 addition & 1 deletion LiteDB.Tests/Mapper/LinqEval_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class User
public string Name { get; set; }
public DateTime Date { get; set; }
public bool Active { get; set; }
public Guid Ticket { get; set; }

public Address Address { get; set; }
public List<Phone> Phones { get; set; }
Expand Down Expand Up @@ -152,7 +153,6 @@ public void Linq_Array_Navigation_Eval()
//** Eval(u, x => x.Phones.Items(z => z.Prefix >= 20).Number, 2, 3);
}


/// <summary>
/// Eval expression and check with expected
/// </summary>
Expand Down
69 changes: 38 additions & 31 deletions LiteDB/Document/Expression/BsonExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@

namespace LiteDB
{
/// <summary>
/// Delegate function to get compiled enumerable expression
/// </summary>
internal delegate IEnumerable<BsonValue> BsonExpressionEnumerableDelegate(IEnumerable<BsonDocument> source, BsonDocument root, BsonValue current, Collation collation, BsonDocument parameters);

/// <summary>
/// Delegate function to get compiled scalar expression
/// </summary>
internal delegate BsonValue BsonExpressionScalarDelegate(IEnumerable<BsonDocument> source, BsonDocument root, BsonValue current, Collation collation, BsonDocument parameters);

/// <summary>
/// Compile and execute string expressions using BsonDocuments. Used in all document manipulation (transform, filter, indexes, updates). See https://github.com/mbdavid/LiteDB/wiki/Expressions
/// </summary>
Expand Down Expand Up @@ -107,12 +117,12 @@ public sealed class BsonExpression
/// <summary>
/// Compiled Expression into a function to be executed: func(source[], root, current, parameters)[]
/// </summary>
private Func<IEnumerable<BsonDocument>, BsonDocument, BsonValue, Collation, BsonDocument, IEnumerable<BsonValue>> _func;
private BsonExpressionEnumerableDelegate _func;

/// <summary>
/// Compiled Expression into a scalar function to be executed: func(source[], root, current, parameters)1
/// </summary>
private Func<IEnumerable<BsonDocument>, BsonDocument, BsonValue, Collation, BsonDocument, BsonValue> _funcScalar;
private BsonExpressionScalarDelegate _funcScalar;

/// <summary>
/// Get default field name when need convert simple BsonValue into BsonDocument
Expand Down Expand Up @@ -275,6 +285,29 @@ internal BsonValue ExecuteScalar(IEnumerable<BsonDocument> source, BsonDocument
/// Parse string and create new instance of BsonExpression - can be cached
/// </summary>
public static BsonExpression Create(string expression)
{
return Create(expression, new BsonDocument());
}

/// <summary>
/// Parse string and create new instance of BsonExpression - can be cached
/// </summary>
public static BsonExpression Create(string expression, params BsonValue[] args)
{
var parameters = new BsonDocument();

for(var i = 0; i < args.Length; i++)
{
parameters[i.ToString()] = args[i];
}

return Create(expression, parameters);
}

/// <summary>
/// Parse string and create new instance of BsonExpression - can be cached
/// </summary>
public static BsonExpression Create(string expression, BsonDocument parameters)
{
if (string.IsNullOrWhiteSpace(expression)) throw new ArgumentNullException(nameof(expression));

Expand All @@ -300,6 +333,7 @@ public static BsonExpression Create(string expression)
IsImmutable = expr.IsImmutable,
UseSource = expr.UseSource,
IsScalar = expr.IsScalar,
Parameters = parameters ?? new BsonDocument(),
Fields = expr.Fields,
Left = expr.Left,
Right = expr.Right,
Expand All @@ -310,33 +344,6 @@ public static BsonExpression Create(string expression)
};
}

/// <summary>
/// Parse string and create new instance of BsonExpression - can be cached
/// </summary>
public static BsonExpression Create(string expression, params BsonValue[] args)
{
var expr = Create(expression);

for(var i = 0; i < args.Length; i++)
{
expr.Parameters[i.ToString()] = args[i];
}

return expr;
}

/// <summary>
/// Parse string and create new instance of BsonExpression - can be cached
/// </summary>
public static BsonExpression Create(string expression, BsonDocument parameters)
{
var expr = Create(expression);

expr.Parameters = parameters;

return expr;
}

/// <summary>
/// Parse tokenizer and create new instance of BsonExpression - for now, do not use cache
/// </summary>
Expand Down Expand Up @@ -395,13 +402,13 @@ internal static void Compile(BsonExpression expr, ExpressionContext context)
// compile linq expression according with return type (scalar or not)
if (expr.IsScalar)
{
var lambda = System.Linq.Expressions.Expression.Lambda<Func<IEnumerable<BsonDocument>, BsonDocument, BsonValue, Collation, BsonDocument, BsonValue>>(expr.Expression, context.Source, context.Root, context.Current, context.Collation, context.Parameters);
var lambda = System.Linq.Expressions.Expression.Lambda<BsonExpressionScalarDelegate>(expr.Expression, context.Source, context.Root, context.Current, context.Collation, context.Parameters);

expr._funcScalar = lambda.Compile();
}
else
{
var lambda = System.Linq.Expressions.Expression.Lambda<Func<IEnumerable<BsonDocument>, BsonDocument, BsonValue, Collation, BsonDocument, IEnumerable<BsonValue>>>(expr.Expression, context.Source, context.Root, context.Current, context.Collation, context.Parameters);
var lambda = System.Linq.Expressions.Expression.Lambda<BsonExpressionEnumerableDelegate>(expr.Expression, context.Source, context.Root, context.Current, context.Collation, context.Parameters);

expr._func = lambda.Compile();
}
Expand Down

0 comments on commit 4fddc91

Please sign in to comment.