SOCI Logo Get SOCI at SourceForge.net. Fast, secure and Free Open Source software downloads

statement.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
SourceForge Logo

Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1