41 # define __attribute__(x)
46 #include "boost/regex.hpp"
54 #include <mysql/mysql.h>
62 namespace dafPer = lsst::daf::persistence;
63 namespace dafBase = lsst::daf::base;
64 namespace pexExcept = lsst::pex::exceptions;
65 namespace pexPolicy = lsst::pex::policy;
69 namespace persistence {
84 template<> enum_field_types
86 template<> enum_field_types
88 template<> enum_field_types
90 template<> enum_field_types
93 template <
typename N> enum_field_types
97 template<> enum_field_types
113 template<> enum_field_types
117 template<> enum_field_types
121 template<> enum_field_types
125 template<> enum_field_types
138 lsst::daf::base::Citizen(typeid(*this)), _data(0) {
144 lsst::daf::base::Citizen(typeid(*this)), _data(location) {
150 lsst::daf::base::Citizen(typeid(*this)),
151 _type(src._type), _isNull(src._isNull), _isUnsigned(src._isUnsigned),
152 _length(src._length), _data(src._data) {
162 lsst::daf::base::Citizen(typeid(*this)), _db(0) {
191 setenv(
"TZ",
"UTC", 1);
200 unsigned int port = strtoul(dbloc.
getPort().c_str(), 0, 10);
201 if (mysql_real_connect(_db,
207 error(
"Unable to connect to MySQL database: " + _location);
230 if (_db == 0)
error(
"Database session not initialized "
231 "in DbStorage::startTransaction()",
false);
232 if (mysql_autocommit(_db,
false))
error(
"Unable to turn off autocommit");
238 if (_db == 0)
error(
"Database session not initialized "
239 "in DbStorage::endTransaction()",
false);
240 if (mysql_commit(_db))
error(
"Unable to commit transaction");
241 if (mysql_autocommit(_db,
true))
error(
"Unable to turn on autocommit");
252 error(
"No DB connection for query: " + query,
false);
254 lsst::pex::logging::TTrace<5>(
"daf.persistence.DbStorage",
256 if (mysql_query(_db, query.c_str()) != 0) {
257 error(
"Unable to execute query: " + query);
264 std::string::size_type pos = name.find(
'.');
265 if (pos == std::string::npos)
return '`' + name +
'`';
266 return '`' + std::string(name, 0, pos) +
"`.`" +
267 std::string(name, pos + 1) +
'`';
271 error(text +
" - * " + mysql_stmt_error(_statement),
false);
276 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, text +
" - * " + mysql_error(_db));
279 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, text);
284 boost::shared_array<char> mem(
new char[size]);
285 _bindingMemory.push_back(mem);
299 std::string
const& templateName,
300 bool mayAlreadyExist) {
301 std::string query =
"CREATE TABLE ";
302 if (mayAlreadyExist) query +=
"IF NOT EXISTS ";
303 query += quote(tableName) +
" LIKE " + quote(templateName);
311 executeQuery(
"DROP TABLE " + quote(tableName));
318 executeQuery(
"TRUNCATE TABLE " + quote(tableName));
326 executeQuery(sqlStatement);
338 error(
"Attempt to insert into read-only database",
false);
340 _insertTable = tableName;
348 template <
typename T>
351 BoundVarMap::iterator bv = _inputVars.find(columnName);
352 size_t size =
sizeof(T);
353 if (bv == _inputVars.end()) {
354 bv = _inputVars.insert(
355 BoundVarMap::value_type(columnName,
356 BoundVar(allocateMemory(size)))).first;
358 else if (bv->second._length != size) {
359 bv->second._data = allocateMemory(size);
362 bv->second._isNull =
false;
364 bv->second._length = size;
365 memcpy(bv->second._data, &value, size);
370 std::string
const& value) {
371 BoundVarMap::iterator bv = _inputVars.find(columnName);
372 size_t size = value.length();
373 if (bv == _inputVars.end()) {
374 bv = _inputVars.insert(
375 BoundVarMap::value_type(columnName,
376 BoundVar(allocateMemory(size)))).first;
378 else if (bv->second._length != size) {
379 bv->second._data = allocateMemory(size);
382 bv->second._isNull =
false;
384 bv->second._length = size;
385 memcpy(bv->second._data, value.data(), size);
391 BoundVarMap::iterator bv = _inputVars.find(columnName);
392 size_t size =
sizeof(MYSQL_TIME);
393 if (bv == _inputVars.end()) {
394 bv = _inputVars.insert(
395 BoundVarMap::value_type(columnName,
396 BoundVar(allocateMemory(size)))).first;
398 else if (bv->second._length != size) {
399 bv->second._data = allocateMemory(size);
402 bv->second._isNull =
false;
404 bv->second._length = size;
405 struct tm v = value.
gmtime();
406 MYSQL_TIME* t =
reinterpret_cast<MYSQL_TIME*
>(bv->second._data);
407 t->year = v.tm_year + 1900;
408 t->month = v.tm_mon + 1;
411 t->minute = v.tm_min;
412 t->second = v.tm_sec;
415 static_cast<unsigned long>((value.
nsecs() % 1000000000
LL) / 1000);
422 BoundVarMap::iterator bv = _inputVars.find(columnName);
423 if (bv == _inputVars.end()) {
424 bv = _inputVars.insert(
425 BoundVarMap::value_type(columnName,
426 BoundVar(allocateMemory(1)))).first;
428 bv->second._isNull =
true;
429 bv->second._length = 1;
437 error(
"Attempt to insert into read-only database",
false);
439 if (_insertTable.empty())
error(
"Insert table not initialized in DbStorage::insertRow()",
false);
440 if (_inputVars.empty())
error(
"No values to insert",
false);
442 std::string query =
"INSERT INTO " + quote(_insertTable) +
" (";
444 boost::scoped_array<MYSQL_BIND> binder(
new MYSQL_BIND[_inputVars.size()]);
445 memset(binder.get(), 0, _inputVars.size() *
sizeof(MYSQL_BIND));
448 for (BoundVarMap::iterator it = _inputVars.begin();
449 it != _inputVars.end(); ++it) {
450 if (it != _inputVars.begin()) {
453 query += quote(it->first);
456 MYSQL_BIND& bind(binder[i]);
459 bind.buffer_type = MYSQL_TYPE_NULL;
462 bind.buffer_type = bv.
_type;
463 bind.buffer = bv.
_data;
464 bind.buffer_length = bv.
_length;
472 query +=
") VALUES (";
473 for (
size_t i = 0; i < _inputVars.size(); ++i) {
483 _statement = mysql_stmt_init(_db);
484 if (_statement == 0) {
485 error(
"Unable to initialize statement: " + query);
487 if (mysql_stmt_prepare(_statement, query.c_str(), query.length()) != 0) {
488 stError(
"Unable to prepare statement: " + query);
490 if (mysql_stmt_bind_param(_statement, binder.get())) {
491 stError(
"Unable to bind variables in: " + query);
493 if (mysql_stmt_execute(_statement) != 0) {
494 stError(
"Unable to execute statement: " + query);
496 mysql_stmt_close(_statement);
510 if (_db == 0)
error(
"Database session not initialized in DbStorage::setTableForQuery()",
false);
511 _queryTables.clear();
512 _queryTables.push_back(isExpr ? tableName : quote(tableName));
516 _whereClause.clear();
526 std::vector<std::string>
const& tableNameList) {
527 if (_db == 0)
error(
"Database session not initialized in DbStorage::setTableListForQuery()",
false);
528 for (std::vector<std::string>::const_iterator it = tableNameList.begin();
529 it != tableNameList.end(); ++it) {
530 _queryTables.push_back(quote(*it));
535 _whereClause.clear();
550 std::string
col = isExpr ? columnName : quote(columnName);
551 _outColumns.push_back(col);
562 template <
typename T>
564 T* location,
bool isExpr) {
565 std::string
col = isExpr ? columnName : quote(columnName);
566 _outColumns.push_back(col);
567 size_t size =
sizeof(T);
568 std::pair<BoundVarMap::iterator, bool> pair = _outputVars.insert(
569 BoundVarMap::value_type(col,
BoundVar(location)));
571 error(
"Duplicate column name requested: " + columnName,
false);
582 std::string* location,
bool isExpr) {
583 std::string
col = isExpr ? columnName : quote(columnName);
584 _outColumns.push_back(col);
586 std::pair<BoundVarMap::iterator, bool> pair = _outputVars.insert(
587 BoundVarMap::value_type(
588 col,
BoundVar(allocateMemory(size +
sizeof(std::string*)))));
590 error(
"Duplicate column name requested: " + columnName,
false);
593 *
reinterpret_cast<std::string**
>(bv.
_data) = location;
604 std::string
col = isExpr ? columnName : quote(columnName);
605 _outColumns.push_back(col);
606 size_t size =
sizeof(MYSQL_TIME);
607 std::pair<BoundVarMap::iterator, bool> pair = _outputVars.insert(
608 BoundVarMap::value_type(
611 error(
"Duplicate column name requested: " + columnName,
false);
626 template <
typename T>
628 setColumn<T>(paramName, value);
636 if (!_orderBy.empty()) {
639 _orderBy += expression;
646 if (!_groupBy.empty()) {
649 _groupBy += expression;
658 _whereClause = whereClause;
664 if (_outColumns.empty())
error(
"No output columns for query",
false);
670 std::string query =
"SELECT ";
671 for (std::vector<std::string>::const_iterator it = _outColumns.begin();
672 it != _outColumns.end(); ++it) {
673 if (it != _outColumns.begin()) {
681 for (std::vector<std::string>::const_iterator it = _queryTables.begin();
682 it != _queryTables.end(); ++it) {
683 if (it != _queryTables.begin()) {
690 std::vector<std::string> whereBindings;
691 if (!_whereClause.empty()) {
692 boost::regex re(
":([A-Za-z_]+)");
694 std::back_insert_iterator<std::string> out(result);
695 boost::regex_iterator<std::string::iterator>
m;
696 for (boost::regex_iterator<std::string::iterator> i(
697 _whereClause.begin(), _whereClause.end(), re);
698 i != boost::regex_iterator<std::string::iterator>(); ++i) {
700 std::copy(m->prefix().first, m->prefix().second, out);
702 assert(m->size() == 2);
703 whereBindings.push_back(m->str(1));
705 if (m != boost::regex_iterator<std::string::iterator>()) {
706 std::copy(m->suffix().first, m->suffix().second, out);
709 std::copy(_whereClause.begin(), _whereClause.end(), out);
711 query +=
" WHERE " + result;
715 if (!_groupBy.empty()) query +=
" GROUP BY " + _groupBy;
718 if (!_orderBy.empty()) query +=
" ORDER BY " + _orderBy;
723 boost::scoped_array<MYSQL_BIND> inBinder(
724 new MYSQL_BIND[whereBindings.size()]);
725 memset(inBinder.get(), 0, whereBindings.size() *
sizeof(MYSQL_BIND));
726 for (
size_t i = 0; i < whereBindings.size(); ++i) {
727 MYSQL_BIND& bind(inBinder[i]);
728 BoundVarMap::iterator it = _inputVars.find(whereBindings[i]);
729 if (it == _inputVars.end()) {
730 error(
"Unbound variable in WHERE clause: " + whereBindings[i],
734 bind.buffer_type = bv.
_type;
735 bind.buffer = bv.
_data;
736 bind.buffer_length = bv.
_length;
745 _statement = mysql_stmt_init(_db);
747 error(
"Unable to initialize prepared statement");
750 if (mysql_stmt_prepare(_statement, query.c_str(), query.length()) != 0) {
751 stError(
"Unable to prepare statement: " + query);
756 unsigned int params = mysql_stmt_param_count(_statement);
757 if (_whereClause.empty()) {
759 error(
"Unbound WHERE clause parameters: " + query,
false);
763 if (params != whereBindings.size()) {
764 error(
"Mismatch in number of WHERE clause parameters: " + query,
767 if (mysql_stmt_bind_param(_statement, inBinder.get())) {
768 stError(
"Unable to bind WHERE parameters: " + query);
773 MYSQL_RES* queryMetadata = mysql_stmt_result_metadata(_statement);
774 if (!queryMetadata) {
775 stError(
"No query metadata: " + query);
777 _numResultFields = mysql_num_fields(queryMetadata);
778 if (static_cast<unsigned int>(_numResultFields) != _outColumns.size()) {
779 error(
"Mismatch in number of SELECT items: " + query,
false);
785 if (mysql_stmt_execute(_statement) != 0) {
786 stError(
"MySQL query failed: " + query);
792 _resultFields = mysql_fetch_fields(queryMetadata);
794 boost::scoped_array<MYSQL_BIND> outBinder(
new MYSQL_BIND[_numResultFields]);
795 memset(outBinder.get(), 0, _numResultFields *
sizeof(MYSQL_BIND));
796 _fieldLengths.reset(
new unsigned long[_numResultFields]);
797 _fieldNulls.reset(
new my_bool[_numResultFields]);
799 for (
int i = 0; i < _numResultFields; ++i) {
800 MYSQL_BIND& bind(outBinder[i]);
801 if (_outputVars.empty()) {
802 bind.buffer_type = MYSQL_TYPE_STRING;
804 bind.buffer_length = 0;
805 bind.length = &(_fieldLengths[i]);
806 bind.is_null = &(_fieldNulls[i]);
807 bind.is_unsigned = (_resultFields[i].flags & UNSIGNED_FLAG) != 0;
811 BoundVarMap::iterator it = _outputVars.find(_outColumns[i]);
812 if (it == _outputVars.end()) {
813 error(
"Unbound variable in SELECT clause: " + _outColumns[i],
818 bind.buffer_type = bv.
_type;
820 bind.buffer =
reinterpret_cast<char*
>(bv.
_data) +
821 sizeof(std::string*);
824 bind.buffer =
reinterpret_cast<char*
>(bv.
_data) +
825 sizeof(std::string*);
828 bind.buffer = bv.
_data;
830 bind.buffer_length = bv.
_length;
831 bind.length = &(_fieldLengths[i]);
832 bind.is_null = &(_fieldNulls[i]);
837 if (mysql_stmt_bind_result(_statement, outBinder.get())) {
838 stError(
"Unable to bind results: " + query);
846 if (_statement == 0) {
847 error(
"Statement not initialized in DbStorage::next()",
false);
849 int ret = mysql_stmt_fetch(_statement);
852 if (!_outputVars.empty()) {
853 for (
size_t i = 0; i < _outColumns.size(); ++i) {
854 BoundVarMap::iterator bvit = _outputVars.find(_outColumns[i]);
855 if (bvit == _outputVars.end()) {
856 error(
"Unbound variable in SELECT clause: " +
857 _outColumns[i],
false);
861 **
reinterpret_cast<std::string**
>(bv.
_data) =
862 std::string(reinterpret_cast<char*>(bv.
_data) +
863 sizeof(std::string*), _fieldLengths[i]);
867 char* cp =
reinterpret_cast<char*
>(bv.
_data) +
869 MYSQL_TIME* t =
reinterpret_cast<MYSQL_TIME*
>(cp);
872 t->hour, t->minute, t->second,
879 if (ret == MYSQL_NO_DATA)
return false;
880 if (ret == MYSQL_DATA_TRUNCATED && _outputVars.empty())
return true;
881 stError(
"Error fetching next row");
889 template <
typename T>
891 if (pos > _numResultFields) {
892 std::ostringstream os;
893 os <<
"Nonexistent column: " << pos;
894 error(os.str(),
false);
897 memset(&bind, 0,
sizeof(MYSQL_BIND));
902 bind.buffer_length =
sizeof(T);
903 bind.length = &(_fieldLengths[pos]);
904 bind.is_null = &(_fieldNulls[pos]);
905 if (mysql_stmt_fetch_column(_statement, &bind, pos, 0)) {
906 std::ostringstream os;
907 os <<
"Error fetching column: " << pos;
908 error(os.str(),
false);
915 if (pos > _numResultFields) {
916 std::ostringstream os;
917 os <<
"Nonexistent column: " << pos;
918 error(os.str(),
false);
921 memset(&bind, 0,
sizeof(MYSQL_BIND));
922 if (_resultFields[pos].type == MYSQL_TYPE_BIT) {
923 error(
"Invalid type for string retrieval",
false);
925 boost::scoped_array<char> t(
new char[_fieldLengths[pos]]);
928 bind.buffer = t.get();
929 bind.buffer_length = _fieldLengths[pos];
930 bind.length = &(_fieldLengths[pos]);
931 bind.is_null = &(_fieldNulls[pos]);
932 if (mysql_stmt_fetch_column(_statement, &bind, pos, 0)) {
933 std::ostringstream os;
934 os <<
"Error fetching string column: " << pos;
937 static std::string s;
938 s = std::string(t.get(), _fieldLengths[pos]);
944 if (pos > _numResultFields) {
945 std::ostringstream os;
946 os <<
"Nonexistent column: " << pos;
947 error(os.str(),
false);
950 memset(&bind, 0,
sizeof(MYSQL_BIND));
951 if (_resultFields[pos].type != MYSQL_TYPE_TIME &&
952 _resultFields[pos].type != MYSQL_TYPE_DATE &&
953 _resultFields[pos].type != MYSQL_TYPE_DATETIME &&
954 _resultFields[pos].type != MYSQL_TYPE_TIMESTAMP) {
955 error(
"Invalid type for DateTime retrieval",
false);
961 bind.buffer_length =
sizeof(MYSQL_TIME);
962 bind.length = &(_fieldLengths[pos]);
963 bind.is_null = &(_fieldNulls[pos]);
964 if (mysql_stmt_fetch_column(_statement, &bind, pos, 0)) {
965 std::ostringstream os;
966 os <<
"Error fetching DateTime column: " << pos;
980 if (pos > _numResultFields) {
981 std::ostringstream os;
982 os <<
"Nonexistent column: " << pos;
983 error(os.str(),
false);
985 return _fieldNulls[pos];
991 mysql_stmt_close(_statement);
999 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
char const& value);
1000 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
short const& value);
1001 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
int const& value);
1002 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
long const& value);
1003 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
long long const& value);
1004 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
float const& value);
1005 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
double const& value);
1006 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName, std::string
const& value);
1007 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
bool const& value);
1008 template void dafPer::DbStorageImpl::setColumn<>(std::string
const& columnName,
dafBase::DateTime const& value);
1010 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
char* location,
bool isExpr);
1011 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
short* location,
bool isExpr);
1012 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
int* location,
bool isExpr);
1013 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
long* location,
bool isExpr);
1014 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
long long* location,
bool isExpr);
1015 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
float* location,
bool isExpr);
1016 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
double* location,
bool isExpr);
1017 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName, std::string* location,
bool isExpr);
1018 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
bool* location,
bool isExpr);
1019 template void dafPer::DbStorageImpl::outParam<>(std::string
const& columnName,
dafBase::DateTime* location,
bool isExpr);
1021 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
char const& value);
1022 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
short const& value);
1023 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
int const& value);
1024 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
long const& value);
1025 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
long long const& value);
1026 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
float const& value);
1027 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
double const& value);
1028 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName, std::string
const& value);
1029 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
bool const& value);
1030 template void dafPer::DbStorageImpl::condParam<>(std::string
const& paramName,
dafBase::DateTime const& value);
1032 template char const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1033 template short const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1034 template int const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1035 template long const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1036 template long long const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1037 template float const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1038 template double const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1039 template std::string
const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1040 template bool const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
1041 template dafBase::DateTime const& dafPer::DbStorageImpl::getColumnByPos<>(
int pos);
virtual void setQueryWhere(std::string const &whereClause)
Class for handling dates/times, including MJD, UTC, and TAI.
table::Key< std::string > name
virtual void setTableForInsert(std::string const &tableName)
void outParam(std::string const &columnName, T *location, bool isExpr)
void setColumn(std::string const &columnName, T const &value)
virtual void executeSql(std::string const &sqlStatement)
Class for logical location of a persisted Persistable instance.
virtual void endTransaction(void)
virtual void setPolicy(pexPolicy::Policy::Ptr policy)
virtual std::string const & getPort(void) const
definition of the Trace messaging facilities
SelectEigenView< T >::Type copy(Eigen::EigenBase< T > const &other)
Copy an arbitrary Eigen expression into a new EigenView.
virtual void finishQuery(void)
virtual void outColumn(std::string const &columnName, bool isExpr)
boost::shared_ptr< Policy > Ptr
virtual void groupBy(std::string const &expression)
void * allocateMemory(size_t size)
virtual void startSession(std::string const &location)
long long nsecs(Timescale scale=TAI) const
virtual ~DbStorageImpl(void)
T const & getColumnByPos(int pos)
Location of a persisted Persistable instance in a database.
virtual void startTransaction(void)
std::string const & locString(void) const
void error(std::string const &text, bool mysqlCaused=true)
bool columnIsNull(int pos)
virtual void setRetrieveLocation(LogicalLocation const &location)
virtual std::string const & getPassword(void) const
void condParam(std::string const ¶mName, T const &value)
Interface for DateTime class.
virtual std::string const & getHostname(void) const
virtual void setTableListForQuery(std::vector< std::string > const &tableNameList)
virtual void orderBy(std::string const &expression)
virtual void truncateTable(std::string const &tableName)
#define LSST_EXCEPT(type,...)
static enum_field_types mysqlType
virtual void setColumnToNull(std::string const &columnName)
std::string quote(std::string const &name)
virtual void createTableFromTemplate(std::string const &tableName, std::string const &templateName, bool mayAlreadyExist)
struct tm gmtime(void) const
void stError(std::string const &text)
Interface for DbStorageImpl class.
static enum_field_types mysqlType
virtual void setPersistLocation(LogicalLocation const &location)
void executeQuery(std::string const &query)
virtual void dropTable(std::string const &tableName)
Interface for LogicalLocation class.
virtual std::string const & getDbName(void) const
virtual void insertRow(void)
virtual std::string const & getUsername(void) const
Include files required for standard LSST Exception handling.
Interface for DbStorageLocation class.
virtual void setTableForQuery(std::string const &tableName, bool isExpr)