blob: 6691252ac770f5ef3a779ebd3c73bd33ca3fb59f [file] [log] [blame]
[email protected]e7afe2452010-08-22 16:19:131// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]e5ffd0e42009-09-11 21:30:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "app/sql/connection.h"
6
7#include <string.h>
8
9#include "app/sql/statement.h"
10#include "base/file_path.h"
11#include "base/logging.h"
12#include "base/string_util.h"
[email protected]d55194ca2010-03-11 18:25:4513#include "base/utf_string_conversions.h"
[email protected]e33cba42010-08-18 23:37:0314#include "third_party/sqlite/sqlite3.h"
[email protected]e5ffd0e42009-09-11 21:30:5615
[email protected]5b96f3772010-09-28 16:30:5716namespace {
17
18// Spin for up to a second waiting for the lock to clear when setting
19// up the database.
20// TODO(shess): Better story on this. http://crbug.com/56559
21const base::TimeDelta kBusyTimeout = base::TimeDelta::FromSeconds(1);
22
23class ScopedBusyTimeout {
24 public:
25 explicit ScopedBusyTimeout(sqlite3* db)
26 : db_(db) {
27 }
28 ~ScopedBusyTimeout() {
29 sqlite3_busy_timeout(db_, 0);
30 }
31
32 int SetTimeout(base::TimeDelta timeout) {
33 DCHECK_LT(timeout.InMilliseconds(), INT_MAX);
34 return sqlite3_busy_timeout(db_,
35 static_cast<int>(timeout.InMilliseconds()));
36 }
37
38 private:
39 sqlite3* db_;
40};
41
42} // namespace
43
[email protected]e5ffd0e42009-09-11 21:30:5644namespace sql {
45
46bool StatementID::operator<(const StatementID& other) const {
47 if (number_ != other.number_)
48 return number_ < other.number_;
49 return strcmp(str_, other.str_) < 0;
50}
51
[email protected]d4799a32010-09-28 22:54:5852ErrorDelegate::ErrorDelegate() {
53}
54
55ErrorDelegate::~ErrorDelegate() {
56}
57
[email protected]e5ffd0e42009-09-11 21:30:5658Connection::StatementRef::StatementRef()
59 : connection_(NULL),
60 stmt_(NULL) {
61}
62
63Connection::StatementRef::StatementRef(Connection* connection,
64 sqlite3_stmt* stmt)
65 : connection_(connection),
66 stmt_(stmt) {
67 connection_->StatementRefCreated(this);
68}
69
70Connection::StatementRef::~StatementRef() {
71 if (connection_)
72 connection_->StatementRefDeleted(this);
73 Close();
74}
75
76void Connection::StatementRef::Close() {
77 if (stmt_) {
78 sqlite3_finalize(stmt_);
79 stmt_ = NULL;
80 }
81 connection_ = NULL; // The connection may be getting deleted.
82}
83
84Connection::Connection()
85 : db_(NULL),
86 page_size_(0),
87 cache_size_(0),
88 exclusive_locking_(false),
89 transaction_nesting_(0),
90 needs_rollback_(false) {
91}
92
93Connection::~Connection() {
94 Close();
95}
96
[email protected]765b44502009-10-02 05:01:4297bool Connection::Open(const FilePath& path) {
[email protected]e5ffd0e42009-09-11 21:30:5698#if defined(OS_WIN)
[email protected]765b44502009-10-02 05:01:4299 return OpenInternal(WideToUTF8(path.value()));
[email protected]e5ffd0e42009-09-11 21:30:56100#elif defined(OS_POSIX)
[email protected]765b44502009-10-02 05:01:42101 return OpenInternal(path.value());
[email protected]e5ffd0e42009-09-11 21:30:56102#endif
[email protected]765b44502009-10-02 05:01:42103}
[email protected]e5ffd0e42009-09-11 21:30:56104
[email protected]765b44502009-10-02 05:01:42105bool Connection::OpenInMemory() {
106 return OpenInternal(":memory:");
[email protected]e5ffd0e42009-09-11 21:30:56107}
108
109void Connection::Close() {
110 statement_cache_.clear();
111 DCHECK(open_statements_.empty());
112 if (db_) {
113 sqlite3_close(db_);
114 db_ = NULL;
115 }
116}
117
118void Connection::Preload() {
119 if (!db_) {
120 NOTREACHED();
121 return;
122 }
123
124 // A statement must be open for the preload command to work. If the meta
125 // table doesn't exist, it probably means this is a new database and there
126 // is nothing to preload (so it's OK we do nothing).
127 if (!DoesTableExist("meta"))
128 return;
129 Statement dummy(GetUniqueStatement("SELECT * FROM meta"));
[email protected]3fb6b1572009-10-07 20:10:30130 if (!dummy || !dummy.Step())
[email protected]e5ffd0e42009-09-11 21:30:56131 return;
132
133 sqlite3Preload(db_);
134}
135
136bool Connection::BeginTransaction() {
137 if (needs_rollback_) {
138 DCHECK(transaction_nesting_ > 0);
139
140 // When we're going to rollback, fail on this begin and don't actually
141 // mark us as entering the nested transaction.
142 return false;
143 }
144
145 bool success = true;
146 if (!transaction_nesting_) {
147 needs_rollback_ = false;
148
149 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
150 if (!begin || !begin.Run())
151 return false;
152 }
153 transaction_nesting_++;
154 return success;
155}
156
157void Connection::RollbackTransaction() {
158 if (!transaction_nesting_) {
[email protected]e7afe2452010-08-22 16:19:13159 NOTREACHED() << "Rolling back a nonexistent transaction";
[email protected]e5ffd0e42009-09-11 21:30:56160 return;
161 }
162
163 transaction_nesting_--;
164
165 if (transaction_nesting_ > 0) {
166 // Mark the outermost transaction as needing rollback.
167 needs_rollback_ = true;
168 return;
169 }
170
171 DoRollback();
172}
173
174bool Connection::CommitTransaction() {
175 if (!transaction_nesting_) {
[email protected]e7afe2452010-08-22 16:19:13176 NOTREACHED() << "Rolling back a nonexistent transaction";
[email protected]e5ffd0e42009-09-11 21:30:56177 return false;
178 }
179 transaction_nesting_--;
180
181 if (transaction_nesting_ > 0) {
182 // Mark any nested transactions as failing after we've already got one.
183 return !needs_rollback_;
184 }
185
186 if (needs_rollback_) {
187 DoRollback();
188 return false;
189 }
190
191 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
192 if (!commit)
193 return false;
194 return commit.Run();
195}
196
197bool Connection::Execute(const char* sql) {
198 if (!db_)
199 return false;
200 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
201}
202
[email protected]5b96f3772010-09-28 16:30:57203bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) {
204 if (!db_)
205 return false;
206
207 ScopedBusyTimeout busy_timeout(db_);
208 busy_timeout.SetTimeout(timeout);
209 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
210}
211
[email protected]e5ffd0e42009-09-11 21:30:56212bool Connection::HasCachedStatement(const StatementID& id) const {
213 return statement_cache_.find(id) != statement_cache_.end();
214}
215
216scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement(
217 const StatementID& id,
218 const char* sql) {
219 CachedStatementMap::iterator i = statement_cache_.find(id);
220 if (i != statement_cache_.end()) {
221 // Statement is in the cache. It should still be active (we're the only
222 // one invalidating cached statements, and we'll remove it from the cache
223 // if we do that. Make sure we reset it before giving out the cached one in
224 // case it still has some stuff bound.
225 DCHECK(i->second->is_valid());
226 sqlite3_reset(i->second->stmt());
227 return i->second;
228 }
229
230 scoped_refptr<StatementRef> statement = GetUniqueStatement(sql);
231 if (statement->is_valid())
232 statement_cache_[id] = statement; // Only cache valid statements.
233 return statement;
234}
235
236scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement(
237 const char* sql) {
238 if (!db_)
239 return new StatementRef(this, NULL); // Return inactive statement.
240
241 sqlite3_stmt* stmt = NULL;
242 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) {
243 // Treat this as non-fatal, it can occur in a number of valid cases, and
244 // callers should be doing their own error handling.
245 DLOG(WARNING) << "SQL compile error " << GetErrorMessage();
246 return new StatementRef(this, NULL);
247 }
248 return new StatementRef(this, stmt);
249}
250
[email protected]1ed78a32009-09-15 20:24:17251bool Connection::DoesTableExist(const char* table_name) const {
252 // GetUniqueStatement can't be const since statements may modify the
253 // database, but we know ours doesn't modify it, so the cast is safe.
254 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
[email protected]e5ffd0e42009-09-11 21:30:56255 "SELECT name FROM sqlite_master "
256 "WHERE type='table' AND name=?"));
257 if (!statement)
258 return false;
259 statement.BindString(0, table_name);
260 return statement.Step(); // Table exists if any row was returned.
261}
262
263bool Connection::DoesColumnExist(const char* table_name,
[email protected]1ed78a32009-09-15 20:24:17264 const char* column_name) const {
[email protected]e5ffd0e42009-09-11 21:30:56265 std::string sql("PRAGMA TABLE_INFO(");
266 sql.append(table_name);
267 sql.append(")");
268
[email protected]1ed78a32009-09-15 20:24:17269 // Our SQL is non-mutating, so this cast is OK.
270 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
271 sql.c_str()));
[email protected]e5ffd0e42009-09-11 21:30:56272 if (!statement)
273 return false;
274
275 while (statement.Step()) {
276 if (!statement.ColumnString(1).compare(column_name))
277 return true;
278 }
279 return false;
280}
281
282int64 Connection::GetLastInsertRowId() const {
283 if (!db_) {
284 NOTREACHED();
285 return 0;
286 }
287 return sqlite3_last_insert_rowid(db_);
288}
289
[email protected]1ed78a32009-09-15 20:24:17290int Connection::GetLastChangeCount() const {
291 if (!db_) {
292 NOTREACHED();
293 return 0;
294 }
295 return sqlite3_changes(db_);
296}
297
[email protected]e5ffd0e42009-09-11 21:30:56298int Connection::GetErrorCode() const {
299 if (!db_)
300 return SQLITE_ERROR;
301 return sqlite3_errcode(db_);
302}
303
[email protected]767718e52010-09-21 23:18:49304int Connection::GetLastErrno() const {
305 if (!db_)
306 return -1;
307
308 int err = 0;
309 if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err))
310 return -2;
311
312 return err;
313}
314
[email protected]e5ffd0e42009-09-11 21:30:56315const char* Connection::GetErrorMessage() const {
316 if (!db_)
317 return "sql::Connection has no connection.";
318 return sqlite3_errmsg(db_);
319}
320
[email protected]765b44502009-10-02 05:01:42321bool Connection::OpenInternal(const std::string& file_name) {
[email protected]9cfbc922009-11-17 20:13:17322 if (db_) {
323 NOTREACHED() << "sql::Connection is already open.";
324 return false;
325 }
326
[email protected]765b44502009-10-02 05:01:42327 int err = sqlite3_open(file_name.c_str(), &db_);
328 if (err != SQLITE_OK) {
329 OnSqliteError(err, NULL);
330 db_ = NULL;
331 return false;
332 }
333
[email protected]658f8332010-09-18 04:40:43334 // Enable extended result codes to provide more color on I/O errors.
335 // Not having extended result codes is not a fatal problem, as
336 // Chromium code does not attempt to handle I/O errors anyhow. The
337 // current implementation always returns SQLITE_OK, the DCHECK is to
338 // quickly notify someone if SQLite changes.
339 err = sqlite3_extended_result_codes(db_, 1);
340 DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes";
341
[email protected]5b96f3772010-09-28 16:30:57342 // If indicated, lock up the database before doing anything else, so
343 // that the following code doesn't have to deal with locking.
344 // TODO(shess): This code is brittle. Find the cases where code
345 // doesn't request |exclusive_locking_| and audit that it does the
346 // right thing with SQLITE_BUSY, and that it doesn't make
347 // assumptions about who might change things in the database.
348 // http://crbug.com/56559
349 if (exclusive_locking_) {
350 // TODO(shess): This should probably be a full CHECK(). Code
351 // which requests exclusive locking but doesn't get it is almost
352 // certain to be ill-tested.
353 if (!Execute("PRAGMA locking_mode=EXCLUSIVE"))
354 NOTREACHED() << "Could not set locking mode: " << GetErrorMessage();
355 }
356
[email protected]765b44502009-10-02 05:01:42357 if (page_size_ != 0) {
[email protected]5b96f3772010-09-28 16:30:57358 // Enforce SQLite restrictions on |page_size_|.
359 DCHECK(!(page_size_ & (page_size_ - 1)))
360 << " page_size_ " << page_size_ << " is not a power of two.";
361 static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
362 DCHECK_LE(page_size_, kSqliteMaxPageSize);
363 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_);
364 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
365 NOTREACHED() << "Could not set page size: " << GetErrorMessage();
[email protected]765b44502009-10-02 05:01:42366 }
367
368 if (cache_size_ != 0) {
[email protected]5b96f3772010-09-28 16:30:57369 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_);
370 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
371 NOTREACHED() << "Could not set cache size: " << GetErrorMessage();
[email protected]765b44502009-10-02 05:01:42372 }
373
374 return true;
375}
376
[email protected]e5ffd0e42009-09-11 21:30:56377void Connection::DoRollback() {
378 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
379 if (rollback)
380 rollback.Run();
381}
382
383void Connection::StatementRefCreated(StatementRef* ref) {
384 DCHECK(open_statements_.find(ref) == open_statements_.end());
385 open_statements_.insert(ref);
386}
387
388void Connection::StatementRefDeleted(StatementRef* ref) {
389 StatementRefSet::iterator i = open_statements_.find(ref);
390 if (i == open_statements_.end())
391 NOTREACHED();
392 else
393 open_statements_.erase(i);
394}
395
396void Connection::ClearCache() {
397 statement_cache_.clear();
398
399 // The cache clear will get most statements. There may be still be references
400 // to some statements that are held by others (including one-shot statements).
401 // This will deactivate them so they can't be used again.
402 for (StatementRefSet::iterator i = open_statements_.begin();
403 i != open_statements_.end(); ++i)
404 (*i)->Close();
405}
406
[email protected]faa604e2009-09-25 22:38:59407int Connection::OnSqliteError(int err, sql::Statement *stmt) {
408 if (error_delegate_.get())
409 return error_delegate_->OnError(err, this, stmt);
410 // The default handling is to assert on debug and to ignore on release.
411 NOTREACHED() << GetErrorMessage();
412 return err;
413}
414
[email protected]e5ffd0e42009-09-11 21:30:56415} // namespace sql