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 //==============================================================================
29 std::string_view
journalModeName(
30 SQLite::JournalMode mode
) noexcept
{
32 case SQLite::JournalMode::DELETE
:
34 case SQLite::JournalMode::TRUNCATE
:
36 case SQLite::JournalMode::PERSIST
:
38 case SQLite::JournalMode::MEMORY
:
40 case SQLite::JournalMode::WAL
:
42 case SQLite::JournalMode::OFF
:
50 SQLite::SQLite(SQLite
&& old
) noexcept
52 m_beginStmt
{std::move(old
.m_beginStmt
)},
53 m_rollbackStmt
{std::move(old
.m_rollbackStmt
)},
54 m_commitStmt
{std::move(old
.m_commitStmt
)} {
57 // No outstanding transactions
58 assertx(old
.m_txDepth
== 0);
59 assertx(old
.m_rollback
== false);
62 SQLite
& SQLite::operator=(SQLite
&& old
) noexcept
{
67 // Outstanding transactions would be invalidated; make sure none
69 assertx(m_txDepth
== 0);
70 assertx(m_rollback
== false);
71 assertx(old
.m_txDepth
== 0);
72 assertx(old
.m_rollback
== false);
74 sqlite3_close_v2(m_dbc
);
78 m_beginStmt
= std::move(old
.m_beginStmt
);
79 m_rollbackStmt
= std::move(old
.m_rollbackStmt
);
80 m_commitStmt
= std::move(old
.m_commitStmt
);
86 sqlite3_close_v2(m_dbc
);
90 void SQLite::analyze() {
91 int rc
= sqlite3_exec(m_dbc
, "ANALYZE", nullptr, nullptr, nullptr);
92 if (rc
!= SQLITE_OK
) {
93 throw SQLiteExc
{rc
, "ANALYZE"};
97 SQLite
SQLite::connect(const std::string
& path
, OpenMode mode
) {
98 return connect(path
.c_str(), mode
);
101 SQLite
SQLite::connect(const char* path
, OpenMode mode
) {
105 case OpenMode::ReadOnly
:
106 return SQLITE_OPEN_READONLY
;
107 case OpenMode::ReadWrite
:
108 return SQLITE_OPEN_READWRITE
;
109 case OpenMode::ReadWriteCreate
:
110 return SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
;
115 sqlite3
* dbc
= nullptr;
116 int rc
= sqlite3_open_v2(
118 SQLITE_OPEN_NOMUTEX
| flags
,
121 throw SQLiteExc
{rc
, ""};
127 * Compile the given SQL query into a statement object which can run and rerun
130 SQLiteStmt
SQLite::prepare(const std::string_view sql
) {
134 SQLiteTxn
SQLite::begin() { return SQLiteTxn
{*this}; }
136 void SQLite::setBusyTimeout(int ms
) noexcept
{
137 sqlite3_busy_timeout(m_dbc
, ms
);
140 void SQLite::setJournalMode(JournalMode mode
) {
141 SQLiteStmt journalModeStmt
{
142 *this, folly::sformat("PRAGMA journal_mode = {}", journalModeName(mode
))};
143 journalModeStmt
.query().step();
146 void SQLite::setSynchronousLevel(SynchronousLevel lvl
) {
149 folly::sformat("PRAGMA synchronous = {}", static_cast<int>(lvl
))};
153 bool SQLite::isReadOnly() const {
154 return isReadOnly("main");
157 bool SQLite::isReadOnly(const std::string
& dbName
) const {
158 return isReadOnly(dbName
.c_str());
161 bool SQLite::isReadOnly(const char* dbName
) const {
162 return bool(sqlite3_db_readonly(m_dbc
, dbName
));
165 std::string
SQLite::errMsg() const noexcept
{ return {sqlite3_errmsg(m_dbc
)}; }
167 SQLite::SQLite(sqlite3
* dbc
)
169 m_beginStmt
{*this, "BEGIN"},
170 m_rollbackStmt
{*this, "ROLLBACK"},
171 m_commitStmt
{*this, "COMMIT"} {
172 setBusyTimeout(60'000);
173 SQLiteStmt foreignKeysStmt
{*this, "PRAGMA foreign_keys = ON"};
174 foreignKeysStmt
.query().step();
177 void SQLite::txPush() {
178 if (m_txDepth
== 0) {
179 SQLiteQuery query
{m_beginStmt
};
185 void SQLite::txPop() {
186 // We mix the concept of rollback with a normal commit so that if we try to
187 // rollback an inner transaction we eventually end up rolling back the outer
188 // transaction instead (Sqlite doesn't support rolling back partial
190 assertx(m_txDepth
> 0);
196 SQLiteQuery query
{m_commitStmt
};
199 // We're in the outermost transaction - so clear the rollback flag.
201 SQLiteQuery query
{m_rollbackStmt
};
204 } catch (const SQLiteExc
& ) {
206 * Having a rollback fail is actually a normal, expected case,
207 * so just swallow this.
209 * In particular, according to the docs, if we got an I/O error
210 * while doing a commit, the rollback will often fail with "no
211 * transaction in progress", because the commit will have
212 * automatically been rolled back. Recommended practice is
213 * still to execute a rollback statement and ignore the error.
217 // Decrement depth after query execution, in case an exception occurs during
218 // commit. This allows for subsequent rollback of the failed commit.
222 void SQLite::rollback() noexcept
{
224 // NOTE: A try/catch isn't necessary - txPop() handles rollback as a nothrow.
228 void SQLite::commit() { txPop(); }
230 //==============================================================================
233 SQLiteTxn::SQLiteTxn(SQLite
& db
) : m_db
{&db
} { m_db
->txPush(); }
235 SQLiteTxn::SQLiteTxn(SQLiteTxn
&& old
) noexcept
236 : m_db
{old
.m_db
}, m_pending
{old
.m_pending
} {
238 old
.m_pending
= false;
241 SQLiteTxn
& SQLiteTxn::operator=(SQLiteTxn
&& old
) noexcept
{
248 m_pending
= old
.m_pending
;
249 old
.m_pending
= false;
253 SQLiteTxn::~SQLiteTxn() {
255 assertx(m_db
!= nullptr);
260 SQLiteQuery
SQLiteTxn::query(SQLiteStmt
& stmt
) noexcept
{ return stmt
.query(); }
262 void SQLiteTxn::exec(std::string_view sql
) {
263 assertx(m_db
!= nullptr);
264 SQLiteStmt stmt
{m_db
->prepare(sql
)};
265 SQLiteQuery query
{stmt
.query()};
269 void SQLiteTxn::commit() {
270 assertx(m_db
!= nullptr);
275 //==============================================================================
278 SQLiteStmt::SQLiteStmt(SQLiteStmt
&& old
) noexcept
279 : m_stmt
{old
.m_stmt
}, m_queryExists
{old
.m_queryExists
} {
280 assertx(!old
.m_queryExists
);
281 old
.m_stmt
= nullptr;
284 SQLiteStmt
& SQLiteStmt::operator=(SQLiteStmt
&& old
) noexcept
{
289 // Outstanding queries would be invalidated; make sure none exist
290 assertx(!m_queryExists
);
291 assertx(!old
.m_queryExists
);
293 sqlite3_finalize(m_stmt
);
295 old
.m_stmt
= nullptr;
300 SQLiteStmt::~SQLiteStmt() {
301 assertx(!m_queryExists
);
302 sqlite3_finalize(m_stmt
);
306 std::string_view
SQLiteStmt::sql() const noexcept
{
307 #if SQLITE_VERSION_NUMBER >= 3014000
308 return std::string_view
{sqlite3_expanded_sql(m_stmt
)};
310 return std::string_view
{sqlite3_sql(m_stmt
)};
314 SQLiteStmt::SQLiteStmt(SQLite
& db
, const std::string_view sql
) {
316 sqlite3_prepare_v2(db
.m_dbc
, sql
.data(), sql
.size(), &m_stmt
, nullptr);
318 throw SQLiteExc
{rc
, std::string
{sql
}};
320 assertx(m_stmt
!= nullptr);
323 SQLiteQuery
SQLiteStmt::query() noexcept
{
324 assertx(!m_queryExists
);
325 assertx(m_stmt
!= nullptr);
326 m_queryExists
= true;
327 return SQLiteQuery
{*this};
330 void SQLiteStmt::reset() noexcept
{
331 m_queryExists
= false;
332 sqlite3_reset(m_stmt
);
333 sqlite3_clear_bindings(m_stmt
);
336 //==============================================================================
339 SQLiteQuery::SQLiteQuery(SQLiteQuery
&& old
) noexcept
340 : m_stmt
{old
.m_stmt
}, m_row
{old
.m_row
}, m_done
{old
.m_done
} {
341 old
.m_stmt
= nullptr;
344 SQLiteQuery
& SQLiteQuery::operator=(SQLiteQuery
&& old
) noexcept
{
349 if (m_stmt
!= nullptr) {
356 old
.m_stmt
= nullptr;
360 SQLiteQuery::~SQLiteQuery() {
361 if (m_stmt
!= nullptr) {
366 std::string_view
SQLiteQuery::sql() const noexcept
{
367 assertx(m_stmt
!= nullptr);
368 return m_stmt
->sql();
371 void SQLiteQuery::step() {
372 assertx(m_stmt
!= nullptr);
373 int rc
= sqlite3_step(m_stmt
->m_stmt
);
384 throw SQLiteExc
{rc
, std::string
{sql()}};
388 void SQLiteQuery::bindBlob(const char* paramName
, const void* blob
, int size
,
389 bool isStatic
/* = false */) noexcept
{
390 assertx(m_stmt
!= nullptr);
391 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
392 int UNUSED rc
= sqlite3_bind_blob(
393 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), blob
, size
,
394 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
395 assertx(rc
== SQLITE_OK
);
398 void SQLiteQuery::bindText(const char* paramName
, const char* text
, int size
,
399 bool isStatic
/* = false */) noexcept
{
400 assertx(m_stmt
!= nullptr);
402 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
403 int UNUSED rc
= sqlite3_bind_text(
404 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), text
, int(size
),
405 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
406 assertx(rc
== SQLITE_OK
);
409 void SQLiteQuery::bindString(const char* paramName
,
410 const std::string_view s
) noexcept
{
411 bindText(paramName
, s
.data(), s
.size(), true);
414 void SQLiteQuery::bindDouble(const char* paramName
, double val
) noexcept
{
415 assertx(m_stmt
!= nullptr);
416 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
417 int UNUSED rc
= sqlite3_bind_double(
418 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
419 assertx(rc
== SQLITE_OK
);
422 void SQLiteQuery::bindInt(const char* paramName
, int val
) noexcept
{
423 assertx(m_stmt
!= nullptr);
424 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
425 int UNUSED rc
= sqlite3_bind_int(
426 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
427 assertx(rc
== SQLITE_OK
);
430 void SQLiteQuery::bindBool(const char* paramName
, bool b
) noexcept
{
431 bindInt(paramName
, int(b
));
434 void SQLiteQuery::bindInt64(const char* paramName
, int64_t val
) noexcept
{
435 assertx(m_stmt
!= nullptr);
436 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
437 int UNUSED rc
= sqlite3_bind_int64(
438 stmt
, sqlite3_bind_parameter_index(stmt
, paramName
), val
);
439 assertx(rc
== SQLITE_OK
);
442 void SQLiteQuery::bindNull(const char* paramName
) noexcept
{
443 assertx(m_stmt
!= nullptr);
444 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
446 sqlite3_bind_null(stmt
, sqlite3_bind_parameter_index(stmt
, paramName
));
447 assertx(rc
== SQLITE_OK
);
450 // Get the column value as the named type. If the value cannot be converted
451 // into the named type then an error is thrown.
452 bool SQLiteQuery::getBool(int iCol
) { return static_cast<bool>(getInt(iCol
)); }
454 int SQLiteQuery::getInt(int iCol
) {
455 assertx(m_stmt
!= nullptr);
456 return sqlite3_column_int(m_stmt
->m_stmt
, iCol
);
459 int64_t SQLiteQuery::getInt64(int iCol
) {
460 assertx(m_stmt
!= nullptr);
461 return sqlite3_column_int64(m_stmt
->m_stmt
, iCol
);
464 double SQLiteQuery::getDouble(int iCol
) {
465 assertx(m_stmt
!= nullptr);
466 return sqlite3_column_double(m_stmt
->m_stmt
, iCol
);
469 void SQLiteQuery::getBlob(int iCol
, const void*& blob
, size_t& size
) {
470 assertx(m_stmt
!= nullptr);
471 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
472 blob
= sqlite3_column_blob(stmt
, iCol
);
473 size
= sqlite3_column_bytes(stmt
, iCol
);
476 const std::string_view
SQLiteQuery::getString(int iCol
) {
477 assertx(m_stmt
!= nullptr);
478 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
480 reinterpret_cast<const char*>(sqlite3_column_text(stmt
, iCol
));
481 int size
= sqlite3_column_bytes(stmt
, iCol
);
483 return {text
, static_cast<size_t>(size
)};
486 Optional
<const std::string_view
> SQLiteQuery::getNullableString(
488 assertx(m_stmt
!= nullptr);
489 sqlite3_stmt
* stmt
= m_stmt
->m_stmt
;
490 if (sqlite3_column_type(stmt
, iCol
) == SQLITE_NULL
) {
493 return {getString(iCol
)};
496 std::string_view
SQLite::openModeName(SQLite::OpenMode mode
) noexcept
{
498 case SQLite::OpenMode::ReadOnly
:
500 case SQLite::OpenMode::ReadWrite
:
502 case SQLite::OpenMode::ReadWriteCreate
:
503 return "READ/WRITE/CREATE";
508 SQLiteQuery::SQLiteQuery(SQLiteStmt
& stmt
) : m_stmt
{&stmt
} {}
510 //==============================================================================
513 SQLiteExc::SQLiteExc(int code
, std::string sql
)
514 : std::runtime_error
{sql
.empty()
515 ? folly::to
<std::string
>(
517 "]: ", sqlite3_errstr(code
))
518 : folly::to
<std::string
>(
519 "SQLiteExc [", code
, "] while running ", sql
,
520 ": ", sqlite3_errstr(code
))},