diff --git a/.github/workflows/TestCosmos.yaml b/.github/workflows/TestCosmos.yaml
index 52b8aeb3cbd..cb9d7103301 100644
--- a/.github/workflows/TestCosmos.yaml
+++ b/.github/workflows/TestCosmos.yaml
@@ -35,7 +35,7 @@ jobs:
shell: cmd
- name: Publish Test Results
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
index 02e75c9aa25..0c2135a68f4 100644
--- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
+++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
@@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
/// Async Limitations
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;
@@ -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")
@@ -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 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();
+ }
+
+ return searchDirs!.Split(new[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
}
///