File: Storage\SQLite\v2\SQLitePersistentStorageService.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Composition;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Storage;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.SQLite.v2;
 
internal sealed class SQLitePersistentStorageService(
    IPersistentStorageConfiguration configuration,
    IAsynchronousOperationListener asyncListener) : AbstractPersistentStorageService(configuration), IWorkspaceService
{
    [ExportWorkspaceServiceFactory(typeof(SQLitePersistentStorageService)), Shared]
    [method: ImportingConstructor]
    [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    internal sealed class ServiceFactory(
        IAsynchronousOperationListenerProvider asyncOperationListenerProvider) : IWorkspaceServiceFactory
    {
        private readonly IAsynchronousOperationListener _asyncListener = asyncOperationListenerProvider.GetListener(FeatureAttribute.PersistentStorage);
 
        public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
            => new SQLitePersistentStorageService(workspaceServices.GetRequiredService<IPersistentStorageConfiguration>(), _asyncListener);
    }
 
    private const string StorageExtension = "sqlite3";
    private const string PersistentStorageFileName = "storage.ide";
 
    private static bool TryInitializeLibraries() => s_initialized.Value;
 
    private static readonly Lazy<bool> s_initialized = new(TryInitializeLibrariesLazy);
 
    private static bool TryInitializeLibrariesLazy()
    {
        try
        {
            // Necessary to initialize SQLitePCL.
            SQLitePCL.Batteries_V2.Init();
        }
        catch (Exception e) when (e is DllNotFoundException or EntryPointNotFoundException)
        {
            StorageDatabaseLogger.LogException(e);
 
            // In debug also insta fail here.  That way if there is an issue with sqlite (for example with authoring,
            // or with some particular configuration) that get CI coverage that reveals this.
            Debug.Fail("Sqlite failed to load: " + e);
            return false;
        }
 
        return true;
    }
 
    protected override string GetDatabaseFilePath(string workingFolderPath)
    {
        Contract.ThrowIfTrue(string.IsNullOrWhiteSpace(workingFolderPath));
        return Path.Combine(workingFolderPath, StorageExtension, nameof(v2), PersistentStorageFileName);
    }
 
    protected override ValueTask<IChecksummedPersistentStorage?> TryOpenDatabaseAsync(
        SolutionKey solutionKey, string workingFolderPath, string databaseFilePath, IPersistentStorageFaultInjector? faultInjector, CancellationToken cancellationToken)
    {
        if (!TryInitializeLibraries())
        {
            // SQLite is not supported on the current platform
            return new((IChecksummedPersistentStorage?)null);
        }
 
        if (solutionKey.FilePath == null)
            return new(NoOpPersistentStorage.GetOrThrow(solutionKey, Configuration.ThrowOnFailure));
 
        return new(SQLitePersistentStorage.TryCreate(
            solutionKey,
            workingFolderPath,
            databaseFilePath,
            asyncListener,
            faultInjector));
    }
}