001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.database; 019 020import java.sql.Connection; 021import java.sql.SQLException; 022import java.util.Collection; 023import java.util.Date; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.logging.Level; 029import java.util.logging.Logger; 030 031import org.syncany.config.Config; 032import org.syncany.database.ChunkEntry.ChunkChecksum; 033import org.syncany.database.FileContent.FileChecksum; 034import org.syncany.database.FileVersion.FileType; 035import org.syncany.database.MultiChunkEntry.MultiChunkId; 036import org.syncany.database.PartialFileHistory.FileHistoryId; 037import org.syncany.database.dao.ApplicationSqlDao; 038import org.syncany.database.dao.ChunkSqlDao; 039import org.syncany.database.dao.DatabaseVersionSqlDao; 040import org.syncany.database.dao.FileContentSqlDao; 041import org.syncany.database.dao.FileHistorySqlDao; 042import org.syncany.database.dao.FileVersionSqlDao; 043import org.syncany.database.dao.MultiChunkSqlDao; 044import org.syncany.operations.cleanup.CleanupOperationOptions.TimeUnit; 045import org.syncany.operations.down.DatabaseBranch; 046import org.syncany.plugins.transfer.files.DatabaseRemoteFile; 047 048/** 049 * Represents the single entry point for all SQL database queries. 050 * 051 * <p>This class combines all specific SQL database data access objects (DAOs) into 052 * a single class, and forwards all method calls to the responsible DAO. 053 * 054 * @see ApplicationSqlDao 055 * @see ChunkSqlDao 056 * @see FileContentSqlDao 057 * @see FileVersionSqlDao 058 * @see FileHistorySqlDao 059 * @see MultiChunkSqlDao 060 * @see DatabaseVersionSqlDao 061 * @author Philipp C. Heckel (philipp.heckel@gmail.com) 062 */ 063public class SqlDatabase { 064 protected static final Logger logger = Logger.getLogger(SqlDatabase.class.getSimpleName()); 065 066 protected Connection connection; 067 protected ApplicationSqlDao applicationDao; 068 protected ChunkSqlDao chunkDao; 069 protected FileContentSqlDao fileContentDao; 070 protected FileVersionSqlDao fileVersionDao; 071 protected FileHistorySqlDao fileHistoryDao; 072 protected MultiChunkSqlDao multiChunkDao; 073 protected DatabaseVersionSqlDao databaseVersionDao; 074 075 public SqlDatabase(Config config) { 076 this(config, false); 077 } 078 079 public SqlDatabase(Config config, boolean readOnly) { 080 this.connection = config.createDatabaseConnection(readOnly); 081 this.applicationDao = new ApplicationSqlDao(connection); 082 this.chunkDao = new ChunkSqlDao(connection); 083 this.fileContentDao = new FileContentSqlDao(connection); 084 this.fileVersionDao = new FileVersionSqlDao(connection); 085 this.fileHistoryDao = new FileHistorySqlDao(connection, fileVersionDao); 086 this.multiChunkDao = new MultiChunkSqlDao(connection); 087 this.databaseVersionDao = new DatabaseVersionSqlDao(connection, chunkDao, fileContentDao, fileVersionDao, fileHistoryDao, multiChunkDao); 088 089 } 090 091 // General 092 093 public void commit() throws SQLException { 094 connection.commit(); 095 } 096 097 @Override 098 public void finalize() { 099 try { 100 if (!connection.isClosed()) { 101 connection.commit(); 102 connection.close(); 103 } 104 } 105 catch (SQLException e) { 106 logger.log(Level.WARNING, "Failed to close database connection. Possible resource leak.", e); 107 } 108 } 109 110 public void rollback() throws SQLException { 111 connection.rollback(); 112 } 113 114 public void removeUnreferencedDatabaseEntities() { 115 try { 116 removeUnreferencedFileHistories(); 117 removeUnreferencedFileContents(); 118 removeUnreferencedMultiChunks(); 119 removeUnreferencedChunks(); 120 121 removeEmptyDatabaseVersionHeaders(); 122 } 123 catch (SQLException e) { 124 throw new RuntimeException(e); 125 } 126 } 127 128 // Application 129 130 public void writeKnownRemoteDatabases(List<DatabaseRemoteFile> remoteDatabases) throws SQLException { 131 applicationDao.writeKnownRemoteDatabases(remoteDatabases); 132 } 133 134 public List<DatabaseRemoteFile> getKnownDatabases() { 135 return applicationDao.getKnownDatabases(); 136 } 137 138 public VectorClock getHighestKnownDatabaseFilenameNumbers() { 139 return applicationDao.getHighestKnownDatabaseFilenameNumbers(); 140 } 141 142 public void removeKnownDatabases() { 143 applicationDao.removeKnownDatabases(); 144 } 145 146 public Long getCleanupNumber() { 147 return applicationDao.getCleanupNumber(); 148 } 149 150 public Long getCleanupTime() { 151 return applicationDao.getCleanupTime(); 152 } 153 154 public void writeCleanupNumber(long cleanupNumber) { 155 applicationDao.writeCleanupNumber(cleanupNumber); 156 } 157 158 public void writeCleanupTime(long cleanupTime) { 159 applicationDao.writeCleanupTime(cleanupTime); 160 } 161 162 public void deleteAll() { 163 applicationDao.deleteAll(); 164 } 165 166 public void shutdown() { 167 applicationDao.shutdown(); 168 } 169 170 // Database version 171 172 public Iterator<DatabaseVersion> getDirtyDatabaseVersions() { 173 return databaseVersionDao.getDirtyDatabaseVersions(); 174 } 175 176 public Iterator<DatabaseVersion> getDatabaseVersionsTo(String machineName, long maxLocalClientVersion) { 177 return databaseVersionDao.getDatabaseVersionsTo(machineName, maxLocalClientVersion); 178 } 179 180 public Iterator<DatabaseVersion> getLastDatabaseVersions(int maxDatabaseVersionCount, int startDatabaseVersionIndex, int maxFileHistoryCount) { 181 return databaseVersionDao.getLastDatabaseVersions(maxDatabaseVersionCount, startDatabaseVersionIndex, maxFileHistoryCount); 182 } 183 184 public DatabaseVersionHeader getLastDatabaseVersionHeader() { 185 return databaseVersionDao.getLastDatabaseVersionHeader(); 186 } 187 188 public DatabaseBranch getLocalDatabaseBranch() { 189 return databaseVersionDao.getLocalDatabaseBranch(); 190 } 191 192 public List<DatabaseVersionHeader> getNonEmptyDatabaseVersionHeaders() { 193 return databaseVersionDao.getNonEmptyDatabaseVersionHeaders(); 194 } 195 196 public long writeDatabaseVersion(DatabaseVersion databaseVersion) { 197 return databaseVersionDao.writeDatabaseVersion(databaseVersion); 198 } 199 200 public void markDatabaseVersionDirty(VectorClock vectorClock) { 201 databaseVersionDao.markDatabaseVersionDirty(vectorClock); 202 } 203 204 public void removeDirtyDatabaseVersions(long newDatabaseVersionId) { 205 databaseVersionDao.removeDirtyDatabaseVersions(newDatabaseVersionId); 206 } 207 208 public void removeEmptyDatabaseVersionHeaders() { 209 databaseVersionDao.removeEmptyDatabaseVersionHeaders(); 210 } 211 212 public Long getMaxDirtyVectorClock(String machineName) { 213 return databaseVersionDao.getMaxDirtyVectorClock(machineName); 214 } 215 216 // File History 217 218 @Deprecated 219 public Map<FileHistoryId, PartialFileHistory> getFileHistoriesWithFileVersions() { 220 // TODO [medium] Note: This returns the full database. Don't use this! 221 return fileHistoryDao.getFileHistoriesWithFileVersions(); 222 } 223 224 public Map<FileHistoryId, PartialFileHistory> getFileHistories(List<FileHistoryId> fileHistoryIds) { 225 return fileHistoryDao.getFileHistories(fileHistoryIds); 226 } 227 228 public List<PartialFileHistory> getFileHistoriesWithLastVersion() { 229 return fileHistoryDao.getFileHistoriesWithLastVersion(); 230 } 231 232 public Collection<PartialFileHistory> getFileHistoriesWithLastVersionByChecksumSizeAndModifiedDate(String checksum, long size, Date modifiedDate) { 233 return fileHistoryDao.getFileHistoriesByChecksumSizeAndModifiedDate(checksum, size, modifiedDate); 234 } 235 236 public PartialFileHistory getFileHistoriesWithLastVersionByPath(String path) { 237 return fileHistoryDao.getFileHistoryWithLastVersionByPath(path); 238 } 239 240 private void removeUnreferencedFileHistories() throws SQLException { 241 fileHistoryDao.removeUnreferencedFileHistories(); 242 } 243 244 public FileHistoryId expandFileHistoryId(FileHistoryId fileHistoryId) { 245 return fileHistoryDao.expandFileHistoryId(fileHistoryId); 246 } 247 248 // File Version 249 250 public Map<String, FileVersion> getCurrentFileTree() { 251 return fileVersionDao.getCurrentFileTree(); 252 } 253 254 public void removeSmallerOrEqualFileVersions(Map<FileHistoryId, FileVersion> purgeFileVersions) throws SQLException { 255 fileVersionDao.removeFileVersions(purgeFileVersions); 256 } 257 258 public void removeFileVersions(Map<FileHistoryId, List<FileVersion>> purgeFileVersions) throws SQLException { 259 fileVersionDao.removeSpecificFileVersions(purgeFileVersions); 260 } 261 262 public List<FileVersion> getFileList(String pathExpression, Date date, boolean fileHistoryId, boolean recursive, boolean deleted, 263 Set<FileType> fileTypes) { 264 265 return fileVersionDao.getFileList(pathExpression, date, fileHistoryId, recursive, deleted, fileTypes); 266 } 267 268 public List<FileVersion> getFileHistory(FileHistoryId fileHistoryId) { 269 return fileVersionDao.getFileHistory(fileHistoryId); 270 } 271 272 public Map<FileHistoryId, List<FileVersion>> getFileHistoriesToPurgeInInterval(long beginTimestamp, long endTimestamp, TimeUnit timeUnit) { 273 return fileVersionDao.getFileHistoriesToPurgeInInterval(beginTimestamp, endTimestamp, timeUnit); 274 } 275 276 public Map<FileHistoryId, List<FileVersion>> getFileHistoriesToPurgeBefore(long timestamp) { 277 return fileVersionDao.getFileHistoriesToPurgeBefore(timestamp); 278 } 279 280 public Map<FileHistoryId, FileVersion> getDeletedFileVersionsBefore(long timestamp) { 281 return fileVersionDao.getDeletedFileVersionsBefore(timestamp); 282 } 283 284 public FileVersion getFileVersion(FileHistoryId fileHistoryId, long version) { 285 return fileVersionDao.getFileVersion(fileHistoryId, version); 286 } 287 288 // Multi Chunk 289 290 public List<MultiChunkId> getMultiChunkIds(FileChecksum fileChecksum) { 291 return multiChunkDao.getMultiChunkIds(fileChecksum); 292 } 293 294 public MultiChunkId getMultiChunkId(ChunkChecksum chunkChecksum) { 295 return multiChunkDao.getMultiChunkId(chunkChecksum); 296 } 297 298 public Map<ChunkChecksum, MultiChunkId> getMultiChunkIdsByChecksums(List<ChunkChecksum> chunkChecksums) { 299 return multiChunkDao.getMultiChunkIdsByChecksums(chunkChecksums); 300 } 301 302 public List<MultiChunkId> getDirtyMultiChunkIds() { 303 return multiChunkDao.getDirtyMultiChunkIds(); 304 } 305 306 public Map<MultiChunkId, MultiChunkEntry> getUnusedMultiChunks() { 307 return multiChunkDao.getUnusedMultiChunks(); 308 } 309 310 private void removeUnreferencedMultiChunks() throws SQLException { 311 multiChunkDao.removeUnreferencedMultiChunks(); 312 } 313 314 public void writeMuddyMultiChunks(Map<DatabaseVersionHeader, Collection<MultiChunkEntry>> muddyMultiChunks) throws SQLException { 315 multiChunkDao.writeMuddyMultiChunks(muddyMultiChunks); 316 } 317 318 public void removeNonMuddyMultiChunks() throws SQLException { 319 multiChunkDao.removeNonMuddyMultiChunks(); 320 } 321 322 // Chunk 323 324 protected Map<ChunkChecksum, ChunkEntry> getChunks(VectorClock vectorClock) { 325 return chunkDao.getChunks(vectorClock); 326 } 327 328 public ChunkEntry getChunk(ChunkChecksum chunkChecksum) { 329 return chunkDao.getChunk(chunkChecksum); 330 } 331 332 private void removeUnreferencedChunks() { 333 chunkDao.removeUnreferencedChunks(); 334 } 335 336 // File Content 337 338 public FileContent getFileContent(FileChecksum fileChecksum, boolean includeChunkChecksums) { 339 return fileContentDao.getFileContent(fileChecksum, includeChunkChecksums); 340 } 341 342 private void removeUnreferencedFileContents() throws SQLException { 343 fileContentDao.removeUnreferencedFileContents(); 344 } 345 346}