• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "SQLiteDatabase.h"
29 
30 #include "DatabaseAuthorizer.h"
31 #include "Logging.h"
32 #include "SQLiteFileSystem.h"
33 #include "SQLiteStatement.h"
34 
35 #include <sqlite3.h>
36 
37 namespace WebCore {
38 
39 const int SQLResultDone = SQLITE_DONE;
40 const int SQLResultError = SQLITE_ERROR;
41 const int SQLResultOk = SQLITE_OK;
42 const int SQLResultRow = SQLITE_ROW;
43 const int SQLResultSchema = SQLITE_SCHEMA;
44 const int SQLResultFull = SQLITE_FULL;
45 
46 
SQLiteDatabase()47 SQLiteDatabase::SQLiteDatabase()
48     : m_db(0)
49     , m_pageSize(-1)
50     , m_transactionInProgress(false)
51     , m_openingThread(0)
52 {
53 }
54 
~SQLiteDatabase()55 SQLiteDatabase::~SQLiteDatabase()
56 {
57     close();
58 }
59 
open(const String & filename)60 bool SQLiteDatabase::open(const String& filename)
61 {
62     close();
63 
64     m_lastError = SQLiteFileSystem::openDatabase(filename, &m_db);
65     if (m_lastError != SQLITE_OK) {
66         LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
67             sqlite3_errmsg(m_db));
68         sqlite3_close(m_db);
69         m_db = 0;
70         return false;
71     }
72 
73     if (isOpen())
74         m_openingThread = currentThread();
75 
76     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
77         LOG_ERROR("SQLite database could not set temp_store to memory");
78 
79     return isOpen();
80 }
81 
close()82 void SQLiteDatabase::close()
83 {
84     if (m_db) {
85         // FIXME: This is being called on themain thread during JS GC. <rdar://problem/5739818>
86         // ASSERT(currentThread() == m_openingThread);
87         sqlite3_close(m_db);
88         m_db = 0;
89     }
90 
91     m_openingThread = 0;
92 }
93 
setFullsync(bool fsync)94 void SQLiteDatabase::setFullsync(bool fsync)
95 {
96     if (fsync)
97         executeCommand("PRAGMA fullfsync = 1;");
98     else
99         executeCommand("PRAGMA fullfsync = 0;");
100 }
101 
maximumSize()102 int64_t SQLiteDatabase::maximumSize()
103 {
104     MutexLocker locker(m_authorizerLock);
105     enableAuthorizer(false);
106 
107     SQLiteStatement statement(*this, "PRAGMA max_page_count");
108     int64_t size = statement.getColumnInt64(0) * pageSize();
109 
110     enableAuthorizer(true);
111     return size;
112 }
113 
setMaximumSize(int64_t size)114 void SQLiteDatabase::setMaximumSize(int64_t size)
115 {
116     if (size < 0)
117         size = 0;
118 
119     int currentPageSize = pageSize();
120 
121     ASSERT(currentPageSize);
122     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
123 
124     MutexLocker locker(m_authorizerLock);
125     enableAuthorizer(false);
126 
127     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
128     statement.prepare();
129     if (statement.step() != SQLResultRow)
130         LOG_ERROR("Failed to set maximum size of database to %lli bytes", size);
131 
132     enableAuthorizer(true);
133 
134 }
135 
pageSize()136 int SQLiteDatabase::pageSize()
137 {
138     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
139     // we can cache the value for future use
140     if (m_pageSize == -1) {
141         MutexLocker locker(m_authorizerLock);
142         enableAuthorizer(false);
143 
144         SQLiteStatement statement(*this, "PRAGMA page_size");
145         m_pageSize = statement.getColumnInt(0);
146 
147         enableAuthorizer(true);
148     }
149 
150     return m_pageSize;
151 }
152 
freeSpaceSize()153 int64_t SQLiteDatabase::freeSpaceSize()
154 {
155     MutexLocker locker(m_authorizerLock);
156     enableAuthorizer(false);
157     // Note: freelist_count was added in SQLite 3.4.1.
158     SQLiteStatement statement(*this, "PRAGMA freelist_count");
159     int64_t size = statement.getColumnInt64(0) * pageSize();
160 
161     enableAuthorizer(true);
162     return size;
163 }
164 
setSynchronous(SynchronousPragma sync)165 void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
166 {
167     executeCommand(String::format("PRAGMA synchronous = %i", sync));
168 }
169 
setBusyTimeout(int ms)170 void SQLiteDatabase::setBusyTimeout(int ms)
171 {
172     if (m_db)
173         sqlite3_busy_timeout(m_db, ms);
174     else
175         LOG(SQLDatabase, "BusyTimeout set on non-open database");
176 }
177 
setBusyHandler(int (* handler)(void *,int))178 void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int))
179 {
180     if (m_db)
181         sqlite3_busy_handler(m_db, handler, NULL);
182     else
183         LOG(SQLDatabase, "Busy handler set on non-open database");
184 }
185 
executeCommand(const String & sql)186 bool SQLiteDatabase::executeCommand(const String& sql)
187 {
188     return SQLiteStatement(*this, sql).executeCommand();
189 }
190 
returnsAtLeastOneResult(const String & sql)191 bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql)
192 {
193     return SQLiteStatement(*this, sql).returnsAtLeastOneResult();
194 }
195 
tableExists(const String & tablename)196 bool SQLiteDatabase::tableExists(const String& tablename)
197 {
198     if (!isOpen())
199         return false;
200 
201     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
202 
203     SQLiteStatement sql(*this, statement);
204     sql.prepare();
205     return sql.step() == SQLITE_ROW;
206 }
207 
clearAllTables()208 void SQLiteDatabase::clearAllTables()
209 {
210     String query = "SELECT name FROM sqlite_master WHERE type='table';";
211     Vector<String> tables;
212     if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) {
213         LOG(SQLDatabase, "Unable to retrieve list of tables from database");
214         return;
215     }
216 
217     for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
218         if (*table == "sqlite_sequence")
219             continue;
220         if (!executeCommand("DROP TABLE " + *table))
221             LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data());
222     }
223 }
224 
runVacuumCommand()225 void SQLiteDatabase::runVacuumCommand()
226 {
227     if (!executeCommand("VACUUM;"))
228         LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
229 }
230 
lastInsertRowID()231 int64_t SQLiteDatabase::lastInsertRowID()
232 {
233     if (!m_db)
234         return 0;
235     return sqlite3_last_insert_rowid(m_db);
236 }
237 
lastChanges()238 int SQLiteDatabase::lastChanges()
239 {
240     if (!m_db)
241         return 0;
242     return sqlite3_changes(m_db);
243 }
244 
lastError()245 int SQLiteDatabase::lastError()
246 {
247     return m_db ? sqlite3_errcode(m_db) : SQLITE_ERROR;
248 }
249 
lastErrorMsg()250 const char* SQLiteDatabase::lastErrorMsg()
251 {
252     return sqlite3_errmsg(m_db);
253 }
254 
authorizerFunction(void * userData,int actionCode,const char * parameter1,const char * parameter2,const char *,const char *)255 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
256 {
257     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
258     ASSERT(auth);
259 
260     switch (actionCode) {
261         case SQLITE_CREATE_INDEX:
262             return auth->createIndex(parameter1, parameter2);
263         case SQLITE_CREATE_TABLE:
264             return auth->createTable(parameter1);
265         case SQLITE_CREATE_TEMP_INDEX:
266             return auth->createTempIndex(parameter1, parameter2);
267         case SQLITE_CREATE_TEMP_TABLE:
268             return auth->createTempTable(parameter1);
269         case SQLITE_CREATE_TEMP_TRIGGER:
270             return auth->createTempTrigger(parameter1, parameter2);
271         case SQLITE_CREATE_TEMP_VIEW:
272             return auth->createTempView(parameter1);
273         case SQLITE_CREATE_TRIGGER:
274             return auth->createTrigger(parameter1, parameter2);
275         case SQLITE_CREATE_VIEW:
276             return auth->createView(parameter1);
277         case SQLITE_DELETE:
278             return auth->allowDelete(parameter1);
279         case SQLITE_DROP_INDEX:
280             return auth->dropIndex(parameter1, parameter2);
281         case SQLITE_DROP_TABLE:
282             return auth->dropTable(parameter1);
283         case SQLITE_DROP_TEMP_INDEX:
284             return auth->dropTempIndex(parameter1, parameter2);
285         case SQLITE_DROP_TEMP_TABLE:
286             return auth->dropTempTable(parameter1);
287         case SQLITE_DROP_TEMP_TRIGGER:
288             return auth->dropTempTrigger(parameter1, parameter2);
289         case SQLITE_DROP_TEMP_VIEW:
290             return auth->dropTempView(parameter1);
291         case SQLITE_DROP_TRIGGER:
292             return auth->dropTrigger(parameter1, parameter2);
293         case SQLITE_DROP_VIEW:
294             return auth->dropView(parameter1);
295         case SQLITE_INSERT:
296             return auth->allowInsert(parameter1);
297         case SQLITE_PRAGMA:
298             return auth->allowPragma(parameter1, parameter2);
299         case SQLITE_READ:
300             return auth->allowRead(parameter1, parameter2);
301         case SQLITE_SELECT:
302             return auth->allowSelect();
303         case SQLITE_TRANSACTION:
304             return auth->allowTransaction();
305         case SQLITE_UPDATE:
306             return auth->allowUpdate(parameter1, parameter2);
307         case SQLITE_ATTACH:
308             return auth->allowAttach(parameter1);
309         case SQLITE_DETACH:
310             return auth->allowDetach(parameter1);
311         case SQLITE_ALTER_TABLE:
312             return auth->allowAlterTable(parameter1, parameter2);
313         case SQLITE_REINDEX:
314             return auth->allowReindex(parameter1);
315 #if SQLITE_VERSION_NUMBER >= 3003013
316         case SQLITE_ANALYZE:
317             return auth->allowAnalyze(parameter1);
318         case SQLITE_CREATE_VTABLE:
319             return auth->createVTable(parameter1, parameter2);
320         case SQLITE_DROP_VTABLE:
321             return auth->dropVTable(parameter1, parameter2);
322         case SQLITE_FUNCTION:
323             return auth->allowFunction(parameter2);
324 #endif
325         default:
326             ASSERT_NOT_REACHED();
327             return SQLAuthDeny;
328     }
329 }
330 
setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)331 void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)
332 {
333     if (!m_db) {
334         LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
335         ASSERT_NOT_REACHED();
336         return;
337     }
338 
339     MutexLocker locker(m_authorizerLock);
340 
341     m_authorizer = auth;
342 
343     enableAuthorizer(true);
344 }
345 
enableAuthorizer(bool enable)346 void SQLiteDatabase::enableAuthorizer(bool enable)
347 {
348     if (m_authorizer && enable)
349         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
350     else
351         sqlite3_set_authorizer(m_db, NULL, 0);
352 }
353 
lock()354 void SQLiteDatabase::lock()
355 {
356     m_lockingMutex.lock();
357 }
358 
unlock()359 void SQLiteDatabase::unlock()
360 {
361     m_lockingMutex.unlock();
362 }
363 
isAutoCommitOn() const364 bool SQLiteDatabase::isAutoCommitOn() const
365 {
366     return sqlite3_get_autocommit(m_db);
367 }
368 
369 } // namespace WebCore
370