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};"; }
public SolutionAccessor(SQLitePersistentStorage storage) : base(storage) { }
public PooledConnection(SQLitePersistentStorage storage, SqlConnection sqlConnection) { _storage = storage; Connection = sqlConnection; }
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; } } }