2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
19 #include "hphp/util/assertions.h"
20 #include "hphp/util/sqlite-wrapper.h"
24 //==============================================================================
27 SQLite::SQLite(SQLite
&& old
) noexcept
29 m_beginStmt
{std::move(old
.m_beginStmt
)},
30 m_rollbackStmt
{std::move(old
.m_rollbackStmt
)},
31 m_commitStmt
{std::move(old
.m_commitStmt
)} {
34 // No outstanding transactions
35 assertx(old
.m_txDepth
== 0);
36 assertx(old
.m_rollback
== false);
39 SQLite
& SQLite::operator=(SQLite
&& old
) noexcept
{
44 // Outstanding transactions would be invalidated; make sure none
46 assertx(m_txDepth
== 0);
47 assertx(m_rollback
== false);
48 assertx(old
.m_txDepth
== 0);
49 assertx(old
.m_rollback
== false);
51 sqlite3_close_v2(m_dbc
);
55 m_beginStmt
= std::move(old
.m_beginStmt
);
56 m_rollbackStmt
= std::move(old
.m_rollbackStmt
);
57 m_commitStmt
= std::move(old
.m_commitStmt
);
63 sqlite3_close_v2(m_dbc
);
67 SQLite
SQLite::connect(const folly::StringPiece path
) {
68 sqlite3
* dbc
= nullptr;
69 int rc
= sqlite3_open_v2(
71 SQLITE_OPEN_NOMUTEX
| SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
,
74 throw SQLiteExc
{rc
, ""};
80 * Compile the given SQL query into a statement object which can run and rerun
83 SQLiteStmt
SQLite::prepare(const folly::StringPiece sql
) {
87 SQLiteTxn
SQLite::begin() { return SQLiteTxn
{*this}; }
89 void SQLite::setBusyTimeout(int ms
) noexcept
{
90 sqlite3_busy_timeout(m_dbc
, ms
);
93 std::string
SQLite::errMsg() const noexcept
{ return {sqlite3_errmsg(m_dbc
)}; }
95 SQLite::SQLite(sqlite3
* dbc
)
97 m_beginStmt
{*this, "BEGIN"},
98 m_rollbackStmt
{*this, "ROLLBACK"},
99 m_commitStmt
{*this, "COMMIT"} {
100 setBusyTimeout(60'000);
101 SQLiteStmt foreignKeysStmt
{*this, "PRAGMA foreign_keys = ON"};
102 foreignKeysStmt
.query().step();
103 SQLiteStmt walModeStmt
{*this, "PRAGMA journal_mode = WAL"};
105 walModeStmt
.query().step();
106 } catch (SQLiteExc
& e
) {
109 // This happens if multiple connections attempt to set WAL mode at the
110 // same time. We only need one connection to succeed.
118 void SQLite::txPush() {
119 if (m_txDepth
== 0) {
120 SQLiteQuery query
{m_beginStmt
};
126 void SQLite::txPop() {
127 // We mix the concept of rollback with a normal commit so that if we try to
128 // rollback an inner transaction we eventually end up rolling back the outer
129 // transaction instead (Sqlite doesn't support rolling back partial
131 assertx(m_txDepth
> 0);
137 SQLiteQuery query
{m_commitStmt
};
140 // We're in the outermost transaction - so clear the rollback flag.
142 SQLiteQuery query
{m_rollbackStmt
};
145 } catch (const SQLiteExc
& ex
) {
147 * Having a rollback fail is actually a normal, expected case,
148 * so just swallow this.
150 * In particular, according to the docs, if we got an I/O error
151 * while doing a commit, the rollback will often fail with "no
152 * transaction in progress", because the commit will have
153 * automatically been rolled back. Recommended practice is
154 * still to execute a rollback statement and ignore the error.
158 // Decrement depth after query execution, in case an exception occurs during
159 // commit. This allows for subsequent rollback of the failed commit.
163 void SQLite::rollback() noexcept
{
165 // NOTE: A try/catch isn't necessary - txPop() handles rollback as a nothrow.
169 void SQLite::commit() { txPop(); }
171 //==============================================================================
174 SQLiteTxn::SQLiteTxn(SQLite
& db
) : m_db
{&db
} { m_db
->txPush(); }
176 SQLiteTxn::SQLiteTxn(SQLiteTxn
&& old
) noexcept
177 : m_db
{old
.m_db
}, m_pending
{old
.m_pending
} {
179 old
.m_pending
= false;
182 SQLiteTxn
& SQLiteTxn::operator=(SQLiteTxn
&& old
) noexcept
{
189 m_pending
= old
.m_pending
;
190 old
.m_pending
= false;
194 SQLiteTxn::~SQLiteTxn() {
196 assertx(m_db
!= nullptr);
201 SQLiteQuery
SQLiteTxn::query(SQLiteStmt
& stmt
) noexcept
{ return stmt
.query(); }
203 void SQLiteTxn::exec(folly::StringPiece sql
) {
204 assertx(m_db
!= nullptr);
205 SQLiteStmt stmt
{m_db
->prepare(sql
)};
206 SQLiteQuery query
{stmt
.query()};
210 void SQLiteTxn::commit() {
211 assertx(m_db
!= nullptr);
216 //==============================================================================
219 SQLiteStmt::SQLiteStmt(SQLiteStmt
&& old
) noexcept
220 : m_stmt
{old
.m_stmt
}, m_queryExists
{old
.m_queryExists
} {
221 assertx(!old
.m_queryExists
);
222 old
.m_stmt
= nullptr;
225 SQLiteStmt
& SQLiteStmt::operator=(SQLiteStmt
&& old
) noexcept
{
230 // Outstanding queries would be invalidated; make sure none exist
231 assertx(!m_queryExists
);
232 assertx(!old
.m_queryExists
);
234 sqlite3_finalize(m_stmt
);
236 old
.m_stmt
= nullptr;
241 SQLiteStmt::~SQLiteStmt() {
242 assertx(!m_queryExists
);
243 sqlite3_finalize(m_stmt
);
247 folly::StringPiece
SQLiteStmt::sql() const noexcept
{
248 #if SQLITE_VERSION_NUMBER >= 3014000
249 return folly::StringPiece
{sqlite3_expanded_sql(m_stmt
)};
251 return folly::StringPiece
{sqlite3_sql(m_stmt
)};
255 SQLiteStmt::SQLiteStmt(SQLite
& db
, folly::StringPiece sql
) {
257 sqlite3_prepare_v2(db
.m_dbc
, sql
.data(), sql
.size(), &m_stmt
, nullptr);
259 throw SQLiteExc
{rc
, ""};
261 assertx(m_stmt
!= nullptr);
264 SQLiteQuery
SQLiteStmt::query() noexcept
{
265 assertx(!m_queryExists
);
266 m_queryExists
= true;
267 return SQLiteQuery
{*this};
270 void SQLiteStmt::reset() noexcept
{
271 m_queryExists
= false;
272 sqlite3_reset(m_stmt
);
273 sqlite3_clear_bindings(m_stmt
);
276 //==============================================================================
279 SQLiteQuery::SQLiteQuery(SQLiteQuery
&& old
) noexcept
280 : m_stmt
{old
.m_stmt
}, m_row
{old
.m_row
}, m_done
{old
.m_done
} {
281 old
.m_stmt
= nullptr;
284 SQLiteQuery
& SQLiteQuery::operator=(SQLiteQuery
&& old
) noexcept
{
289 if (m_stmt
!= nullptr) {
296 old
.m_stmt
= nullptr;
300 SQLiteQuery::~SQLiteQuery() {
301 if (m_stmt
!= nullptr) {
306 folly::StringPiece
SQLiteQuery::sql() const noexcept
{
307 assertx(m_stmt
!= nullptr);
308 return m_stmt
->sql();
311 void SQLiteQuery::step() {
312 assertx(m_stmt
!= nullptr);
313 int rc
= sqlite3_step(m_stmt
->m_stmt
);
324 throw SQLiteExc
{rc
, sql().str()};
328 void SQLiteQuery::bindBlob(const char* paramName
, const void* blob
, int size
,
329 bool isStatic
/* = false */) noexcept
{
330 assertx(m_stmt
!= nullptr);
331 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
332 int UNUSED rc
= sqlite3_bind_blob(
333 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), blob
, size
,
334 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
335 assertx(rc
== SQLITE_OK
);
338 void SQLiteQuery::bindText(const char* paramName
, const char* text
, int size
,
339 bool isStatic
/* = false */) noexcept
{
340 assertx(m_stmt
!= nullptr);
342 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
343 int UNUSED rc
= sqlite3_bind_text(
344 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), text
, int(size
),
345 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
346 assertx(rc
== SQLITE_OK
);
349 void SQLiteQuery::bindString(const char* paramName
,
350 const folly::StringPiece s
) noexcept
{
351 bindText(paramName
, s
.data(), s
.size(), true);
354 void SQLiteQuery::bindDouble(const char* paramName
, double val
) noexcept
{
355 assertx(m_stmt
!= nullptr);
356 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
357 int UNUSED rc
= sqlite3_bind_double(
358 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
359 assertx(rc
== SQLITE_OK
);
362 void SQLiteQuery::bindInt(const char* paramName
, int val
) noexcept
{
363 assertx(m_stmt
!= nullptr);
364 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
365 int UNUSED rc
= sqlite3_bind_int(
366 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
367 assertx(rc
== SQLITE_OK
);
370 void SQLiteQuery::bindBool(const char* paramName
, bool b
) noexcept
{
371 bindInt(paramName
, int(b
));
374 void SQLiteQuery::bindInt64(const char* paramName
, int64_t val
) noexcept
{
375 assertx(m_stmt
!= nullptr);
376 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
377 int UNUSED rc
= sqlite3_bind_int64(
378 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
379 assertx(rc
== SQLITE_OK
);
382 void SQLiteQuery::bindNull(const char* paramName
) noexcept
{
383 assertx(m_stmt
!= nullptr);
384 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
386 sqlite3_bind_null(stmt
, sqlite3_bind_parameter_index(stmt
, paramName
));
387 assertx(rc
== SQLITE_OK
);
390 // Get the column value as the named type. If the value cannot be converted
391 // into the named type then an error is thrown.
392 bool SQLiteQuery::getBool(int iCol
) { return static_cast<bool>(getInt(iCol
)); }
394 int SQLiteQuery::getInt(int iCol
) {
395 assertx(m_stmt
!= nullptr);
396 return sqlite3_column_int(m_stmt
->m_stmt
, iCol
);
399 int64_t SQLiteQuery::getInt64(int iCol
) {
400 assertx(m_stmt
!= nullptr);
401 return sqlite3_column_int64(m_stmt
->m_stmt
, iCol
);
404 double SQLiteQuery::getDouble(int iCol
) {
405 assertx(m_stmt
!= nullptr);
406 return sqlite3_column_double(m_stmt
->m_stmt
, iCol
);
409 void SQLiteQuery::getBlob(int iCol
, const void*& blob
, size_t& size
) {
410 assertx(m_stmt
!= nullptr);
411 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
412 blob
= sqlite3_column_blob(stmt
, iCol
);
413 size
= sqlite3_column_bytes(stmt
, iCol
);
416 const folly::StringPiece
SQLiteQuery::getString(int iCol
) {
417 assertx(m_stmt
!= nullptr);
418 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
420 reinterpret_cast<const char*>(sqlite3_column_text(stmt
, iCol
));
421 int size
= sqlite3_column_bytes(stmt
, iCol
);
423 return {text
, static_cast<size_t>(size
)};
426 SQLiteQuery::SQLiteQuery(SQLiteStmt
& stmt
) : m_stmt
{&stmt
} {}
428 //==============================================================================
431 SQLiteExc::SQLiteExc(int code
, std::string sql
)
432 : std::runtime_error
{sql
.empty()
433 ? folly::to
<std::string
>(
435 "]: ", sqlite3_errstr(code
))
436 : folly::to
<std::string
>(
437 "SQLiteExc [", code
, "] while running ", sql
,
438 ": ", sqlite3_errstr(code
))},