Skip to content

Commit

Permalink
Merge branch 'release/8.0-staging' => 'release/8.0' (#35742)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriySvyryd authored Mar 6, 2025
2 parents 51e50e9 + eeab918 commit 5f9d0dc
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 5 deletions.
55 changes: 53 additions & 2 deletions src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public class ParameterExtractingExpressionVisitor : ExpressionVisitor
private static readonly bool UseOldBehavior31552 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31552", out var enabled31552) && enabled31552;

private static readonly bool UseOldBehavior35100 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35100", out var enabled35100) && enabled35100;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down Expand Up @@ -181,9 +184,11 @@ protected override Expression VisitConditional(ConditionalExpression conditional
/// </summary>
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
var method = methodCallExpression.Method;

if (!UseOldBehavior31552
&& methodCallExpression.Method.DeclaringType == typeof(EF)
&& methodCallExpression.Method.Name == nameof(EF.Constant))
&& method.DeclaringType == typeof(EF)
&& method.Name == nameof(EF.Constant))
{
// If this is a call to EF.Constant(), then examine its operand. If the operand isn't evaluatable (i.e. contains a reference
// to a database table), throw immediately.
Expand All @@ -197,6 +202,52 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
return Evaluate(operand, generateParameter: false);
}

// .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans").
// Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to
// Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757.
if (method.DeclaringType == typeof(MemoryExtensions) && !UseOldBehavior35100)
{
switch (method.Name)
{
case nameof(MemoryExtensions.Contains)
when methodCallExpression.Arguments is [var arg0, var arg1] &&
TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0):
{
return Visit(
Expression.Call(
EnumerableMethods.Contains.MakeGenericMethod(method.GetGenericArguments()[0]),
unwrappedArg0, arg1));
}

case nameof(MemoryExtensions.SequenceEqual)
when methodCallExpression.Arguments is [var arg0, var arg1]
&& TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0)
&& TryUnwrapSpanImplicitCast(arg1, out var unwrappedArg1):
return Visit(
Expression.Call(
EnumerableMethods.SequenceEqual.MakeGenericMethod(method.GetGenericArguments()[0]),
unwrappedArg0, unwrappedArg1));
}

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is MethodCallExpression
{
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Arguments: [var unwrapped]
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

return base.VisitMethodCall(methodCallExpression);
}

Expand Down
82 changes: 79 additions & 3 deletions src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
/// <seealso href="https://docs.microsoft.com/dotnet/standard/data/sqlite/async">Async Limitations</seealso>
public partial class SqliteConnection : DbConnection
{
private static readonly bool UseOldBehavior35715 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35715", out var enabled35715) && enabled35715;

internal const string MainDatabaseName = "main";

private const int SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1;
Expand All @@ -48,6 +51,8 @@ public partial class SqliteConnection : DbConnection
private static readonly StateChangeEventArgs _fromClosedToOpenEventArgs = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
private static readonly StateChangeEventArgs _fromOpenToClosedEventArgs = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);

private static string[]? NativeDllSearchDirectories;

static SqliteConnection()
{
Type.GetType("SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2")
Expand Down Expand Up @@ -626,11 +631,82 @@ public virtual void LoadExtension(string file, string? proc = null)

private void LoadExtensionCore(string file, string? proc)
{
var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
if (rc != SQLITE_OK)
if (UseOldBehavior35715)
{
var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
if (rc != SQLITE_OK)
{
throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
}
}
else
{
SqliteException? firstException = null;
foreach (var path in GetLoadExtensionPaths(file))
{
var rc = sqlite3_load_extension(Handle, utf8z.FromString(path), utf8z.FromString(proc), out var errmsg);
if (rc == SQLITE_OK)
{
return;
}

if (firstException == null)
{
// We store the first exception so that error message looks more obvious if file appears in there
firstException = new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
}
}

if (firstException != null)
{
throw firstException;
}
}
}

private static IEnumerable<string> GetLoadExtensionPaths(string file)
{
// we always try original input first
yield return file;

string? dirName = Path.GetDirectoryName(file);

// we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
if (!string.IsNullOrEmpty(dirName))
{
throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
yield break;
}

bool shouldTryAddingLibPrefix = !file.StartsWith("lib", StringComparison.Ordinal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

if (shouldTryAddingLibPrefix)
{
yield return $"lib{file}";
}

NativeDllSearchDirectories ??= GetNativeDllSearchDirectories();

foreach (string dir in NativeDllSearchDirectories)
{
yield return Path.Combine(dir, file);

if (shouldTryAddingLibPrefix)
{
yield return Path.Combine(dir, $"lib{file}");
}
}
}

private static string[] GetNativeDllSearchDirectories()
{
string? searchDirs = AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES") as string;

if (string.IsNullOrEmpty(searchDirs))
{
return Array.Empty<string>();
}

return searchDirs!.Split(new[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
Expand Down

0 comments on commit 5f9d0dc

Please sign in to comment.