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}