Skip to content

Commit 6e2f99c

Browse files
rojikrwq
andauthored
Improve LoadExtension to work correctly with dotnet run and lib* named libs (#35717)
* Improve LoadExtension to work correctly with dotnet run and lib packages * Use [] instead of Array.Empty Co-authored-by: Krzysztof Wicher <[email protected]>
1 parent 7cbf9ce commit 6e2f99c

File tree

1 file changed

+79
-3
lines changed

1 file changed

+79
-3
lines changed

Diff for: src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs

+79-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
2323
/// <seealso href="https://docs.microsoft.com/dotnet/standard/data/sqlite/async">Async Limitations</seealso>
2424
public partial class SqliteConnection : DbConnection
2525
{
26+
private static readonly bool UseOldBehavior35715 =
27+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35715", out var enabled35715) && enabled35715;
28+
2629
internal const string MainDatabaseName = "main";
2730

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

54+
private static string[]? NativeDllSearchDirectories;
55+
5156
static SqliteConnection()
5257
{
5358
Type.GetType("SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2")
@@ -624,11 +629,82 @@ public virtual void LoadExtension(string file, string? proc = null)
624629

625630
private void LoadExtensionCore(string file, string? proc)
626631
{
627-
var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
628-
if (rc != SQLITE_OK)
632+
if (UseOldBehavior35715)
633+
{
634+
var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
635+
if (rc != SQLITE_OK)
636+
{
637+
throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
638+
}
639+
}
640+
else
641+
{
642+
SqliteException? firstException = null;
643+
foreach (var path in GetLoadExtensionPaths(file))
644+
{
645+
var rc = sqlite3_load_extension(Handle, utf8z.FromString(path), utf8z.FromString(proc), out var errmsg);
646+
if (rc == SQLITE_OK)
647+
{
648+
return;
649+
}
650+
651+
if (firstException == null)
652+
{
653+
// We store the first exception so that error message looks more obvious if file appears in there
654+
firstException = new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
655+
}
656+
}
657+
658+
if (firstException != null)
659+
{
660+
throw firstException;
661+
}
662+
}
663+
}
664+
665+
private static IEnumerable<string> GetLoadExtensionPaths(string file)
666+
{
667+
// we always try original input first
668+
yield return file;
669+
670+
string? dirName = Path.GetDirectoryName(file);
671+
672+
// we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
673+
if (!string.IsNullOrEmpty(dirName))
629674
{
630-
throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
675+
yield break;
631676
}
677+
678+
bool shouldTryAddingLibPrefix = !file.StartsWith("lib", StringComparison.Ordinal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
679+
680+
if (shouldTryAddingLibPrefix)
681+
{
682+
yield return $"lib{file}";
683+
}
684+
685+
NativeDllSearchDirectories ??= GetNativeDllSearchDirectories();
686+
687+
foreach (string dir in NativeDllSearchDirectories)
688+
{
689+
yield return Path.Combine(dir, file);
690+
691+
if (shouldTryAddingLibPrefix)
692+
{
693+
yield return Path.Combine(dir, $"lib{file}");
694+
}
695+
}
696+
}
697+
698+
private static string[] GetNativeDllSearchDirectories()
699+
{
700+
string? searchDirs = AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES") as string;
701+
702+
if (string.IsNullOrEmpty(searchDirs))
703+
{
704+
return [];
705+
}
706+
707+
return searchDirs!.Split([ Path.PathSeparator ], StringSplitOptions.RemoveEmptyEntries);
632708
}
633709

634710
/// <summary>

0 commit comments

Comments
 (0)