protected override ValueTask <IChecksummedPersistentStorage?> TryOpenDatabaseAsync(
            SolutionKey solutionKey, string workingFolderPath, string databaseFilePath, CancellationToken cancellationToken)
        {
            if (!TryInitializeLibraries())
            {
                // SQLite is not supported on the current platform
                return(new((IChecksummedPersistentStorage?)null));
            }

            if (solutionKey.FilePath == null)
            {
                return(new(NoOpPersistentStorage.GetOrThrow(Configuration.ThrowOnFailure)));
            }

            return(new(SQLitePersistentStorage.TryCreate(
                           _connectionPoolService,
                           workingFolderPath,
                           solutionKey.FilePath,
                           databaseFilePath,
                           _asyncListener,
                           _faultInjector)));
        }
        protected override ValueTask <IChecksummedPersistentStorage?> TryOpenDatabaseAsync(
            SolutionKey solutionKey,
            string workingFolderPath,
            string databaseFilePath
            )
        {
            if (!TryInitializeLibraries())
            {
                // SQLite is not supported on the current platform
                return(new((IChecksummedPersistentStorage?)null));
            }

            Contract.ThrowIfNull(solutionKey.FilePath);
            return(new(
                       SQLitePersistentStorage.TryCreate(
                           _connectionPoolService,
                           workingFolderPath,
                           solutionKey.FilePath,
                           databaseFilePath,
                           _faultInjector
                           )
                       ));
        }
            public Accessor(SQLitePersistentStorage storage)
            {
                var main       = Database.Main.GetName();
                var writeCache = Database.WriteCache.GetName();

                var dataTableName = this.Table switch
                {
                    Table.Solution => SolutionDataTableName,
                    Table.Project => ProjectDataTableName,
                    Table.Document => DocumentDataTableName,
                    _ => throw ExceptionUtilities.UnexpectedValue(this.Table),
                };

                Storage = storage;
                _select_rowid_from_main_table_where_0 =
                    $@"select rowid from {main}.{dataTableName} where ""{DataIdColumnName}"" = ?";
                _select_rowid_from_writecache_table_where_0 =
                    $@"select rowid from {writeCache}.{dataTableName} where ""{DataIdColumnName}"" = ?";
                _insert_or_replace_into_writecache_table_values_0_1_2 =
                    $@"insert or replace into {writeCache}.{dataTableName}(""{DataIdColumnName}"",""{ChecksumColumnName}"",""{DataColumnName}"") values (?,?,?)";
                _delete_from_writecache_table = $"delete from {writeCache}.{dataTableName};";
                _insert_or_replace_into_main_table_select_star_from_writecache_table =
                    $"insert or replace into {main}.{dataTableName} select * from {writeCache}.{dataTableName};";
            }
Beispiel #4
0
 public SolutionAccessor(SQLitePersistentStorage storage) : base(storage)
 {
 }
Beispiel #5
0
 public PooledConnection(SQLitePersistentStorage storage, SqlConnection sqlConnection)
 {
     _storage   = storage;
     Connection = sqlConnection;
 }
Beispiel #6
0
        private bool BulkPopulateProjectIdsWorker(SqlConnection connection, Project project)
        {
            // First, in bulk, get string-ids for all the paths and names for the project and documents.
            if (!AddIndividualProjectAndDocumentComponentIds())
            {
                return(false);
            }

            // Now, ensure we have the project id known locally.  We cannot do this until we've
            // gotten all the IDs for the individual project components as the project ID is built
            // from a compound key using the IDs for the project's FilePath and Name.
            //
            // If this fails for any reason, we can't proceed.
            var projectId = TryGetProjectId(connection, (ProjectKey)project);

            if (projectId == null)
            {
                return(false);
            }

            // Finally, in bulk, determine the final DB IDs for all our documents. We cannot do
            // this until we have the project-id as the document IDs are built from a compound
            // ID including the project-id.
            return(AddDocumentIds());

            // Local functions below.

            // Use local functions so that other members of this class don't accidentally use these.
            // There are invariants in the context of BulkPopulateProjectIdsWorker that these functions
            // can depend on.
            bool AddIndividualProjectAndDocumentComponentIds()
            {
                var stringsToAdd = new HashSet <string>();

                AddIfUnknownId(project.FilePath, stringsToAdd);
                AddIfUnknownId(project.Name, stringsToAdd);

                foreach (var document in project.Documents)
                {
                    AddIfUnknownId(document.FilePath, stringsToAdd);
                    AddIfUnknownId(document.Name, stringsToAdd);
                }

                return(AddStrings(stringsToAdd));
            }

            bool AddStrings(HashSet <string> stringsToAdd)
            {
                if (stringsToAdd.Count > 0)
                {
                    using var idToString = s_dictionaryPool.GetPooledObject();
                    try
                    {
                        connection.RunInTransaction(s_insertAllStrings, (this, stringsToAdd, connection, idToString.Object));
                    }
                    catch (SqlException ex) when(ex.Result == Result.CONSTRAINT)
                    {
                        // Constraint exceptions are possible as we may be trying bulk insert
                        // strings while some other thread/process does the same.
                        return(false);
                    }
                    catch (Exception ex)
                    {
                        // Something failed. Log the issue, and let the caller know we should stop
                        // with the bulk population.
                        StorageDatabaseLogger.LogException(ex);
                        return(false);
                    }

                    // We succeeded inserting all the strings.  Ensure our local cache has all the
                    // values we added.
                    foreach (var kvp in idToString.Object)
                    {
                        _stringToIdMap[kvp.Value] = kvp.Key;
                    }
                }

                return(true);
            }

            bool AddDocumentIds()
            {
                var stringsToAdd = new HashSet <string>();

                foreach (var document in project.Documents)
                {
                    // Produce the string like "projId-docPathId-docNameId" so that we can get a
                    // unique ID for it.
                    AddIfUnknownId(GetDocumentIdString(document), stringsToAdd);
                }

                // Ensure we have unique IDs for all these document string ids.  If we fail to
                // bulk import these strings, we can't proceed.
                if (!AddStrings(stringsToAdd))
                {
                    return(false);
                }

                foreach (var document in project.Documents)
                {
                    // Get the integral ID for this document.  It's safe to directly index into
                    // the map as we just successfully added these strings to the DB.
                    var id = _stringToIdMap[GetDocumentIdString(document)];
                    _documentIdToIdMap.TryAdd(document.Id, id);
                }

                return(true);
            }

            string GetDocumentIdString(Document document)
            {
                // We should always be able to index directly into these maps.  This function is only
                // ever called after we called AddIndividualProjectAndDocumentComponentIds.
                var documentPathId = _stringToIdMap[document.FilePath];
                var documentNameId = _stringToIdMap[document.Name];

                var documentIdString = SQLitePersistentStorage.GetDocumentIdString(
                    projectId.Value, documentPathId, documentNameId);

                return(documentIdString);
            }

            void AddIfUnknownId(string?value, HashSet <string> stringsToAdd)
            {
                // Null strings are not supported at all.  Just ignore these. Any read/writes
                // to null values will fail and will return 'false/null' to indicate failure
                // (which is part of the documented contract of the persistence layer API).
                if (value == null)
                {
                    return;
                }

                if (!_stringToIdMap.TryGetValue(value, out var id))
                {
                    stringsToAdd.Add(value);
                }
                else
                {
                    // We did know about this string.  However, we want to ensure that the
                    // actual string instance we're pointing to is the one produced by the
                    // rest of the workspace, and not by the database.  This way we don't
                    // end up having tons of duplicate strings in the storage service.
                    //
                    // So overwrite whatever we have so far in the table so we can release
                    // the DB strings.
                    _stringToIdMap[value] = id;
                }
            }
        }