Arquivos
hhvm/hphp/runtime/vm/repo_helpers.cpp
T
Edwin Smith d15e609333 Revert "Do copying with the AttachLiteral constructors."
Revert "Explicit CopyString, remove (const char*) constructors."
2013-05-06 09:31:26 -07:00

453 linhas
12 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "runtime/vm/repo.h"
#include "runtime/vm/repo_helpers.h"
#include "runtime/vm/blob_helper.h"
#include "runtime/base/builtin_functions.h"
namespace HPHP {
namespace VM {
static const Trace::Module TRACEMOD = Trace::hhbc;
//==============================================================================
// RepoStmt.
RepoStmt::RepoStmt(Repo& repo)
: m_repo(repo), m_stmt(nullptr) {
}
RepoStmt::~RepoStmt() {
finalize();
}
void RepoStmt::finalize() {
if (m_stmt != nullptr) {
m_sql.clear();
sqlite3_finalize(m_stmt);
m_stmt = nullptr;
}
}
void RepoStmt::prepare(const std::string& sql) {
finalize();
m_sql = sql;
int rc = sqlite3_prepare_v2(m_repo.dbc(), sql.c_str(), sql.size(), &m_stmt,
nullptr);
if (rc != SQLITE_OK) {
throw RepoExc("RepoStmt::%s(repo=%p) error: '%s' --> (%d) %s\n",
__func__, &m_repo, sql.c_str(), rc,
sqlite3_errmsg(m_repo.dbc()));
}
}
void RepoStmt::reset() {
sqlite3_reset(m_stmt);
sqlite3_clear_bindings(m_stmt);
}
//==============================================================================
// RepoTxn.
RepoTxn::RepoTxn(Repo& repo)
: m_repo(repo), m_pending(true), m_error(false) {
try {
m_repo.begin();
} catch (RepoExc& re) {
rollback();
throw;
}
}
RepoTxn::~RepoTxn() {
if (m_pending) {
rollback();
}
}
void RepoTxn::prepare(RepoStmt& stmt, const std::string& sql) {
try {
stmt.prepare(sql);
} catch (const std::exception&) {
rollback();
throw;
}
}
void RepoTxn::exec(const std::string& sQuery) {
try {
m_repo.exec(sQuery);
} catch (const std::exception& e) {
rollback();
TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
__func__, &m_repo, e.what());
throw;
}
}
void RepoTxn::commit() {
if (m_pending) {
try {
m_repo.commit();
} catch (const std::exception& e) {
rollback();
TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
__func__, &m_repo, e.what());
throw;
}
m_pending = false;
}
}
void RepoTxn::step(RepoQuery& query) {
try {
query.step();
} catch (const std::exception&) {
rollback();
throw;
}
}
void RepoTxn::exec(RepoQuery& query) {
try {
query.exec();
} catch (const std::exception& e) {
rollback();
TRACE(4, "RepoTxn::%s(repo=%p) caught '%s'\n",
__func__, &m_repo, e.what());
throw;
}
}
void RepoTxn::rollback() {
assert(!m_error);
assert(m_pending);
m_error = true;
m_pending = false;
m_repo.rollback();
}
//==============================================================================
// RepoQuery.
void RepoQuery::bindBlob(const char* paramName, const void* blob,
size_t size, bool isStatic /* = false */) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_blob(stmt,
sqlite3_bind_parameter_index(stmt, paramName),
blob, int(size),
isStatic ? SQLITE_STATIC : SQLITE_TRANSIENT);
assert(rc == SQLITE_OK);
}
void RepoQuery::bindBlob(const char* paramName, const BlobEncoder& blob,
bool isStatic) {
return bindBlob(paramName, blob.data(), blob.size(), isStatic);
}
void RepoQuery::bindMd5(const char* paramName, const MD5& md5) {
char md5nbo[16];
md5.nbo((void*)md5nbo);
bindBlob(paramName, md5nbo, sizeof(md5nbo));
}
void RepoQuery::bindTypedValue(const char* paramName, const TypedValue& tv) {
if (tv.m_type == KindOfUninit) {
bindBlob(paramName, "", 0, true);
} else {
String blob = f_serialize(tvAsCVarRef(&tv));
bindBlob(paramName, blob->data(), blob->size());
}
}
void RepoQuery::bindText(const char* paramName, const char* text,
size_t size, bool isStatic /* = false */) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_text(stmt,
sqlite3_bind_parameter_index(stmt, paramName),
text, int(size),
isStatic ? SQLITE_STATIC : SQLITE_TRANSIENT);
assert(rc == SQLITE_OK);
}
void RepoQuery::bindStaticString(const char* paramName, const StringData* sd) {
if (sd == nullptr) {
bindNull(paramName);
} else {
bindText(paramName, sd->data(), sd->size(), true);
}
}
void RepoQuery::bindDouble(const char* paramName, double val) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_double(stmt,
sqlite3_bind_parameter_index(stmt, paramName),
val);
assert(rc == SQLITE_OK);
}
void RepoQuery::bindInt(const char* paramName, int val) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_int(stmt,
sqlite3_bind_parameter_index(stmt, paramName),
val);
assert(rc == SQLITE_OK);
}
void RepoQuery::bindId(const char* paramName, Id id) {
bindInt(paramName, int(id));
}
void RepoQuery::bindOffset(const char* paramName, Offset offset) {
bindInt(paramName, int(offset));
}
void RepoQuery::bindAttr(const char* paramName, Attr attrs) {
bindInt(paramName, int(attrs));
}
void RepoQuery::bindBool(const char* paramName, bool b) {
bindInt(paramName, int(b));
}
void RepoQuery::bindInt64(const char* paramName, int64_t val) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_int64(stmt,
sqlite3_bind_parameter_index(stmt, paramName),
val);
assert(rc == SQLITE_OK);
}
void RepoQuery::bindNull(const char* paramName) {
sqlite3_stmt* stmt = m_stmt.get();
int rc UNUSED =
sqlite3_bind_null(stmt,
sqlite3_bind_parameter_index(stmt, paramName));
assert(rc == SQLITE_OK);
}
void RepoQuery::step() {
if (m_done) {
throw RepoExc("RepoQuery::%s(repo=%p) error: Query done",
__func__, &m_stmt.repo());
}
int rc = sqlite3_step(m_stmt.get());
switch (rc) {
case SQLITE_DONE:
m_row = false;
m_done = true;
break;
case SQLITE_ROW:
m_row = true;
m_done = false;
break;
default:
m_row = false;
m_done = true;
throw RepoExc("RepoQuery::%s(repo=%p) error: '%s' --> (%d) %s",
__func__, &m_stmt.repo(), m_stmt.sql().c_str(), rc,
sqlite3_errmsg(m_stmt.repo().dbc()));
}
}
void RepoQuery::reset() {
m_stmt.reset();
m_row = false;
m_done = false;
}
void RepoQuery::exec() {
step();
reset();
}
int64_t RepoQuery::getInsertedRowid() {
return sqlite3_last_insert_rowid(m_stmt.repo().dbc());
}
bool RepoQuery::isBlob(int iCol) {
return (iCol < sqlite3_data_count(m_stmt.get())
&& sqlite3_column_type(m_stmt.get(), iCol) == SQLITE_BLOB);
}
bool RepoQuery::isText(int iCol) {
return (iCol < sqlite3_data_count(m_stmt.get())
&& sqlite3_column_type(m_stmt.get(), iCol) == SQLITE_TEXT);
}
bool RepoQuery::isDouble(int iCol) {
return (iCol < sqlite3_data_count(m_stmt.get())
&& sqlite3_column_type(m_stmt.get(), iCol) == SQLITE_FLOAT);
}
bool RepoQuery::isInt(int iCol) {
return (iCol < sqlite3_data_count(m_stmt.get())
&& sqlite3_column_type(m_stmt.get(), iCol) == SQLITE_INTEGER);
}
bool RepoQuery::isNull(int iCol) {
return (iCol < sqlite3_data_count(m_stmt.get())
&& sqlite3_column_type(m_stmt.get(), iCol) == SQLITE_NULL);
}
void RepoQuery::getBlob(int iCol, const void*& blob, size_t& size) {
if (!isBlob(iCol)) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is not a blob in '%s'",
__func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str());
}
blob = sqlite3_column_blob(m_stmt.get(), iCol);
size = size_t(sqlite3_column_bytes(m_stmt.get(), iCol));
}
BlobDecoder RepoQuery::getBlob(int iCol) {
const void* vp;
size_t sz;
getBlob(iCol, vp, sz);
return BlobDecoder(vp, sz);
}
void RepoQuery::getMd5(int iCol, MD5& md5) {
const void* blob;
size_t size;
getBlob(iCol, blob, size);
if (size != 16) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is the wrong size"
" (expected 16, got %zu) in '%s'",
__func__, &m_stmt.repo(), iCol, size, m_stmt.sql().c_str());
}
new (&md5) MD5(blob);
}
void RepoQuery::getTypedValue(int iCol, TypedValue& tv) {
const void* blob;
size_t size;
getBlob(iCol, blob, size);
tvWriteUninit(&tv);
if (size > 0) {
String s = String((const char*)blob, size, CopyString);
Variant v = unserialize_from_string(s);
if (v.isString()) {
v = String(StringData::GetStaticString(v.asCStrRef().get()));
} else if (v.isArray()) {
v = Array(ArrayData::GetScalarArray(v.asCArrRef().get()));
} else {
// Serialized variants and objects shouldn't ever make it into the repo.
assert(!IS_REFCOUNTED_TYPE(v.getType()));
}
tvAsVariant(&tv) = v;
}
}
void RepoQuery::getText(int iCol, const char*& text) {
if (!isText(iCol)) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is not text in '%s'",
__func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str());
}
text = (const char*)sqlite3_column_text(m_stmt.get(), iCol);
}
void RepoQuery::getText(int iCol, const char*& text, size_t& size) {
getText(iCol, text);
size = size_t(sqlite3_column_bytes(m_stmt.get(), iCol));
}
void RepoQuery::getStaticString(int iCol, StringData*& s) {
if (isNull(iCol)) {
s = nullptr;
} else {
const char* text;
size_t size;
getText(iCol, text, size);
StackStringData sd(text, size, AttachLiteral);
s = StringData::GetStaticString(&sd);
}
}
void RepoQuery::getDouble(int iCol, double& val) {
if (!isDouble(iCol)) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is not a double in '%s'",
__func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str());
}
val = sqlite3_column_double(m_stmt.get(), iCol);
}
void RepoQuery::getInt(int iCol, int& val) {
if (!isInt(iCol)) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is not an integer in '%s'",
__func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str());
}
val = sqlite3_column_int(m_stmt.get(), iCol);
}
void RepoQuery::getId(int iCol, Id& id) {
int val;
getInt(iCol, val);
id = Id(val);
}
void RepoQuery::getOffset(int iCol, Offset& offset) {
int val;
getInt(iCol, val);
offset = Offset(val);
}
void RepoQuery::getAttr(int iCol, Attr& attrs) {
int val;
getInt(iCol, val);
attrs = Attr(val);
}
void RepoQuery::getBool(int iCol, bool& b) {
int val;
getInt(iCol, val);
b = bool(val);
}
void RepoQuery::getInt64(int iCol, int64_t& val) {
if (!isInt(iCol)) {
throw RepoExc(
"RepoQuery::%s(repo=%p) error: Column %d is not an integer in '%s'",
__func__, &m_stmt.repo(), iCol, m_stmt.sql().c_str());
}
val = sqlite3_column_int64(m_stmt.get(), iCol);
}
//==============================================================================
// RepoTxnQuery.
void RepoTxnQuery::step() {
assert(!m_txn.error());
m_txn.step(*this);
}
void RepoTxnQuery::exec() {
assert(!m_txn.error());
m_txn.exec(*this);
}
} } // HPHP::VM