2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/repo-helpers.h"
18 #include "hphp/runtime/vm/repo.h"
19 #include "hphp/runtime/vm/blob-helper.h"
20 #include "hphp/runtime/base/builtin-functions.h"
26 //==============================================================================
29 RepoStmt::RepoStmt(Repo
& repo
)
30 : m_repo(repo
), m_stmt(nullptr) {
33 RepoStmt::~RepoStmt() {
37 void RepoStmt::finalize() {
38 if (m_stmt
!= nullptr) {
40 sqlite3_finalize(m_stmt
);
45 void RepoStmt::prepare(const std::string
& sql
) {
48 int rc
= sqlite3_prepare_v2(m_repo
.dbc(), sql
.c_str(), sql
.size(), &m_stmt
,
50 if (rc
!= SQLITE_OK
) {
51 throw RepoExc("RepoStmt::%s(repo=%p) error: '%s' --> (%d) %s\n",
52 __func__
, &m_repo
, sql
.c_str(), rc
,
53 sqlite3_errmsg(m_repo
.dbc()));
57 void RepoStmt::reset() {
58 sqlite3_reset(m_stmt
);
59 sqlite3_clear_bindings(m_stmt
);
62 //==============================================================================
65 RepoTxn::RepoTxn(Repo
& repo
)
66 : m_repo(repo
), m_pending(true), m_error(false) {
69 } catch (RepoExc
& re
) {
81 void RepoTxn::prepare(RepoStmt
& stmt
, const std::string
& sql
) {
84 } catch (const std::exception
&) {
90 void RepoTxn::exec(const std::string
& sQuery
) {
93 } catch (const std::exception
& e
) {
95 TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
96 __func__
, &m_repo
, e
.what());
101 void RepoTxn::commit() {
105 } catch (const std::exception
& e
) {
107 TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
108 __func__
, &m_repo
, e
.what());
115 void RepoTxn::step(RepoQuery
& query
) {
118 } catch (const std::exception
&) {
124 void RepoTxn::exec(RepoQuery
& query
) {
127 } catch (const std::exception
& e
) {
129 TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
130 __func__
, &m_repo
, e
.what());
135 void RepoTxn::rollback() {
143 //==============================================================================
146 void RepoQuery::bindBlob(const char* paramName
, const void* blob
,
147 size_t size
, bool isStatic
/* = false */) {
148 sqlite3_stmt
* stmt
= m_stmt
.get();
150 sqlite3_bind_blob(stmt
,
151 sqlite3_bind_parameter_index(stmt
, paramName
),
153 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
154 assert(rc
== SQLITE_OK
);
157 void RepoQuery::bindBlob(const char* paramName
, const BlobEncoder
& blob
,
159 return bindBlob(paramName
, blob
.data(), blob
.size(), isStatic
);
162 void RepoQuery::bindMd5(const char* paramName
, const MD5
& md5
) {
164 md5
.nbo((void*)md5nbo
);
165 bindBlob(paramName
, md5nbo
, sizeof(md5nbo
));
168 void RepoQuery::bindTypedValue(const char* paramName
, const TypedValue
& tv
) {
169 if (tv
.m_type
== KindOfUninit
) {
170 bindBlob(paramName
, "", 0, true);
172 String blob
= f_serialize(tvAsCVarRef(&tv
));
173 bindBlob(paramName
, blob
.data(), blob
.size());
177 void RepoQuery::bindText(const char* paramName
, const char* text
,
178 size_t size
, bool isStatic
/* = false */) {
179 sqlite3_stmt
* stmt
= m_stmt
.get();
181 sqlite3_bind_text(stmt
,
182 sqlite3_bind_parameter_index(stmt
, paramName
),
184 isStatic
? SQLITE_STATIC
: SQLITE_TRANSIENT
);
185 assert(rc
== SQLITE_OK
);
188 void RepoQuery::bindStaticString(const char* paramName
, const StringData
* sd
) {
192 bindText(paramName
, sd
->data(), sd
->size(), true);
196 void RepoQuery::bindStdString(const char* paramName
, const std::string
& s
) {
197 bindText(paramName
, s
.data(), s
.size(), true);
200 void RepoQuery::bindDouble(const char* paramName
, double val
) {
201 sqlite3_stmt
* stmt
= m_stmt
.get();
203 sqlite3_bind_double(stmt
,
204 sqlite3_bind_parameter_index(stmt
, paramName
),
206 assert(rc
== SQLITE_OK
);
209 void RepoQuery::bindInt(const char* paramName
, int val
) {
210 sqlite3_stmt
* stmt
= m_stmt
.get();
212 sqlite3_bind_int(stmt
,
213 sqlite3_bind_parameter_index(stmt
, paramName
),
215 assert(rc
== SQLITE_OK
);
218 void RepoQuery::bindId(const char* paramName
, Id id
) {
219 bindInt(paramName
, int(id
));
222 void RepoQuery::bindOffset(const char* paramName
, Offset offset
) {
223 bindInt(paramName
, int(offset
));
226 void RepoQuery::bindAttr(const char* paramName
, Attr attrs
) {
227 bindInt(paramName
, int(attrs
));
230 void RepoQuery::bindBool(const char* paramName
, bool b
) {
231 bindInt(paramName
, int(b
));
234 void RepoQuery::bindInt64(const char* paramName
, int64_t val
) {
235 sqlite3_stmt
* stmt
= m_stmt
.get();
237 sqlite3_bind_int64(stmt
,
238 sqlite3_bind_parameter_index(stmt
, paramName
),
240 assert(rc
== SQLITE_OK
);
243 void RepoQuery::bindNull(const char* paramName
) {
244 sqlite3_stmt
* stmt
= m_stmt
.get();
246 sqlite3_bind_null(stmt
,
247 sqlite3_bind_parameter_index(stmt
, paramName
));
248 assert(rc
== SQLITE_OK
);
251 void RepoQuery::step() {
253 throw RepoExc("RepoQuery::%s(repo=%p) error: Query done",
254 __func__
, &m_stmt
.repo());
256 int rc
= sqlite3_step(m_stmt
.get());
269 throw RepoExc("RepoQuery::%s(repo=%p) error: '%s' --> (%d) %s",
270 __func__
, &m_stmt
.repo(), m_stmt
.sql().c_str(), rc
,
271 sqlite3_errmsg(m_stmt
.repo().dbc()));
275 void RepoQuery::reset() {
281 void RepoQuery::exec() {
286 int64_t RepoQuery::getInsertedRowid() {
287 return sqlite3_last_insert_rowid(m_stmt
.repo().dbc());
290 bool RepoQuery::isBlob(int iCol
) {
291 return (iCol
< sqlite3_data_count(m_stmt
.get())
292 && sqlite3_column_type(m_stmt
.get(), iCol
) == SQLITE_BLOB
);
295 bool RepoQuery::isText(int iCol
) {
296 return (iCol
< sqlite3_data_count(m_stmt
.get())
297 && sqlite3_column_type(m_stmt
.get(), iCol
) == SQLITE_TEXT
);
300 bool RepoQuery::isDouble(int iCol
) {
301 return (iCol
< sqlite3_data_count(m_stmt
.get())
302 && sqlite3_column_type(m_stmt
.get(), iCol
) == SQLITE_FLOAT
);
305 bool RepoQuery::isInt(int iCol
) {
306 return (iCol
< sqlite3_data_count(m_stmt
.get())
307 && sqlite3_column_type(m_stmt
.get(), iCol
) == SQLITE_INTEGER
);
310 bool RepoQuery::isNull(int iCol
) {
311 return (iCol
< sqlite3_data_count(m_stmt
.get())
312 && sqlite3_column_type(m_stmt
.get(), iCol
) == SQLITE_NULL
);
315 void RepoQuery::getBlob(int iCol
, const void*& blob
, size_t& size
) {
318 "RepoQuery::%s(repo=%p) error: Column %d is not a blob in '%s'",
319 __func__
, &m_stmt
.repo(), iCol
, m_stmt
.sql().c_str());
321 blob
= sqlite3_column_blob(m_stmt
.get(), iCol
);
322 size
= size_t(sqlite3_column_bytes(m_stmt
.get(), iCol
));
325 BlobDecoder
RepoQuery::getBlob(int iCol
) {
328 getBlob(iCol
, vp
, sz
);
329 return BlobDecoder(vp
, sz
);
332 void RepoQuery::getMd5(int iCol
, MD5
& md5
) {
335 getBlob(iCol
, blob
, size
);
338 "RepoQuery::%s(repo=%p) error: Column %d is the wrong size"
339 " (expected 16, got %zu) in '%s'",
340 __func__
, &m_stmt
.repo(), iCol
, size
, m_stmt
.sql().c_str());
342 new (&md5
) MD5(blob
);
345 void RepoQuery::getTypedValue(int iCol
, TypedValue
& tv
) {
348 getBlob(iCol
, blob
, size
);
351 String s
= String((const char*)blob
, size
, CopyString
);
352 Variant v
= unserialize_from_string(s
);
354 v
= String(makeStaticString(v
.asCStrRef().get()));
355 } else if (v
.isArray()) {
356 v
= Array(ArrayData::GetScalarArray(v
.asCArrRef().get()));
358 // Serialized variants and objects shouldn't ever make it into the repo.
359 assert(!IS_REFCOUNTED_TYPE(v
.getType()));
361 tvAsVariant(&tv
) = v
;
365 void RepoQuery::getText(int iCol
, const char*& text
) {
368 "RepoQuery::%s(repo=%p) error: Column %d is not text in '%s'",
369 __func__
, &m_stmt
.repo(), iCol
, m_stmt
.sql().c_str());
371 text
= (const char*)sqlite3_column_text(m_stmt
.get(), iCol
);
374 void RepoQuery::getText(int iCol
, const char*& text
, size_t& size
) {
376 size
= size_t(sqlite3_column_bytes(m_stmt
.get(), iCol
));
379 void RepoQuery::getStdString(int iCol
, std::string
& s
) {
382 getText(iCol
, text
, size
);
383 s
= std::string(text
, size
);
386 void RepoQuery::getStaticString(int iCol
, StringData
*& s
) {
392 getText(iCol
, text
, size
);
393 s
= makeStaticString(text
, size
);
397 void RepoQuery::getDouble(int iCol
, double& val
) {
398 if (!isDouble(iCol
)) {
400 "RepoQuery::%s(repo=%p) error: Column %d is not a double in '%s'",
401 __func__
, &m_stmt
.repo(), iCol
, m_stmt
.sql().c_str());
403 val
= sqlite3_column_double(m_stmt
.get(), iCol
);
406 void RepoQuery::getInt(int iCol
, int& val
) {
409 "RepoQuery::%s(repo=%p) error: Column %d is not an integer in '%s'",
410 __func__
, &m_stmt
.repo(), iCol
, m_stmt
.sql().c_str());
412 val
= sqlite3_column_int(m_stmt
.get(), iCol
);
415 void RepoQuery::getId(int iCol
, Id
& id
) {
421 void RepoQuery::getOffset(int iCol
, Offset
& offset
) {
424 offset
= Offset(val
);
427 void RepoQuery::getAttr(int iCol
, Attr
& attrs
) {
433 void RepoQuery::getBool(int iCol
, bool& b
) {
439 void RepoQuery::getInt64(int iCol
, int64_t& val
) {
442 "RepoQuery::%s(repo=%p) error: Column %d is not an integer in '%s'",
443 __func__
, &m_stmt
.repo(), iCol
, m_stmt
.sql().c_str());
445 val
= sqlite3_column_int64(m_stmt
.get(), iCol
);
448 //==============================================================================
451 void RepoTxnQuery::step() {
452 assert(!m_txn
.error());
456 void RepoTxnQuery::exec() {
457 assert(!m_txn
.error());