File indexing completed on 2025-02-23 05:15:16
0001 // 0002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #define SOCI_FIREBIRD_SOURCE 0009 #include "soci/firebird/soci-firebird.h" 0010 #include "firebird/error-firebird.h" 0011 #include "soci/session.h" 0012 #include <locale> 0013 #include <map> 0014 #include <sstream> 0015 #include <string> 0016 0017 using namespace soci; 0018 using namespace soci::details::firebird; 0019 0020 namespace 0021 { 0022 0023 // Helpers of explodeISCConnectString() for reading words from a string. "Word" 0024 // here is defined very loosely as just a sequence of non-space characters. 0025 // 0026 // All these helper functions update the input iterator to point to the first 0027 // character not consumed by them. 0028 0029 // Advance the input iterator until the first non-space character or end of the 0030 // string. 0031 void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end) 0032 { 0033 std::locale const loc; 0034 for (; i != end; ++i) 0035 { 0036 if (!std::isspace(*i, loc)) 0037 break; 0038 } 0039 } 0040 0041 // Return the string of all characters until the first space or the specified 0042 // delimiter. 0043 // 0044 // Throws if the first non-space character after the end of the word is not the 0045 // delimiter. However just returns en empty string, without throwing, if 0046 // nothing is left at all in the string except for white space. 0047 std::string 0048 getWordUntil(std::string const &s, std::string::const_iterator &i, char delim) 0049 { 0050 std::string::const_iterator const end = s.end(); 0051 skipWhiteSpace(i, end); 0052 0053 // We need to handle this case specially because it's not an error if 0054 // nothing at all remains in the string. But if anything does remain, then 0055 // we must have the delimiter. 0056 if (i == end) 0057 return std::string(); 0058 0059 // Simply put anything until the delimiter into the word, stopping at the 0060 // first white space character. 0061 std::string word; 0062 std::locale const loc; 0063 for (; i != end; ++i) 0064 { 0065 if (*i == delim) 0066 break; 0067 0068 if (std::isspace(*i, loc)) 0069 { 0070 skipWhiteSpace(i, end); 0071 if (i == end || *i != delim) 0072 { 0073 std::ostringstream os; 0074 os << "Expected '" << delim << "' at position " 0075 << (i - s.begin() + 1) 0076 << " in Firebird connection string \"" 0077 << s << "\"."; 0078 0079 throw soci_error(os.str()); 0080 } 0081 0082 break; 0083 } 0084 0085 word += *i; 0086 } 0087 0088 if (i == end) 0089 { 0090 std::ostringstream os; 0091 os << "Expected '" << delim 0092 << "' not found before the end of the string " 0093 << "in Firebird connection string \"" 0094 << s << "\"."; 0095 0096 throw soci_error(os.str()); 0097 } 0098 0099 ++i; // Skip the delimiter itself. 0100 0101 return word; 0102 } 0103 0104 // Return a possibly quoted word, i.e. either just a sequence of non-space 0105 // characters or everything inside a double-quoted string. 0106 // 0107 // Throws if the word is quoted and the closing quote is not found. However 0108 // doesn't throw, just returns an empty string if there is nothing left. 0109 std::string 0110 getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i) 0111 { 0112 std::string::const_iterator const end = s.end(); 0113 skipWhiteSpace(i, end); 0114 0115 std::string word; 0116 0117 if (i != end && *i == '"') 0118 { 0119 for (;;) 0120 { 0121 if (++i == end) 0122 { 0123 std::ostringstream os; 0124 os << "Expected '\"' not found before the end of the string " 0125 "in Firebird connection string \"" 0126 << s << "\"."; 0127 0128 throw soci_error(os.str()); 0129 } 0130 0131 if (*i == '"') 0132 { 0133 ++i; 0134 break; 0135 } 0136 0137 word += *i; 0138 } 0139 } 0140 else // Not quoted. 0141 { 0142 std::locale const loc; 0143 for (; i != end; ++i) 0144 { 0145 if (std::isspace(*i, loc)) 0146 break; 0147 0148 word += *i; 0149 } 0150 } 0151 0152 return word; 0153 } 0154 0155 // retrieves parameters from the uniform connect string which is supposed to be 0156 // in the form "key=value[ key2=value2 ...]" and the values may be quoted to 0157 // allow including spaces into them. Notice that currently there is no way to 0158 // include both a space and a double quote in a value. 0159 std::map<std::string, std::string> 0160 explodeISCConnectString(std::string const &connectString) 0161 { 0162 std::map<std::string, std::string> parameters; 0163 0164 std::string key, value; 0165 for (std::string::const_iterator i = connectString.begin(); ; ) 0166 { 0167 key = getWordUntil(connectString, i, '='); 0168 if (key.empty()) 0169 break; 0170 0171 value = getPossiblyQuotedWord(connectString, i); 0172 0173 parameters.insert(std::pair<std::string, std::string>(key, value)); 0174 } 0175 0176 return parameters; 0177 } 0178 0179 // extracts given parameter from map previusly build with explodeISCConnectString 0180 bool getISCConnectParameter(std::map<std::string, std::string> const & m, std::string const & key, 0181 std::string & value) 0182 { 0183 std::map <std::string, std::string> :: const_iterator i; 0184 value.clear(); 0185 0186 i = m.find(key); 0187 0188 if (i != m.end()) 0189 { 0190 value = i->second; 0191 return true; 0192 } 0193 else 0194 { 0195 return false; 0196 } 0197 } 0198 0199 void setDPBOption(std::string& dpb, int const option, std::string const & value) 0200 { 0201 0202 if (dpb.empty()) 0203 { 0204 dpb.append(1, static_cast<char>(isc_dpb_version1)); 0205 } 0206 0207 // now we are adding new option 0208 dpb.append(1, static_cast<char>(option)); 0209 dpb.append(1, static_cast<char>(value.size())); 0210 dpb.append(value); 0211 } 0212 0213 } // namespace anonymous 0214 0215 firebird_session_backend::firebird_session_backend( 0216 connection_parameters const & parameters) : dbhp_(0), trhp_(0) 0217 , decimals_as_strings_(false) 0218 { 0219 // extract connection parameters 0220 std::map<std::string, std::string> 0221 params(explodeISCConnectString(parameters.get_connect_string())); 0222 0223 ISC_STATUS stat[stat_size]; 0224 std::string param; 0225 0226 // preparing connection options 0227 std::string dpb; 0228 if (getISCConnectParameter(params, "user", param)) 0229 { 0230 setDPBOption(dpb, isc_dpb_user_name, param); 0231 } 0232 0233 if (getISCConnectParameter(params, "password", param)) 0234 { 0235 setDPBOption(dpb, isc_dpb_password, param); 0236 } 0237 0238 if (getISCConnectParameter(params, "role", param)) 0239 { 0240 setDPBOption(dpb, isc_dpb_sql_role_name, param); 0241 } 0242 0243 if (getISCConnectParameter(params, "charset", param)) 0244 { 0245 setDPBOption(dpb, isc_dpb_lc_ctype, param); 0246 } 0247 0248 if (getISCConnectParameter(params, "service", param) == false) 0249 { 0250 throw soci_error("Service name not specified."); 0251 } 0252 0253 // connecting data base 0254 if (isc_attach_database(stat, static_cast<short>(param.size()), 0255 const_cast<char*>(param.c_str()), &dbhp_, 0256 static_cast<short>(dpb.size()), const_cast<char*>(dpb.c_str()))) 0257 { 0258 throw_iscerror(stat); 0259 } 0260 0261 if (getISCConnectParameter(params, "decimals_as_strings", param)) 0262 { 0263 decimals_as_strings_ = param == "1" || param == "Y" || param == "y"; 0264 } 0265 } 0266 0267 0268 void firebird_session_backend::begin() 0269 { 0270 if (trhp_ == 0) 0271 { 0272 ISC_STATUS stat[stat_size]; 0273 if (isc_start_transaction(stat, &trhp_, 1, &dbhp_, 0, NULL)) 0274 { 0275 throw_iscerror(stat); 0276 } 0277 } 0278 } 0279 0280 firebird_session_backend::~firebird_session_backend() 0281 { 0282 cleanUp(); 0283 } 0284 0285 bool firebird_session_backend::is_connected() 0286 { 0287 ISC_STATUS stat[stat_size]; 0288 ISC_SCHAR req[] = { isc_info_ods_version, isc_info_end }; 0289 ISC_SCHAR res[256]; 0290 0291 return isc_database_info(stat, &dbhp_, sizeof(req), req, sizeof(res), res) == 0; 0292 } 0293 0294 void firebird_session_backend::commit() 0295 { 0296 ISC_STATUS stat[stat_size]; 0297 0298 if (trhp_ != 0) 0299 { 0300 if (isc_commit_transaction(stat, &trhp_)) 0301 { 0302 throw_iscerror(stat); 0303 } 0304 0305 trhp_ = 0; 0306 } 0307 } 0308 0309 void firebird_session_backend::rollback() 0310 { 0311 ISC_STATUS stat[stat_size]; 0312 0313 if (trhp_ != 0) 0314 { 0315 if (isc_rollback_transaction(stat, &trhp_)) 0316 { 0317 throw_iscerror(stat); 0318 } 0319 0320 trhp_ = 0; 0321 } 0322 } 0323 0324 isc_tr_handle* firebird_session_backend::current_transaction() 0325 { 0326 // It will do nothing if we're already inside a transaction. 0327 begin(); 0328 0329 return &trhp_; 0330 } 0331 0332 void firebird_session_backend::cleanUp() 0333 { 0334 ISC_STATUS stat[stat_size]; 0335 0336 // at the end of session our transaction is finally commited. 0337 if (trhp_ != 0) 0338 { 0339 if (isc_commit_transaction(stat, &trhp_)) 0340 { 0341 throw_iscerror(stat); 0342 } 0343 0344 trhp_ = 0; 0345 } 0346 0347 if (isc_detach_database(stat, &dbhp_)) 0348 { 0349 throw_iscerror(stat); 0350 } 0351 0352 dbhp_ = 0L; 0353 } 0354 0355 bool firebird_session_backend::get_next_sequence_value( 0356 session & s, std::string const & sequence, long long & value) 0357 { 0358 // We could use isq_execute2() directly but this is even simpler. 0359 s << "select next value for " + sequence + " from rdb$database", 0360 into(value); 0361 0362 return true; 0363 } 0364 0365 firebird_statement_backend * firebird_session_backend::make_statement_backend() 0366 { 0367 return new firebird_statement_backend(*this); 0368 } 0369 0370 details::rowid_backend* firebird_session_backend::make_rowid_backend() 0371 { 0372 throw soci_error("RowIDs are not supported"); 0373 } 0374 0375 firebird_blob_backend * firebird_session_backend::make_blob_backend() 0376 { 0377 return new firebird_blob_backend(*this); 0378 }