/* * Copyright (C) 2011 Emweb bvba, Kessel-Lo, Belgium. * * See the LICENSE file for terms of use. * * Originally contributed by Lukasz Matuszewski */ #include #include "Wt/Dbo/Exception" #include #include #include #ifdef WIN32 #define snprintf _snprintf #endif //#define DEBUG(x) x #define DEBUG(x) #define bindErr(x, y) \ std::cerr << this << " bind " << x << " " << y << std::endl; #define resultErr(x, y) \ std::cerr << this << " getResult " << x << " " << y << std::endl; #include namespace Wt { namespace Dbo { namespace backend { class FirebirdException : public Wt::Dbo::Exception { public: FirebirdException(const std::string& msg) : Wt::Dbo::Exception(msg) { } }; class Firebird_impl { public: IBPP::Database m_db; IBPP::Transaction m_tra; ~Firebird_impl() { if (m_db->Connected()) m_db->Disconnect(); } Firebird_impl() { } Firebird_impl(IBPP::Database db) : m_db(db) { } }; class FirebirdStatement : public SqlStatement { public: FirebirdStatement(Firebird& conn, const std::string& sql) : conn_(conn), sql_(convertToNumberedPlaceholders(sql)) { lastId_ = -1; row_ = affectedRows_ = 0; snprintf(name_, 64, "SQL%p%08X", this, rand()); DEBUG(std::cerr << this << " for: " << sql_ << std::endl); IBPP::Transaction tr = conn_.impl_->m_tra; m_stmt = IBPP::StatementFactory(conn_.impl_->m_db, tr); if (!m_stmt.intf()) throw FirebirdException("Could not create a IBPP::Statement"); try { m_stmt->Prepare(sql_); } catch(IBPP::LogicException &e) { throw FirebirdException(e.what()); } } virtual ~FirebirdStatement() { } virtual void reset() { } // The first column index is 1! virtual void bind(int column, const std::string& value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, value); } virtual void bind(int column, short value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, value); } virtual void bind(int column, int value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, value); } virtual void bind(int column, long long value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, (int64_t) value); } virtual void bind(int column, float value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, value); } virtual void bind(int column, double value) { DEBUG(bindErr(column, value)); m_stmt->Set(column + 1, value); } int getMilliSeconds(const boost::posix_time::time_duration &d) { using namespace boost::posix_time; time_duration::fractional_seconds_type ticks_per_msec = time_duration::ticks_per_second() / 1000; time_duration::fractional_seconds_type msec = d.fractional_seconds(); return msec / ticks_per_msec; } virtual void bind(int column, const boost::posix_time::time_duration & value) { DEBUG(bindErr(column, boost::posix_time::to_simple_string(value))); int h = value.hours(); int m = value.minutes(); int s = value.seconds(); int ms = getMilliSeconds(value); IBPP::Time t(h, m, s, ms * 10); m_stmt->Set(column + 1, t); } virtual void bind(int column, const boost::posix_time::ptime& value, SqlDateTimeType type) { DEBUG(bindErr(column, boost::posix_time::to_simple_string(value))); if (type == SqlDate) { IBPP::Date idate(value.date().year(), value.date().month(), value.date().day()); m_stmt->Set(column + 1, idate); } else { int h = value.time_of_day().hours(); int m = value.time_of_day().minutes(); int s = value.time_of_day().seconds(); int ms = getMilliSeconds(value.time_of_day()); IBPP::Timestamp ts(value.date().year(), value.date().month(), value.date().day(), h, m, s, ms * 10); m_stmt->Set(column + 1, ts); } } virtual void bind(int column, const std::vector& value) { DEBUG(bindErr(column, std::string(" (blob, size=") + value.size() + ")")); IBPP::Blob b = IBPP::BlobFactory(conn_.impl_->m_db, conn_.impl_->m_tra); b->Create(); if (value.size() > 0) b->Write((void *)&value.front(), value.size()); b->Close(); m_stmt->Set(column + 1, b); } virtual void bindNull(int column) { DEBUG(bindErr(column, "null")); m_stmt->SetNull(column + 1); } virtual void execute() { if (conn_.showQueries()) std::cerr << sql_ << std::endl; try { m_stmt->Execute(); affectedRows_ = m_stmt->AffectedRows(); row_ = 0; } catch(IBPP::LogicException &e) { throw FirebirdException(e.what()); } bool isInsertReturningId = false; if (affectedRows_ == 0) { const std::string returning = conn_.autoincrementInsertSuffix(); std::size_t j = sql_.rfind(returning); if (j != std::string::npos && sql_.find(' ', j + returning.length()) == std::string::npos) { if (m_stmt->Columns() == 1) { lastId_ = -1; m_stmt->Get(1, lastId_); } } } } virtual long long insertedId() { return lastId_; } virtual int affectedRowCount() { return affectedRows_; } virtual bool nextRow() { try { return m_stmt->Fetch(); } catch(IBPP::Exception &e) { throw FirebirdException(e.what()); } } void getString(int column, std::string *value, int size) { m_stmt->Get(column, *value); } virtual bool getResult(int column, std::string *value, int size) { if (m_stmt->IsNull(++column)) return false; getString(column, value, size); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, short *value) { if (m_stmt->IsNull(++column)) return false; m_stmt->Get(column, *value); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, int *value) { if (m_stmt->IsNull(++column)) return false; m_stmt->Get(column, *value); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, long long *value) { if (m_stmt->IsNull(++column)) return false; m_stmt->Get(column, *((int64_t *)value)); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, float *value) { if (m_stmt->IsNull(++column)) return false; m_stmt->Get(column, *value); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, double *value) { if (m_stmt->IsNull(++column)) return false; m_stmt->Get(column, *value); DEBUG(resultErr(column, *value)); return true; } virtual bool getResult(int column, boost::posix_time::ptime *value, SqlDateTimeType type) { using namespace boost::posix_time; using namespace boost::gregorian; if (m_stmt->IsNull(++column)) return false; switch(type) { case SqlDate: { IBPP::Date d; m_stmt->Get(column, d); *value = ptime(date(d.Year(), d.Month(), d.Day())); break; } case SqlDateTime: { IBPP::Timestamp tm; m_stmt->Get(column, tm); *value = ptime(date(tm.Year(), tm.Month(), tm.Day()), hours(tm.Hours()) + minutes(tm.Minutes()) + seconds(tm.Seconds()) + microseconds(tm.SubSeconds() * 100)); break; } } DEBUG(resultErr(column, to_simple_string(*value))); return true; } virtual bool getResult(int column, boost::posix_time::time_duration *value) { using namespace boost::posix_time; if (m_stmt->IsNull(++column)) return false; IBPP::Time t; m_stmt->Get(column, t); *value = boost::posix_time::hours(t.Hours()) + boost::posix_time::minutes(t.Minutes()) + boost::posix_time::seconds(t.Seconds()) + boost::posix_time::microseconds(t.SubSeconds() * 100); DEBUG(resultErr(column, to_simple_string(*value))); return true; } virtual bool getResult(int column, std::vector *value, int size) { if (m_stmt->IsNull(++column)) return false; IBPP::Blob b = IBPP::BlobFactory(conn_.impl_->m_db, conn_.impl_->m_tra); m_stmt->Get(column, b); b->Open(); int largest = -1, segments = -1; b->Info(&size, &largest, &segments); value->resize(size); if (size > 0) b->Read((void *)&value->front(), size); DEBUG(resultErr(column, std::string("blob (size=") + value.size())); return true; } virtual std::string sql() const { return sql_; } private: Firebird& conn_; std::string sql_; IBPP::Statement m_stmt; char name_[64]; int lastId_, row_, affectedRows_; void setValue(int column, const std::string& value) { m_stmt->Set(column + 1, value); } std::string convertToNumberedPlaceholders(const std::string& sql) { return sql; } }; Firebird::Firebird() : m_writableTransaction(true) { impl_ = new Firebird_impl(); } Firebird::Firebird(const std::string& ServerName, const std::string& DatabaseName, const std::string& UserName, const std::string& UserPassword, const std::string& RoleName, const std::string& CharSet, const std::string& CreateParams) : m_writableTransaction(true) { IBPP::Database db = IBPP::DatabaseFactory(ServerName, DatabaseName, UserName, UserPassword, RoleName, CharSet, CreateParams); db->Connect(); impl_ = new Firebird_impl(db); } Firebird::Firebird(IBPP::Database db) { impl_ = new Firebird_impl(db); } Firebird::Firebird(const Firebird& other) : SqlConnection(other), m_writableTransaction(other.m_writableTransaction) { IBPP::Database db = IBPP::DatabaseFactory(other.impl_->m_db->ServerName(), other.impl_->m_db->DatabaseName(), other.impl_->m_db->Username(), other.impl_->m_db->UserPassword(), other.impl_->m_db->RoleName(), other.impl_->m_db->CharSet(), other.impl_->m_db->CreateParams()); db->Connect(); impl_ = new Firebird_impl(db); } bool Firebird::connect(const std::string& ServerName, const std::string& DatabaseName, const std::string& UserName, const std::string& UserPassword, const std::string& RoleName, const std::string& CharSet, const std::string& CreateParams) { IBPP::Database db = IBPP::DatabaseFactory(ServerName, DatabaseName, UserName, UserPassword, RoleName, CharSet, CreateParams); db->Connect(); impl_->m_db = db; return true; } Firebird::~Firebird() { clearStatementCache(); delete impl_; } Firebird *Firebird::clone() const { return new Firebird(*this); } SqlStatement *Firebird::prepareStatement(const std::string& sql) { return new FirebirdStatement(*this, sql); } std::string Firebird::autoincrementType() const { return "bigint"; } std::string Firebird::autoincrementSql() const { return std::string(); } std::vector Firebird::autoincrementCreateSequenceSql(const std::string &table, const std::string &id) const { std::vector sql; std::string sequenceId = "sequence_" + table + "_" + id; sql.push_back(std::string("create generator ") + sequenceId + ";"); sql.push_back(std::string("set generator ") + sequenceId + " to 0;"); std::stringstream trigger; trigger << "CREATE TRIGGER sequence_trigger_" << table << " FOR \"" << table << "\"" << " ACTIVE BEFORE INSERT POSITION 0" << " AS" << " BEGIN" << " if (NEW.\"" << id << "\" is NULL)" << " then NEW.\"" << id << "\" " << " = GEN_ID(" << sequenceId << ", 1);" << " END "; sql.push_back(trigger.str()); return sql; } std::vector Firebird::autoincrementDropSequenceSql(const std::string &table, const std::string &id) const { std::vector sql; sql.push_back(std::string("drop trigger sequence_trigger_") + table); sql.push_back(std::string("drop generator sequence_") + table + "_" + id); return sql; } std::string Firebird::autoincrementInsertSuffix() const { return " returning "; } const char *Firebird::dateTimeType(SqlDateTimeType type) const { switch (type) { case SqlDate: return "date"; case SqlDateTime: return "timestamp"; case SqlTime: return "time"; } std::stringstream ss; ss << __FILE__ << ":" << __LINE__ << ": implementation error"; throw FirebirdException(ss.str()); } const char *Firebird::blobType() const { return "blob"; } void Firebird::startTransaction() { if (!impl_->m_tra.intf()) { if (m_writableTransaction) { impl_->m_tra = IBPP::TransactionFactory(impl_->m_db, IBPP::amWrite, IBPP::ilReadCommitted, IBPP::lrWait); } else { impl_->m_tra = IBPP::TransactionFactory(impl_->m_db, IBPP::amRead, IBPP::ilReadCommitted, IBPP::lrWait); } } if (!impl_->m_tra.intf()) throw new FirebirdException("Could not start transaction!"); impl_->m_tra->Start(); } void Firebird::commitTransaction() { if (!impl_->m_tra.intf()) throw new FirebirdException("Transaction was not started yet!"); impl_->m_tra->Commit(); } void Firebird::rollbackTransaction() { if (!impl_->m_tra.intf()) throw new FirebirdException("Transaction was not started yet!"); impl_->m_tra->Rollback(); } const char *Firebird::textType() const { return "blob sub_type text"; } const char *Firebird::booleanType() const { return "smallint"; } void Firebird::prepareForDropTables() { clearStatementCache(); } bool Firebird::usesRowsFromTo() const { return true; } IBPP::Database Firebird::connection() { return impl_->m_db; } } } }