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