// // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // #define SOCI_FIREBIRD_SOURCE #include "soci/firebird/soci-firebird.h" #include "firebird/error-firebird.h" #include "soci/session.h" #include #include #include #include using namespace soci; using namespace soci::details::firebird; namespace { // Helpers of explodeISCConnectString() for reading words from a string. "Word" // here is defined very loosely as just a sequence of non-space characters. // // All these helper functions update the input iterator to point to the first // character not consumed by them. // Advance the input iterator until the first non-space character or end of the // string. void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end) { std::locale const loc; for (; i != end; ++i) { if (!std::isspace(*i, loc)) break; } } // Return the string of all characters until the first space or the specified // delimiter. // // Throws if the first non-space character after the end of the word is not the // delimiter. However just returns en empty string, without throwing, if // nothing is left at all in the string except for white space. std::string getWordUntil(std::string const &s, std::string::const_iterator &i, char delim) { std::string::const_iterator const end = s.end(); skipWhiteSpace(i, end); // We need to handle this case specially because it's not an error if // nothing at all remains in the string. But if anything does remain, then // we must have the delimiter. if (i == end) return std::string(); // Simply put anything until the delimiter into the word, stopping at the // first white space character. std::string word; std::locale const loc; for (; i != end; ++i) { if (*i == delim) break; if (std::isspace(*i, loc)) { skipWhiteSpace(i, end); if (i == end || *i != delim) { std::ostringstream os; os << "Expected '" << delim << "' at position " << (i - s.begin() + 1) << " in Firebird connection string \"" << s << "\"."; throw soci_error(os.str()); } break; } word += *i; } if (i == end) { std::ostringstream os; os << "Expected '" << delim << "' not found before the end of the string " << "in Firebird connection string \"" << s << "\"."; throw soci_error(os.str()); } ++i; // Skip the delimiter itself. return word; } // Return a possibly quoted word, i.e. either just a sequence of non-space // characters or everything inside a double-quoted string. // // Throws if the word is quoted and the closing quote is not found. However // doesn't throw, just returns an empty string if there is nothing left. std::string getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i) { std::string::const_iterator const end = s.end(); skipWhiteSpace(i, end); std::string word; if (i != end && *i == '"') { for (;;) { if (++i == end) { std::ostringstream os; os << "Expected '\"' not found before the end of the string " "in Firebird connection string \"" << s << "\"."; throw soci_error(os.str()); } if (*i == '"') { ++i; break; } word += *i; } } else // Not quoted. { std::locale const loc; for (; i != end; ++i) { if (std::isspace(*i, loc)) break; word += *i; } } return word; } // retrieves parameters from the uniform connect string which is supposed to be // in the form "key=value[ key2=value2 ...]" and the values may be quoted to // allow including spaces into them. Notice that currently there is no way to // include both a space and a double quote in a value. std::map explodeISCConnectString(std::string const &connectString) { std::map parameters; std::string key, value; for (std::string::const_iterator i = connectString.begin(); ; ) { key = getWordUntil(connectString, i, '='); if (key.empty()) break; value = getPossiblyQuotedWord(connectString, i); parameters.insert(std::pair(key, value)); } return parameters; } // extracts given parameter from map previusly build with explodeISCConnectString bool getISCConnectParameter(std::map const & m, std::string const & key, std::string & value) { std::map :: const_iterator i; value.clear(); i = m.find(key); if (i != m.end()) { value = i->second; return true; } else { return false; } } void setDPBOption(std::string& dpb, int const option, std::string const & value) { if (dpb.empty()) { dpb.append(1, static_cast(isc_dpb_version1)); } // now we are adding new option dpb.append(1, static_cast(option)); dpb.append(1, static_cast(value.size())); dpb.append(value); } } // namespace anonymous firebird_session_backend::firebird_session_backend( connection_parameters const & parameters) : dbhp_(0), trhp_(0) , decimals_as_strings_(false) { // extract connection parameters std::map params(explodeISCConnectString(parameters.get_connect_string())); ISC_STATUS stat[stat_size]; std::string param; // preparing connection options std::string dpb; if (getISCConnectParameter(params, "user", param)) { setDPBOption(dpb, isc_dpb_user_name, param); } if (getISCConnectParameter(params, "password", param)) { setDPBOption(dpb, isc_dpb_password, param); } if (getISCConnectParameter(params, "role", param)) { setDPBOption(dpb, isc_dpb_sql_role_name, param); } if (getISCConnectParameter(params, "charset", param)) { setDPBOption(dpb, isc_dpb_lc_ctype, param); } if (getISCConnectParameter(params, "service", param) == false) { throw soci_error("Service name not specified."); } // connecting data base if (isc_attach_database(stat, static_cast(param.size()), const_cast(param.c_str()), &dbhp_, static_cast(dpb.size()), const_cast(dpb.c_str()))) { throw_iscerror(stat); } if (getISCConnectParameter(params, "decimals_as_strings", param)) { decimals_as_strings_ = param == "1" || param == "Y" || param == "y"; } } void firebird_session_backend::begin() { if (trhp_ == 0) { ISC_STATUS stat[stat_size]; if (isc_start_transaction(stat, &trhp_, 1, &dbhp_, 0, NULL)) { throw_iscerror(stat); } } } firebird_session_backend::~firebird_session_backend() { cleanUp(); } void firebird_session_backend::commit() { ISC_STATUS stat[stat_size]; if (trhp_ != 0) { if (isc_commit_transaction(stat, &trhp_)) { throw_iscerror(stat); } trhp_ = 0; } } void firebird_session_backend::rollback() { ISC_STATUS stat[stat_size]; if (trhp_ != 0) { if (isc_rollback_transaction(stat, &trhp_)) { throw_iscerror(stat); } trhp_ = 0; } } isc_tr_handle* firebird_session_backend::current_transaction() { // It will do nothing if we're already inside a transaction. begin(); return &trhp_; } void firebird_session_backend::cleanUp() { ISC_STATUS stat[stat_size]; // at the end of session our transaction is finally commited. if (trhp_ != 0) { if (isc_commit_transaction(stat, &trhp_)) { throw_iscerror(stat); } trhp_ = 0; } if (isc_detach_database(stat, &dbhp_)) { throw_iscerror(stat); } dbhp_ = 0L; } bool firebird_session_backend::get_next_sequence_value( session & s, std::string const & sequence, long & value) { // We could use isq_execute2() directly but this is even simpler. s << "select next value for " + sequence + " from rdb$database", into(value); return true; } firebird_statement_backend * firebird_session_backend::make_statement_backend() { return new firebird_statement_backend(*this); } details::rowid_backend* firebird_session_backend::make_rowid_backend() { throw soci_error("RowIDs are not supported"); } firebird_blob_backend * firebird_session_backend::make_blob_backend() { return new firebird_blob_backend(*this); }