Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Side by Side Diff: app/sql/connection.cc

Issue 7353026: Move app/sql/* files to sql/ directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « app/sql/connection.h ('k') | app/sql/connection_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // 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/stringprintf.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "third_party/sqlite/sqlite3.h"
16
17 namespace {
18
19 // Spin for up to a second waiting for the lock to clear when setting
20 // up the database.
21 // TODO(shess): Better story on this. http://crbug.com/56559
22 const base::TimeDelta kBusyTimeout = base::TimeDelta::FromSeconds(1);
23
24 class ScopedBusyTimeout {
25 public:
26 explicit ScopedBusyTimeout(sqlite3* db)
27 : db_(db) {
28 }
29 ~ScopedBusyTimeout() {
30 sqlite3_busy_timeout(db_, 0);
31 }
32
33 int SetTimeout(base::TimeDelta timeout) {
34 DCHECK_LT(timeout.InMilliseconds(), INT_MAX);
35 return sqlite3_busy_timeout(db_,
36 static_cast<int>(timeout.InMilliseconds()));
37 }
38
39 private:
40 sqlite3* db_;
41 };
42
43 } // namespace
44
45 namespace sql {
46
47 bool StatementID::operator<(const StatementID& other) const {
48 if (number_ != other.number_)
49 return number_ < other.number_;
50 return strcmp(str_, other.str_) < 0;
51 }
52
53 ErrorDelegate::ErrorDelegate() {
54 }
55
56 ErrorDelegate::~ErrorDelegate() {
57 }
58
59 Connection::StatementRef::StatementRef()
60 : connection_(NULL),
61 stmt_(NULL) {
62 }
63
64 Connection::StatementRef::StatementRef(Connection* connection,
65 sqlite3_stmt* stmt)
66 : connection_(connection),
67 stmt_(stmt) {
68 connection_->StatementRefCreated(this);
69 }
70
71 Connection::StatementRef::~StatementRef() {
72 if (connection_)
73 connection_->StatementRefDeleted(this);
74 Close();
75 }
76
77 void Connection::StatementRef::Close() {
78 if (stmt_) {
79 sqlite3_finalize(stmt_);
80 stmt_ = NULL;
81 }
82 connection_ = NULL; // The connection may be getting deleted.
83 }
84
85 Connection::Connection()
86 : db_(NULL),
87 page_size_(0),
88 cache_size_(0),
89 exclusive_locking_(false),
90 transaction_nesting_(0),
91 needs_rollback_(false) {
92 }
93
94 Connection::~Connection() {
95 Close();
96 }
97
98 bool Connection::Open(const FilePath& path) {
99 #if defined(OS_WIN)
100 return OpenInternal(WideToUTF8(path.value()));
101 #elif defined(OS_POSIX)
102 return OpenInternal(path.value());
103 #endif
104 }
105
106 bool Connection::OpenInMemory() {
107 return OpenInternal(":memory:");
108 }
109
110 void Connection::Close() {
111 statement_cache_.clear();
112 DCHECK(open_statements_.empty());
113 if (db_) {
114 sqlite3_close(db_);
115 db_ = NULL;
116 }
117 }
118
119 void Connection::Preload() {
120 if (!db_) {
121 NOTREACHED();
122 return;
123 }
124
125 // A statement must be open for the preload command to work. If the meta
126 // table doesn't exist, it probably means this is a new database and there
127 // is nothing to preload (so it's OK we do nothing).
128 if (!DoesTableExist("meta"))
129 return;
130 Statement dummy(GetUniqueStatement("SELECT * FROM meta"));
131 if (!dummy || !dummy.Step())
132 return;
133
134 #if !defined(USE_SYSTEM_SQLITE)
135 // This function is only defined in Chromium's version of sqlite.
136 // Do not call it when using system sqlite.
137 sqlite3_preload(db_);
138 #endif
139 }
140
141 bool Connection::BeginTransaction() {
142 if (needs_rollback_) {
143 DCHECK_GT(transaction_nesting_, 0);
144
145 // When we're going to rollback, fail on this begin and don't actually
146 // mark us as entering the nested transaction.
147 return false;
148 }
149
150 bool success = true;
151 if (!transaction_nesting_) {
152 needs_rollback_ = false;
153
154 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
155 if (!begin || !begin.Run())
156 return false;
157 }
158 transaction_nesting_++;
159 return success;
160 }
161
162 void Connection::RollbackTransaction() {
163 if (!transaction_nesting_) {
164 NOTREACHED() << "Rolling back a nonexistent transaction";
165 return;
166 }
167
168 transaction_nesting_--;
169
170 if (transaction_nesting_ > 0) {
171 // Mark the outermost transaction as needing rollback.
172 needs_rollback_ = true;
173 return;
174 }
175
176 DoRollback();
177 }
178
179 bool Connection::CommitTransaction() {
180 if (!transaction_nesting_) {
181 NOTREACHED() << "Rolling back a nonexistent transaction";
182 return false;
183 }
184 transaction_nesting_--;
185
186 if (transaction_nesting_ > 0) {
187 // Mark any nested transactions as failing after we've already got one.
188 return !needs_rollback_;
189 }
190
191 if (needs_rollback_) {
192 DoRollback();
193 return false;
194 }
195
196 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
197 if (!commit)
198 return false;
199 return commit.Run();
200 }
201
202 bool Connection::Execute(const char* sql) {
203 if (!db_)
204 return false;
205 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
206 }
207
208 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) {
209 if (!db_)
210 return false;
211
212 ScopedBusyTimeout busy_timeout(db_);
213 busy_timeout.SetTimeout(timeout);
214 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
215 }
216
217 bool Connection::HasCachedStatement(const StatementID& id) const {
218 return statement_cache_.find(id) != statement_cache_.end();
219 }
220
221 scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement(
222 const StatementID& id,
223 const char* sql) {
224 CachedStatementMap::iterator i = statement_cache_.find(id);
225 if (i != statement_cache_.end()) {
226 // Statement is in the cache. It should still be active (we're the only
227 // one invalidating cached statements, and we'll remove it from the cache
228 // if we do that. Make sure we reset it before giving out the cached one in
229 // case it still has some stuff bound.
230 DCHECK(i->second->is_valid());
231 sqlite3_reset(i->second->stmt());
232 return i->second;
233 }
234
235 scoped_refptr<StatementRef> statement = GetUniqueStatement(sql);
236 if (statement->is_valid())
237 statement_cache_[id] = statement; // Only cache valid statements.
238 return statement;
239 }
240
241 scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement(
242 const char* sql) {
243 if (!db_)
244 return new StatementRef(this, NULL); // Return inactive statement.
245
246 sqlite3_stmt* stmt = NULL;
247 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) {
248 // Treat this as non-fatal, it can occur in a number of valid cases, and
249 // callers should be doing their own error handling.
250 DLOG(WARNING) << "SQL compile error " << GetErrorMessage();
251 return new StatementRef(this, NULL);
252 }
253 return new StatementRef(this, stmt);
254 }
255
256 bool Connection::DoesTableExist(const char* table_name) const {
257 // GetUniqueStatement can't be const since statements may modify the
258 // database, but we know ours doesn't modify it, so the cast is safe.
259 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
260 "SELECT name FROM sqlite_master "
261 "WHERE type='table' AND name=?"));
262 if (!statement)
263 return false;
264 statement.BindString(0, table_name);
265 return statement.Step(); // Table exists if any row was returned.
266 }
267
268 bool Connection::DoesColumnExist(const char* table_name,
269 const char* column_name) const {
270 std::string sql("PRAGMA TABLE_INFO(");
271 sql.append(table_name);
272 sql.append(")");
273
274 // Our SQL is non-mutating, so this cast is OK.
275 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
276 sql.c_str()));
277 if (!statement)
278 return false;
279
280 while (statement.Step()) {
281 if (!statement.ColumnString(1).compare(column_name))
282 return true;
283 }
284 return false;
285 }
286
287 int64 Connection::GetLastInsertRowId() const {
288 if (!db_) {
289 NOTREACHED();
290 return 0;
291 }
292 return sqlite3_last_insert_rowid(db_);
293 }
294
295 int Connection::GetLastChangeCount() const {
296 if (!db_) {
297 NOTREACHED();
298 return 0;
299 }
300 return sqlite3_changes(db_);
301 }
302
303 int Connection::GetErrorCode() const {
304 if (!db_)
305 return SQLITE_ERROR;
306 return sqlite3_errcode(db_);
307 }
308
309 int Connection::GetLastErrno() const {
310 if (!db_)
311 return -1;
312
313 int err = 0;
314 if (SQLITE_OK != sqlite3_file_control(db_, NULL, SQLITE_LAST_ERRNO, &err))
315 return -2;
316
317 return err;
318 }
319
320 const char* Connection::GetErrorMessage() const {
321 if (!db_)
322 return "sql::Connection has no connection.";
323 return sqlite3_errmsg(db_);
324 }
325
326 bool Connection::OpenInternal(const std::string& file_name) {
327 if (db_) {
328 NOTREACHED() << "sql::Connection is already open.";
329 return false;
330 }
331
332 int err = sqlite3_open(file_name.c_str(), &db_);
333 if (err != SQLITE_OK) {
334 OnSqliteError(err, NULL);
335 db_ = NULL;
336 return false;
337 }
338
339 // Enable extended result codes to provide more color on I/O errors.
340 // Not having extended result codes is not a fatal problem, as
341 // Chromium code does not attempt to handle I/O errors anyhow. The
342 // current implementation always returns SQLITE_OK, the DCHECK is to
343 // quickly notify someone if SQLite changes.
344 err = sqlite3_extended_result_codes(db_, 1);
345 DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes";
346
347 // If indicated, lock up the database before doing anything else, so
348 // that the following code doesn't have to deal with locking.
349 // TODO(shess): This code is brittle. Find the cases where code
350 // doesn't request |exclusive_locking_| and audit that it does the
351 // right thing with SQLITE_BUSY, and that it doesn't make
352 // assumptions about who might change things in the database.
353 // http://crbug.com/56559
354 if (exclusive_locking_) {
355 // TODO(shess): This should probably be a full CHECK(). Code
356 // which requests exclusive locking but doesn't get it is almost
357 // certain to be ill-tested.
358 if (!Execute("PRAGMA locking_mode=EXCLUSIVE"))
359 NOTREACHED() << "Could not set locking mode: " << GetErrorMessage();
360 }
361
362 if (page_size_ != 0) {
363 // Enforce SQLite restrictions on |page_size_|.
364 DCHECK(!(page_size_ & (page_size_ - 1)))
365 << " page_size_ " << page_size_ << " is not a power of two.";
366 static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
367 DCHECK_LE(page_size_, kSqliteMaxPageSize);
368 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_);
369 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
370 NOTREACHED() << "Could not set page size: " << GetErrorMessage();
371 }
372
373 if (cache_size_ != 0) {
374 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_);
375 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
376 NOTREACHED() << "Could not set cache size: " << GetErrorMessage();
377 }
378
379 return true;
380 }
381
382 void Connection::DoRollback() {
383 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
384 if (rollback)
385 rollback.Run();
386 }
387
388 void Connection::StatementRefCreated(StatementRef* ref) {
389 DCHECK(open_statements_.find(ref) == open_statements_.end());
390 open_statements_.insert(ref);
391 }
392
393 void Connection::StatementRefDeleted(StatementRef* ref) {
394 StatementRefSet::iterator i = open_statements_.find(ref);
395 if (i == open_statements_.end())
396 NOTREACHED();
397 else
398 open_statements_.erase(i);
399 }
400
401 void Connection::ClearCache() {
402 statement_cache_.clear();
403
404 // The cache clear will get most statements. There may be still be references
405 // to some statements that are held by others (including one-shot statements).
406 // This will deactivate them so they can't be used again.
407 for (StatementRefSet::iterator i = open_statements_.begin();
408 i != open_statements_.end(); ++i)
409 (*i)->Close();
410 }
411
412 int Connection::OnSqliteError(int err, sql::Statement *stmt) {
413 if (error_delegate_.get())
414 return error_delegate_->OnError(err, this, stmt);
415 // The default handling is to assert on debug and to ignore on release.
416 NOTREACHED() << GetErrorMessage();
417 return err;
418 }
419
420 } // namespace sql
OLDNEW
« no previous file with comments | « app/sql/connection.h ('k') | app/sql/connection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698