blob: 81f3d362eeab1b5a8e77e188f30be68eb0d302b0 [file] [log] [blame]
[email protected]e5ffd0e42009-09-11 21:30:561// Copyright (c) 2009 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/string_util.h"
[email protected]d55194ca2010-03-11 18:25:4513#include "base/utf_string_conversions.h"
[email protected]e5ffd0e42009-09-11 21:30:5614#include "third_party/sqlite/preprocessed/sqlite3.h"
15
16namespace sql {
17
18bool StatementID::operator<(const StatementID& other) const {
19 if (number_ != other.number_)
20 return number_ < other.number_;
21 return strcmp(str_, other.str_) < 0;
22}
23
24Connection::StatementRef::StatementRef()
25 : connection_(NULL),
26 stmt_(NULL) {
27}
28
29Connection::StatementRef::StatementRef(Connection* connection,
30 sqlite3_stmt* stmt)
31 : connection_(connection),
32 stmt_(stmt) {
33 connection_->StatementRefCreated(this);
34}
35
36Connection::StatementRef::~StatementRef() {
37 if (connection_)
38 connection_->StatementRefDeleted(this);
39 Close();
40}
41
42void Connection::StatementRef::Close() {
43 if (stmt_) {
44 sqlite3_finalize(stmt_);
45 stmt_ = NULL;
46 }
47 connection_ = NULL; // The connection may be getting deleted.
48}
49
50Connection::Connection()
51 : db_(NULL),
52 page_size_(0),
53 cache_size_(0),
54 exclusive_locking_(false),
55 transaction_nesting_(0),
56 needs_rollback_(false) {
57}
58
59Connection::~Connection() {
60 Close();
61}
62
[email protected]765b44502009-10-02 05:01:4263bool Connection::Open(const FilePath& path) {
[email protected]e5ffd0e42009-09-11 21:30:5664#if defined(OS_WIN)
[email protected]765b44502009-10-02 05:01:4265 return OpenInternal(WideToUTF8(path.value()));
[email protected]e5ffd0e42009-09-11 21:30:5666#elif defined(OS_POSIX)
[email protected]765b44502009-10-02 05:01:4267 return OpenInternal(path.value());
[email protected]e5ffd0e42009-09-11 21:30:5668#endif
[email protected]765b44502009-10-02 05:01:4269}
[email protected]e5ffd0e42009-09-11 21:30:5670
[email protected]765b44502009-10-02 05:01:4271bool Connection::OpenInMemory() {
72 return OpenInternal(":memory:");
[email protected]e5ffd0e42009-09-11 21:30:5673}
74
75void Connection::Close() {
76 statement_cache_.clear();
77 DCHECK(open_statements_.empty());
78 if (db_) {
79 sqlite3_close(db_);
80 db_ = NULL;
81 }
82}
83
84void Connection::Preload() {
85 if (!db_) {
86 NOTREACHED();
87 return;
88 }
89
90 // A statement must be open for the preload command to work. If the meta
91 // table doesn't exist, it probably means this is a new database and there
92 // is nothing to preload (so it's OK we do nothing).
93 if (!DoesTableExist("meta"))
94 return;
95 Statement dummy(GetUniqueStatement("SELECT * FROM meta"));
[email protected]3fb6b1572009-10-07 20:10:3096 if (!dummy || !dummy.Step())
[email protected]e5ffd0e42009-09-11 21:30:5697 return;
98
99 sqlite3Preload(db_);
100}
101
102bool Connection::BeginTransaction() {
103 if (needs_rollback_) {
104 DCHECK(transaction_nesting_ > 0);
105
106 // When we're going to rollback, fail on this begin and don't actually
107 // mark us as entering the nested transaction.
108 return false;
109 }
110
111 bool success = true;
112 if (!transaction_nesting_) {
113 needs_rollback_ = false;
114
115 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
116 if (!begin || !begin.Run())
117 return false;
118 }
119 transaction_nesting_++;
120 return success;
121}
122
123void Connection::RollbackTransaction() {
124 if (!transaction_nesting_) {
125 NOTREACHED() << "Rolling back a nonexistant transaction";
126 return;
127 }
128
129 transaction_nesting_--;
130
131 if (transaction_nesting_ > 0) {
132 // Mark the outermost transaction as needing rollback.
133 needs_rollback_ = true;
134 return;
135 }
136
137 DoRollback();
138}
139
140bool Connection::CommitTransaction() {
141 if (!transaction_nesting_) {
142 NOTREACHED() << "Rolling back a nonexistant transaction";
143 return false;
144 }
145 transaction_nesting_--;
146
147 if (transaction_nesting_ > 0) {
148 // Mark any nested transactions as failing after we've already got one.
149 return !needs_rollback_;
150 }
151
152 if (needs_rollback_) {
153 DoRollback();
154 return false;
155 }
156
157 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
158 if (!commit)
159 return false;
160 return commit.Run();
161}
162
163bool Connection::Execute(const char* sql) {
164 if (!db_)
165 return false;
166 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
167}
168
169bool Connection::HasCachedStatement(const StatementID& id) const {
170 return statement_cache_.find(id) != statement_cache_.end();
171}
172
173scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement(
174 const StatementID& id,
175 const char* sql) {
176 CachedStatementMap::iterator i = statement_cache_.find(id);
177 if (i != statement_cache_.end()) {
178 // Statement is in the cache. It should still be active (we're the only
179 // one invalidating cached statements, and we'll remove it from the cache
180 // if we do that. Make sure we reset it before giving out the cached one in
181 // case it still has some stuff bound.
182 DCHECK(i->second->is_valid());
183 sqlite3_reset(i->second->stmt());
184 return i->second;
185 }
186
187 scoped_refptr<StatementRef> statement = GetUniqueStatement(sql);
188 if (statement->is_valid())
189 statement_cache_[id] = statement; // Only cache valid statements.
190 return statement;
191}
192
193scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement(
194 const char* sql) {
195 if (!db_)
196 return new StatementRef(this, NULL); // Return inactive statement.
197
198 sqlite3_stmt* stmt = NULL;
199 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) {
200 // Treat this as non-fatal, it can occur in a number of valid cases, and
201 // callers should be doing their own error handling.
202 DLOG(WARNING) << "SQL compile error " << GetErrorMessage();
203 return new StatementRef(this, NULL);
204 }
205 return new StatementRef(this, stmt);
206}
207
[email protected]1ed78a32009-09-15 20:24:17208bool Connection::DoesTableExist(const char* table_name) const {
209 // GetUniqueStatement can't be const since statements may modify the
210 // database, but we know ours doesn't modify it, so the cast is safe.
211 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
[email protected]e5ffd0e42009-09-11 21:30:56212 "SELECT name FROM sqlite_master "
213 "WHERE type='table' AND name=?"));
214 if (!statement)
215 return false;
216 statement.BindString(0, table_name);
217 return statement.Step(); // Table exists if any row was returned.
218}
219
220bool Connection::DoesColumnExist(const char* table_name,
[email protected]1ed78a32009-09-15 20:24:17221 const char* column_name) const {
[email protected]e5ffd0e42009-09-11 21:30:56222 std::string sql("PRAGMA TABLE_INFO(");
223 sql.append(table_name);
224 sql.append(")");
225
[email protected]1ed78a32009-09-15 20:24:17226 // Our SQL is non-mutating, so this cast is OK.
227 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
228 sql.c_str()));
[email protected]e5ffd0e42009-09-11 21:30:56229 if (!statement)
230 return false;
231
232 while (statement.Step()) {
233 if (!statement.ColumnString(1).compare(column_name))
234 return true;
235 }
236 return false;
237}
238
239int64 Connection::GetLastInsertRowId() const {
240 if (!db_) {
241 NOTREACHED();
242 return 0;
243 }
244 return sqlite3_last_insert_rowid(db_);
245}
246
[email protected]1ed78a32009-09-15 20:24:17247int Connection::GetLastChangeCount() const {
248 if (!db_) {
249 NOTREACHED();
250 return 0;
251 }
252 return sqlite3_changes(db_);
253}
254
[email protected]e5ffd0e42009-09-11 21:30:56255int Connection::GetErrorCode() const {
256 if (!db_)
257 return SQLITE_ERROR;
258 return sqlite3_errcode(db_);
259}
260
261const char* Connection::GetErrorMessage() const {
262 if (!db_)
263 return "sql::Connection has no connection.";
264 return sqlite3_errmsg(db_);
265}
266
[email protected]765b44502009-10-02 05:01:42267bool Connection::OpenInternal(const std::string& file_name) {
[email protected]9cfbc922009-11-17 20:13:17268 if (db_) {
269 NOTREACHED() << "sql::Connection is already open.";
270 return false;
271 }
272
[email protected]765b44502009-10-02 05:01:42273 int err = sqlite3_open(file_name.c_str(), &db_);
274 if (err != SQLITE_OK) {
275 OnSqliteError(err, NULL);
276 db_ = NULL;
277 return false;
278 }
279
280 if (page_size_ != 0) {
281 if (!Execute(StringPrintf("PRAGMA page_size=%d", page_size_).c_str()))
282 NOTREACHED() << "Could not set page size";
283 }
284
285 if (cache_size_ != 0) {
286 if (!Execute(StringPrintf("PRAGMA cache_size=%d", cache_size_).c_str()))
287 NOTREACHED() << "Could not set page size";
288 }
289
290 if (exclusive_locking_) {
291 if (!Execute("PRAGMA locking_mode=EXCLUSIVE"))
292 NOTREACHED() << "Could not set locking mode.";
293 }
294
295 return true;
296}
297
[email protected]e5ffd0e42009-09-11 21:30:56298void Connection::DoRollback() {
299 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
300 if (rollback)
301 rollback.Run();
302}
303
304void Connection::StatementRefCreated(StatementRef* ref) {
305 DCHECK(open_statements_.find(ref) == open_statements_.end());
306 open_statements_.insert(ref);
307}
308
309void Connection::StatementRefDeleted(StatementRef* ref) {
310 StatementRefSet::iterator i = open_statements_.find(ref);
311 if (i == open_statements_.end())
312 NOTREACHED();
313 else
314 open_statements_.erase(i);
315}
316
317void Connection::ClearCache() {
318 statement_cache_.clear();
319
320 // The cache clear will get most statements. There may be still be references
321 // to some statements that are held by others (including one-shot statements).
322 // This will deactivate them so they can't be used again.
323 for (StatementRefSet::iterator i = open_statements_.begin();
324 i != open_statements_.end(); ++i)
325 (*i)->Close();
326}
327
[email protected]faa604e2009-09-25 22:38:59328int Connection::OnSqliteError(int err, sql::Statement *stmt) {
329 if (error_delegate_.get())
330 return error_delegate_->OnError(err, this, stmt);
331 // The default handling is to assert on debug and to ignore on release.
332 NOTREACHED() << GetErrorMessage();
333 return err;
334}
335
[email protected]e5ffd0e42009-09-11 21:30:56336} // namespace sql