00001 // 00002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski 00003 // Distributed under the Boost Software License, Version 1.0. 00004 // (See accompanying file LICENSE_1_0.txt or copy at 00005 // http://www.boost.org/LICENSE_1_0.txt) 00006 // 00007 00008 #define SOCI_FIREBIRD_SOURCE 00009 #include "soci-firebird.h" 00010 #include "error-firebird.h" 00011 #include <cctype> 00012 #include <sstream> 00013 00014 using namespace soci; 00015 using namespace soci::details; 00016 using namespace soci::details::firebird; 00017 00018 firebird_statement_backend::firebird_statement_backend(firebird_session_backend &session) 00019 : session_(session), stmtp_(0), sqldap_(NULL), sqlda2p_(NULL), 00020 boundByName_(false), boundByPos_(false), rowsFetched_(0), 00021 intoType_(eStandard), useType_(eStandard), procedure_(false) 00022 {} 00023 00024 void firebird_statement_backend::prepareSQLDA(XSQLDA ** sqldap, int size) 00025 { 00026 if (*sqldap != NULL) 00027 { 00028 *sqldap = reinterpret_cast<XSQLDA*>(realloc(*sqldap, XSQLDA_LENGTH(size))); 00029 } 00030 else 00031 { 00032 *sqldap = reinterpret_cast<XSQLDA*>(malloc(XSQLDA_LENGTH(size))); 00033 } 00034 00035 (*sqldap)->sqln = size; 00036 (*sqldap)->version = 1; 00037 } 00038 00039 void firebird_statement_backend::alloc() 00040 { 00041 ISC_STATUS stat[stat_size]; 00042 00043 if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &stmtp_)) 00044 { 00045 throw_iscerror(stat); 00046 } 00047 } 00048 00049 void firebird_statement_backend::clean_up() 00050 { 00051 ISC_STATUS stat[stat_size]; 00052 00053 if (stmtp_ != NULL) 00054 { 00055 if (isc_dsql_free_statement(stat, &stmtp_, DSQL_drop)) 00056 { 00057 throw_iscerror(stat); 00058 } 00059 stmtp_ = NULL; 00060 } 00061 00062 if (sqldap_ != NULL) 00063 { 00064 free(sqldap_); 00065 sqldap_ = NULL; 00066 } 00067 00068 if (sqlda2p_ != NULL) 00069 { 00070 free(sqlda2p_); 00071 sqlda2p_ = NULL; 00072 } 00073 } 00074 00075 void firebird_statement_backend::rewriteParameters( 00076 std::string const & src, std::vector<char> & dst) 00077 { 00078 std::vector<char>::iterator dst_it = dst.begin(); 00079 00080 // rewrite the query by transforming all named parameters into 00081 // the Firebird question marks (:abc -> ?, etc.) 00082 00083 enum { eNormal, eInQuotes, eInName } state = eNormal; 00084 00085 std::string name; 00086 int position = 0; 00087 00088 for (std::string::const_iterator it = src.begin(), end = src.end(); 00089 it != end; ++it) 00090 { 00091 switch (state) 00092 { 00093 case eNormal: 00094 if (*it == '\'') 00095 { 00096 *dst_it++ = *it; 00097 state = eInQuotes; 00098 } 00099 else if (*it == ':') 00100 { 00101 state = eInName; 00102 } 00103 else // regular character, stay in the same state 00104 { 00105 *dst_it++ = *it; 00106 } 00107 break; 00108 case eInQuotes: 00109 if (*it == '\'') 00110 { 00111 *dst_it++ = *it; 00112 state = eNormal; 00113 } 00114 else // regular quoted character 00115 { 00116 *dst_it++ = *it; 00117 } 00118 break; 00119 case eInName: 00120 if (std::isalnum(*it) || *it == '_') 00121 { 00122 name += *it; 00123 } 00124 else // end of name 00125 { 00126 names_.insert(std::pair<std::string, int>(name, position++)); 00127 name.clear(); 00128 *dst_it++ = '?'; 00129 *dst_it++ = *it; 00130 state = eNormal; 00131 } 00132 break; 00133 } 00134 } 00135 00136 if (state == eInName) 00137 { 00138 names_.insert(std::pair<std::string, int>(name, position++)); 00139 *dst_it++ = '?'; 00140 } 00141 00142 *dst_it = '\0'; 00143 } 00144 00145 namespace 00146 { 00147 int statementType(isc_stmt_handle stmt) 00148 { 00149 int stype; 00150 int length; 00151 char type_item[] = {isc_info_sql_stmt_type}; 00152 char res_buffer[8]; 00153 00154 ISC_STATUS stat[stat_size]; 00155 00156 if (isc_dsql_sql_info(stat, &stmt, sizeof(type_item), 00157 type_item, sizeof(res_buffer), res_buffer)) 00158 { 00159 throw_iscerror(stat); 00160 } 00161 00162 if (res_buffer[0] == isc_info_sql_stmt_type) 00163 { 00164 length = isc_vax_integer(res_buffer+1, 2); 00165 stype = isc_vax_integer(res_buffer+3, length); 00166 } 00167 else 00168 { 00169 throw soci_error("Can't determine statement type."); 00170 } 00171 00172 return stype; 00173 } 00174 } 00175 00176 void firebird_statement_backend::rewriteQuery( 00177 std::string const &query, std::vector<char> &buffer) 00178 { 00179 // buffer for temporary query 00180 std::vector<char> tmpQuery; 00181 std::vector<char>::iterator qItr; 00182 00183 // buffer for query with named parameters changed to standard ones 00184 std::vector<char> rewQuery(query.size() + 1); 00185 00186 // take care of named parameters in original query 00187 rewriteParameters(query, rewQuery); 00188 00189 std::string const prefix("execute procedure "); 00190 std::string const prefix2("select * from "); 00191 00192 // for procedures, we are preparing statement to determine 00193 // type of procedure. 00194 if (procedure_) 00195 { 00196 tmpQuery.resize(prefix.size() + rewQuery.size()); 00197 qItr = tmpQuery.begin(); 00198 std::copy(prefix.begin(), prefix.end(), qItr); 00199 qItr += prefix.size(); 00200 } 00201 else 00202 { 00203 tmpQuery.resize(rewQuery.size()); 00204 qItr = tmpQuery.begin(); 00205 } 00206 00207 // prepare temporary query 00208 std::copy(rewQuery.begin(), rewQuery.end(), qItr); 00209 00210 // preparing buffers for output parameters 00211 if (sqldap_ == NULL) 00212 { 00213 prepareSQLDA(&sqldap_); 00214 } 00215 00216 ISC_STATUS stat[stat_size]; 00217 isc_stmt_handle tmpStmtp = 0; 00218 00219 // allocate temporary statement to determine its type 00220 if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &tmpStmtp)) 00221 { 00222 throw_iscerror(stat); 00223 } 00224 00225 // prepare temporary statement 00226 if (isc_dsql_prepare(stat, &(session_.trhp_), &tmpStmtp, 0, 00227 &tmpQuery[0], SQL_DIALECT_V6, sqldap_)) 00228 { 00229 throw_iscerror(stat); 00230 } 00231 00232 // get statement type 00233 int stType = statementType(tmpStmtp); 00234 00235 // free temporary prepared statement 00236 if (isc_dsql_free_statement(stat, &tmpStmtp, DSQL_drop)) 00237 { 00238 throw_iscerror(stat); 00239 } 00240 00241 // take care of special cases 00242 if (procedure_) 00243 { 00244 // for procedures that return values, we need to use correct syntax 00245 if (sqldap_->sqld != 0) 00246 { 00247 // this is "select" procedure, so we have to change syntax 00248 buffer.resize(prefix2.size() + rewQuery.size()); 00249 qItr = buffer.begin(); 00250 std::copy(prefix2.begin(), prefix2.end(), qItr); 00251 qItr += prefix2.size(); 00252 std::copy(rewQuery.begin(), rewQuery.end(), qItr); 00253 00254 // that won't be needed anymore 00255 procedure_ = false; 00256 00257 return; 00258 } 00259 } 00260 else 00261 { 00262 // this is not procedure, so syntax is ok except for named 00263 // parameters in ddl 00264 if (stType == isc_info_sql_stmt_ddl) 00265 { 00266 // this statement is a DDL - we can't rewrite named parameters 00267 // so, we will use original query 00268 buffer.resize(query.size() + 1); 00269 std::copy(query.begin(), query.end(), buffer.begin()); 00270 00271 // that won't be needed anymore 00272 procedure_ = false; 00273 00274 return; 00275 } 00276 } 00277 00278 // here we know, that temporary query is OK, so we leave it as is 00279 buffer.resize(tmpQuery.size()); 00280 std::copy(tmpQuery.begin(), tmpQuery.end(), buffer.begin()); 00281 00282 // that won't be needed anymore 00283 procedure_ = false; 00284 } 00285 00286 void firebird_statement_backend::prepare(std::string const & query, 00287 statement_type /* eType */) 00288 { 00289 // clear named parametes 00290 names_.clear(); 00291 00292 std::vector<char> queryBuffer; 00293 00294 // modify query's syntax and prepare buffer for use with 00295 // firebird's api 00296 rewriteQuery(query, queryBuffer); 00297 00298 ISC_STATUS stat[stat_size]; 00299 00300 // prepare real statement 00301 if (isc_dsql_prepare(stat, &(session_.trhp_), &stmtp_, 0, 00302 &queryBuffer[0], SQL_DIALECT_V6, sqldap_)) 00303 { 00304 throw_iscerror(stat); 00305 } 00306 00307 if (sqldap_->sqln < sqldap_->sqld) 00308 { 00309 // sqlda is too small for all columns. it must be reallocated 00310 prepareSQLDA(&sqldap_, sqldap_->sqld); 00311 00312 if (isc_dsql_describe(stat, &stmtp_, SQL_DIALECT_V6, sqldap_)) 00313 { 00314 throw_iscerror(stat); 00315 } 00316 } 00317 00318 // preparing input parameters 00319 if (sqlda2p_ == NULL) 00320 { 00321 prepareSQLDA(&sqlda2p_); 00322 } 00323 00324 if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_)) 00325 { 00326 throw_iscerror(stat); 00327 } 00328 00329 if (sqlda2p_->sqln < sqlda2p_->sqld) 00330 { 00331 // sqlda is too small for all columns. it must be reallocated 00332 prepareSQLDA(&sqlda2p_, sqlda2p_->sqld); 00333 00334 if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_)) 00335 { 00336 throw_iscerror(stat); 00337 } 00338 } 00339 00340 // prepare buffers for indicators 00341 inds_.clear(); 00342 inds_.resize(sqldap_->sqld); 00343 00344 // reset types of into buffers 00345 intoType_ = eStandard; 00346 intos_.resize(0); 00347 00348 // reset types of use buffers 00349 useType_ = eStandard; 00350 uses_.resize(0); 00351 } 00352 00353 00354 namespace 00355 { 00356 void checkSize(std::size_t actual, std::size_t expected, 00357 std::string const & name) 00358 { 00359 if (actual != expected) 00360 { 00361 std::ostringstream msg; 00362 msg << "Incorrect number of " << name << " variables. " 00363 << "Expected " << expected << ", got " << actual; 00364 throw soci_error(msg.str()); 00365 } 00366 } 00367 } 00368 00369 statement_backend::exec_fetch_result 00370 firebird_statement_backend::execute(int number) 00371 { 00372 ISC_STATUS stat[stat_size]; 00373 XSQLDA *t = NULL; 00374 00375 std::size_t usize = uses_.size(); 00376 00377 // do we have enough into variables ? 00378 checkSize(intos_.size(), sqldap_->sqld, "into"); 00379 // do we have enough use variables ? 00380 checkSize(usize, sqlda2p_->sqld, "use"); 00381 00382 // do we have parameters ? 00383 if (sqlda2p_->sqld) 00384 { 00385 t = sqlda2p_; 00386 00387 if (useType_ == eStandard) 00388 { 00389 for (std::size_t col=0; col<usize; ++col) 00390 { 00391 static_cast<firebird_standard_use_type_backend*>(uses_[col])->exchangeData(); 00392 } 00393 } 00394 } 00395 00396 // make sure there is no active cursor 00397 if (isc_dsql_free_statement(stat, &stmtp_, DSQL_close)) 00398 { 00399 // ignore attempt to close already closed cursor 00400 if (check_iscerror(stat, isc_dsql_cursor_close_err) == false) 00401 { 00402 throw_iscerror(stat); 00403 } 00404 } 00405 00406 if (useType_ == eVector) 00407 { 00408 // Here we have to explicitly loop to achieve the 00409 // effect of inserting or updating with vector use elements. 00410 std::size_t rows = static_cast<firebird_vector_use_type_backend*>(uses_[0])->size(); 00411 for (std::size_t row=0; row < rows; ++row) 00412 { 00413 // first we have to prepare input parameters 00414 for (std::size_t col=0; col<usize; ++col) 00415 { 00416 static_cast<firebird_vector_use_type_backend*>(uses_[col])->exchangeData(row); 00417 } 00418 00419 // then execute query 00420 if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t)) 00421 { 00422 throw_iscerror(stat); 00423 } 00424 00425 // soci does not allow bulk insert/update and bulk select operations 00426 // in same query. So here, we know that into elements are not 00427 // vectors. So, there is no need to fetch data here. 00428 } 00429 } 00430 else 00431 { 00432 // use elements aren't vectors 00433 if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t)) 00434 { 00435 throw_iscerror(stat); 00436 } 00437 } 00438 00439 if (sqldap_->sqld) 00440 { 00441 // query may return some data 00442 if (number > 0) 00443 { 00444 // number contains size of input variables, so we may fetch() data here 00445 return fetch(number); 00446 } 00447 else 00448 { 00449 // execute(0) was meant to only perform the query 00450 return ef_success; 00451 } 00452 } 00453 else 00454 { 00455 // query can't return any data 00456 return ef_no_data; 00457 } 00458 } 00459 00460 statement_backend::exec_fetch_result 00461 firebird_statement_backend::fetch(int number) 00462 { 00463 ISC_STATUS stat[stat_size]; 00464 00465 for (size_t i = 0; i<static_cast<unsigned int>(sqldap_->sqld); ++i) 00466 { 00467 inds_[i].resize(number > 0 ? number : 1); 00468 } 00469 00470 // Here we have to explicitly loop to achieve the effect of fetching 00471 // vector into elements. After each fetch, we have to exchange data 00472 // with into buffers. 00473 rowsFetched_ = 0; 00474 for (int i = 0; i < number; ++i) 00475 { 00476 long fetch_stat = isc_dsql_fetch(stat, &stmtp_, SQL_DIALECT_V6, sqldap_); 00477 00478 // there is more data to read 00479 if (fetch_stat == 0) 00480 { 00481 ++rowsFetched_; 00482 exchangeData(true, i); 00483 } 00484 else if (fetch_stat == 100L) 00485 { 00486 return ef_no_data; 00487 } 00488 else 00489 { 00490 // error 00491 throw_iscerror(stat); 00492 return ef_no_data; // unreachable, for compiler only 00493 } 00494 } // for 00495 00496 return ef_success; 00497 } 00498 00499 // here we put data fetched from database into user buffers 00500 void firebird_statement_backend::exchangeData(bool gotData, int row) 00501 { 00502 if (gotData) 00503 { 00504 for (size_t i = 0; i < static_cast<unsigned int>(sqldap_->sqld); ++i) 00505 { 00506 // first save indicators 00507 if (((sqldap_->sqlvar+i)->sqltype & 1) == 0) 00508 { 00509 // there is no indicator for this column 00510 inds_[i][row] = i_ok; 00511 } 00512 else if (*((sqldap_->sqlvar+i)->sqlind) == 0) 00513 { 00514 inds_[i][row] = i_ok; 00515 } 00516 else if (*((sqldap_->sqlvar+i)->sqlind) == -1) 00517 { 00518 inds_[i][row] = i_null; 00519 } 00520 else 00521 { 00522 throw soci_error("Unknown state in firebird_statement_backend::exchangeData()"); 00523 } 00524 00525 // then deal with data 00526 if (inds_[i][row] != i_null) 00527 { 00528 if (intoType_ == eVector) 00529 { 00530 static_cast<firebird_vector_into_type_backend*>( 00531 intos_[i])->exchangeData(row); 00532 } 00533 else 00534 { 00535 static_cast<firebird_standard_into_type_backend*>( 00536 intos_[i])->exchangeData(); 00537 } 00538 } 00539 } 00540 } 00541 } 00542 00543 int firebird_statement_backend::get_number_of_rows() 00544 { 00545 return rowsFetched_; 00546 } 00547 00548 std::string firebird_statement_backend::rewrite_for_procedure_call( 00549 std::string const &query) 00550 { 00551 procedure_ = true; 00552 return query; 00553 } 00554 00555 int firebird_statement_backend::prepare_for_describe() 00556 { 00557 return static_cast<int>(sqldap_->sqld); 00558 } 00559 00560 void firebird_statement_backend::describe_column(int colNum, 00561 data_type & type, std::string & columnName) 00562 { 00563 XSQLVAR * var = sqldap_->sqlvar+(colNum-1); 00564 00565 columnName.assign(var->aliasname, var->aliasname_length); 00566 00567 switch (var->sqltype & ~1) 00568 { 00569 case SQL_TEXT: 00570 case SQL_VARYING: 00571 type = dt_string; 00572 break; 00573 case SQL_TYPE_DATE: 00574 case SQL_TYPE_TIME: 00575 case SQL_TIMESTAMP: 00576 type = dt_date; 00577 break; 00578 case SQL_FLOAT: 00579 case SQL_DOUBLE: 00580 type = dt_double; 00581 break; 00582 case SQL_SHORT: 00583 case SQL_LONG: 00584 if (var->sqlscale < 0) 00585 { 00586 type = dt_double; 00587 } 00588 else 00589 { 00590 type = dt_integer; 00591 } 00592 break; 00593 case SQL_INT64: 00594 if (var->sqlscale < 0) 00595 { 00596 type = dt_double; 00597 } 00598 else 00599 { 00600 type = dt_long_long; 00601 } 00602 break; 00603 /* case SQL_BLOB: 00604 case SQL_ARRAY:*/ 00605 default: 00606 std::ostringstream msg; 00607 msg << "Type of column ["<< colNum << "] \"" << columnName 00608 << "\" is not supported for dynamic queries"; 00609 throw soci_error(msg.str()); 00610 break; 00611 } 00612 } 00613 00614 firebird_standard_into_type_backend * firebird_statement_backend::make_into_type_backend() 00615 { 00616 return new firebird_standard_into_type_backend(*this); 00617 } 00618 00619 firebird_standard_use_type_backend * firebird_statement_backend::make_use_type_backend() 00620 { 00621 return new firebird_standard_use_type_backend(*this); 00622 } 00623 00624 firebird_vector_into_type_backend * firebird_statement_backend::make_vector_into_type_backend() 00625 { 00626 return new firebird_vector_into_type_backend(*this); 00627 } 00628 00629 firebird_vector_use_type_backend * firebird_statement_backend::make_vector_use_type_backend() 00630 { 00631 return new firebird_vector_use_type_backend(*this); 00632 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1