• 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 "modules/webdatabase/sqlite/SQLiteDatabase.h"
29 
30 #include <sqlite3.h>
31 #include "platform/Logging.h"
32 #include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
33 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
34 #include "modules/webdatabase/DatabaseAuthorizer.h"
35 
36 namespace blink {
37 
38 const int SQLResultDone = SQLITE_DONE;
39 const int SQLResultOk = SQLITE_OK;
40 const int SQLResultRow = SQLITE_ROW;
41 const int SQLResultFull = SQLITE_FULL;
42 const int SQLResultInterrupt = SQLITE_INTERRUPT;
43 const int SQLResultConstraint = SQLITE_CONSTRAINT;
44 
45 static const char notOpenErrorMessage[] = "database is not open";
46 
SQLiteDatabase()47 SQLiteDatabase::SQLiteDatabase()
48     : m_db(0)
49     , m_pageSize(-1)
50     , m_transactionInProgress(false)
51     , m_sharable(false)
52     , m_openingThread(0)
53     , m_openError(SQLITE_ERROR)
54     , m_openErrorMessage()
55     , m_lastChangesCount(0)
56 {
57 }
58 
~SQLiteDatabase()59 SQLiteDatabase::~SQLiteDatabase()
60 {
61     close();
62 }
63 
open(const String & filename,bool forWebSQLDatabase)64 bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
65 {
66     close();
67 
68     m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
69     if (m_openError != SQLITE_OK) {
70         m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
71         WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
72             m_openErrorMessage.data());
73         sqlite3_close(m_db);
74         m_db = 0;
75         return false;
76     }
77 
78     m_openError = sqlite3_extended_result_codes(m_db, 1);
79     if (m_openError != SQLITE_OK) {
80         m_openErrorMessage = sqlite3_errmsg(m_db);
81         WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
82         sqlite3_close(m_db);
83         m_db = 0;
84         return false;
85     }
86 
87     if (isOpen())
88         m_openingThread = currentThread();
89     else
90         m_openErrorMessage = "sqlite_open returned null";
91 
92     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
93         WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
94 
95     return isOpen();
96 }
97 
close()98 void SQLiteDatabase::close()
99 {
100     if (m_db) {
101         // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
102         // ASSERT(currentThread() == m_openingThread);
103         sqlite3* db = m_db;
104         {
105             MutexLocker locker(m_databaseClosingMutex);
106             m_db = 0;
107         }
108         sqlite3_close(db);
109     }
110 
111     m_openingThread = 0;
112     m_openError = SQLITE_ERROR;
113     m_openErrorMessage = CString();
114 }
115 
setMaximumSize(int64_t size)116 void SQLiteDatabase::setMaximumSize(int64_t size)
117 {
118     if (size < 0)
119         size = 0;
120 
121     int currentPageSize = pageSize();
122 
123     ASSERT(currentPageSize || !m_db);
124     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
125 
126     MutexLocker locker(m_authorizerLock);
127     enableAuthorizer(false);
128 
129     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
130     statement.prepare();
131     if (statement.step() != SQLResultRow)
132 #if OS(WIN)
133         WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
134 #else
135         WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
136 #endif
137 
138     enableAuthorizer(true);
139 
140 }
141 
pageSize()142 int SQLiteDatabase::pageSize()
143 {
144     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
145     // we can cache the value for future use
146     if (m_pageSize == -1) {
147         MutexLocker locker(m_authorizerLock);
148         enableAuthorizer(false);
149 
150         SQLiteStatement statement(*this, "PRAGMA page_size");
151         m_pageSize = statement.getColumnInt(0);
152 
153         enableAuthorizer(true);
154     }
155 
156     return m_pageSize;
157 }
158 
freeSpaceSize()159 int64_t SQLiteDatabase::freeSpaceSize()
160 {
161     int64_t freelistCount = 0;
162 
163     {
164         MutexLocker locker(m_authorizerLock);
165         enableAuthorizer(false);
166         // Note: freelist_count was added in SQLite 3.4.1.
167         SQLiteStatement statement(*this, "PRAGMA freelist_count");
168         freelistCount = statement.getColumnInt64(0);
169         enableAuthorizer(true);
170     }
171 
172     return freelistCount * pageSize();
173 }
174 
totalSize()175 int64_t SQLiteDatabase::totalSize()
176 {
177     int64_t pageCount = 0;
178 
179     {
180         MutexLocker locker(m_authorizerLock);
181         enableAuthorizer(false);
182         SQLiteStatement statement(*this, "PRAGMA page_count");
183         pageCount = statement.getColumnInt64(0);
184         enableAuthorizer(true);
185     }
186 
187     return pageCount * pageSize();
188 }
189 
setBusyTimeout(int ms)190 void SQLiteDatabase::setBusyTimeout(int ms)
191 {
192     if (m_db)
193         sqlite3_busy_timeout(m_db, ms);
194     else
195         WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
196 }
197 
executeCommand(const String & sql)198 bool SQLiteDatabase::executeCommand(const String& sql)
199 {
200     return SQLiteStatement(*this, sql).executeCommand();
201 }
202 
tableExists(const String & tablename)203 bool SQLiteDatabase::tableExists(const String& tablename)
204 {
205     if (!isOpen())
206         return false;
207 
208     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
209 
210     SQLiteStatement sql(*this, statement);
211     sql.prepare();
212     return sql.step() == SQLITE_ROW;
213 }
214 
runVacuumCommand()215 int SQLiteDatabase::runVacuumCommand()
216 {
217     if (!executeCommand("VACUUM;"))
218         WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
219     return lastError();
220 }
221 
runIncrementalVacuumCommand()222 int SQLiteDatabase::runIncrementalVacuumCommand()
223 {
224     MutexLocker locker(m_authorizerLock);
225     enableAuthorizer(false);
226 
227     if (!executeCommand("PRAGMA incremental_vacuum"))
228         WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
229 
230     enableAuthorizer(true);
231     return lastError();
232 }
233 
lastInsertRowID()234 int64_t SQLiteDatabase::lastInsertRowID()
235 {
236     if (!m_db)
237         return 0;
238     return sqlite3_last_insert_rowid(m_db);
239 }
240 
updateLastChangesCount()241 void SQLiteDatabase::updateLastChangesCount()
242 {
243     if (!m_db)
244         return;
245 
246     m_lastChangesCount = sqlite3_total_changes(m_db);
247 }
248 
lastChanges()249 int SQLiteDatabase::lastChanges()
250 {
251     if (!m_db)
252         return 0;
253 
254     return sqlite3_total_changes(m_db) - m_lastChangesCount;
255 }
256 
lastError()257 int SQLiteDatabase::lastError()
258 {
259     return m_db ? sqlite3_errcode(m_db) : m_openError;
260 }
261 
lastErrorMsg()262 const char* SQLiteDatabase::lastErrorMsg()
263 {
264     if (m_db)
265         return sqlite3_errmsg(m_db);
266     return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
267 }
268 
authorizerFunction(void * userData,int actionCode,const char * parameter1,const char * parameter2,const char *,const char *)269 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
270 {
271     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
272     ASSERT(auth);
273 
274     switch (actionCode) {
275         case SQLITE_CREATE_INDEX:
276             return auth->createIndex(parameter1, parameter2);
277         case SQLITE_CREATE_TABLE:
278             return auth->createTable(parameter1);
279         case SQLITE_CREATE_TEMP_INDEX:
280             return auth->createTempIndex(parameter1, parameter2);
281         case SQLITE_CREATE_TEMP_TABLE:
282             return auth->createTempTable(parameter1);
283         case SQLITE_CREATE_TEMP_TRIGGER:
284             return auth->createTempTrigger(parameter1, parameter2);
285         case SQLITE_CREATE_TEMP_VIEW:
286             return auth->createTempView(parameter1);
287         case SQLITE_CREATE_TRIGGER:
288             return auth->createTrigger(parameter1, parameter2);
289         case SQLITE_CREATE_VIEW:
290             return auth->createView(parameter1);
291         case SQLITE_DELETE:
292             return auth->allowDelete(parameter1);
293         case SQLITE_DROP_INDEX:
294             return auth->dropIndex(parameter1, parameter2);
295         case SQLITE_DROP_TABLE:
296             return auth->dropTable(parameter1);
297         case SQLITE_DROP_TEMP_INDEX:
298             return auth->dropTempIndex(parameter1, parameter2);
299         case SQLITE_DROP_TEMP_TABLE:
300             return auth->dropTempTable(parameter1);
301         case SQLITE_DROP_TEMP_TRIGGER:
302             return auth->dropTempTrigger(parameter1, parameter2);
303         case SQLITE_DROP_TEMP_VIEW:
304             return auth->dropTempView(parameter1);
305         case SQLITE_DROP_TRIGGER:
306             return auth->dropTrigger(parameter1, parameter2);
307         case SQLITE_DROP_VIEW:
308             return auth->dropView(parameter1);
309         case SQLITE_INSERT:
310             return auth->allowInsert(parameter1);
311         case SQLITE_PRAGMA:
312             return auth->allowPragma(parameter1, parameter2);
313         case SQLITE_READ:
314             return auth->allowRead(parameter1, parameter2);
315         case SQLITE_SELECT:
316             return auth->allowSelect();
317         case SQLITE_TRANSACTION:
318             return auth->allowTransaction();
319         case SQLITE_UPDATE:
320             return auth->allowUpdate(parameter1, parameter2);
321         case SQLITE_ATTACH:
322             return auth->allowAttach(parameter1);
323         case SQLITE_DETACH:
324             return auth->allowDetach(parameter1);
325         case SQLITE_ALTER_TABLE:
326             return auth->allowAlterTable(parameter1, parameter2);
327         case SQLITE_REINDEX:
328             return auth->allowReindex(parameter1);
329 #if SQLITE_VERSION_NUMBER >= 3003013
330         case SQLITE_ANALYZE:
331             return auth->allowAnalyze(parameter1);
332         case SQLITE_CREATE_VTABLE:
333             return auth->createVTable(parameter1, parameter2);
334         case SQLITE_DROP_VTABLE:
335             return auth->dropVTable(parameter1, parameter2);
336         case SQLITE_FUNCTION:
337             return auth->allowFunction(parameter2);
338 #endif
339         default:
340             ASSERT_NOT_REACHED();
341             return SQLAuthDeny;
342     }
343 }
344 
setAuthorizer(DatabaseAuthorizer * auth)345 void SQLiteDatabase::setAuthorizer(DatabaseAuthorizer* auth)
346 {
347     if (!m_db) {
348         WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
349         ASSERT_NOT_REACHED();
350         return;
351     }
352 
353     MutexLocker locker(m_authorizerLock);
354 
355     m_authorizer = auth;
356 
357     enableAuthorizer(true);
358 }
359 
enableAuthorizer(bool enable)360 void SQLiteDatabase::enableAuthorizer(bool enable)
361 {
362     if (m_authorizer && enable)
363         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
364     else
365         sqlite3_set_authorizer(m_db, NULL, 0);
366 }
367 
isAutoCommitOn() const368 bool SQLiteDatabase::isAutoCommitOn() const
369 {
370     return sqlite3_get_autocommit(m_db);
371 }
372 
turnOnIncrementalAutoVacuum()373 bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
374 {
375     SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
376     int autoVacuumMode = statement.getColumnInt(0);
377     int error = lastError();
378 
379     // Check if we got an error while trying to get the value of the auto_vacuum flag.
380     // If we got a SQLITE_BUSY error, then there's probably another transaction in
381     // progress on this database. In this case, keep the current value of the
382     // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
383     // database. If the error is not SQLITE_BUSY, then we probably ran into a more
384     // serious problem and should return false (to log an error message).
385     if (error != SQLITE_ROW)
386         return false;
387 
388     switch (autoVacuumMode) {
389     case AutoVacuumIncremental:
390         return true;
391     case AutoVacuumFull:
392         return executeCommand("PRAGMA auto_vacuum = 2");
393     case AutoVacuumNone:
394     default:
395         if (!executeCommand("PRAGMA auto_vacuum = 2"))
396             return false;
397         runVacuumCommand();
398         error = lastError();
399         return (error == SQLITE_OK);
400     }
401 }
402 
trace(Visitor * visitor)403 void SQLiteDatabase::trace(Visitor* visitor)
404 {
405     visitor->trace(m_authorizer);
406 }
407 
408 } // namespace blink
409