00001 // 00002 // Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton 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_POSTGRESQL_SOURCE 00009 #include "soci-postgresql.h" 00010 #include <libpq/libpq-fs.h> // libpq 00011 #include <cctype> 00012 #include <cstdio> 00013 #include <cstring> 00014 #include <ctime> 00015 #include <sstream> 00016 00017 #ifdef SOCI_PGSQL_NOPARAMS 00018 #define SOCI_PGSQL_NOBINDBYNAME 00019 #endif // SOCI_PGSQL_NOPARAMS 00020 00021 #ifdef _MSC_VER 00022 #pragma warning(disable:4355) 00023 #endif 00024 00025 using namespace soci; 00026 using namespace soci::details; 00027 00028 00029 postgresql_statement_backend::postgresql_statement_backend( 00030 postgresql_session_backend &session) 00031 : session_(session), result_(NULL), justDescribed_(false), 00032 hasIntoElements_(false), hasVectorIntoElements_(false), 00033 hasUseElements_(false), hasVectorUseElements_(false) 00034 { 00035 } 00036 00037 void postgresql_statement_backend::alloc() 00038 { 00039 // nothing to do here 00040 } 00041 00042 void postgresql_statement_backend::clean_up() 00043 { 00044 if (result_ != NULL) 00045 { 00046 PQclear(result_); 00047 result_ = NULL; 00048 } 00049 } 00050 00051 void postgresql_statement_backend::prepare(std::string const & query, 00052 statement_type stType) 00053 { 00054 #ifdef SOCI_PGSQL_NOBINDBYNAME 00055 query_ = query; 00056 #else 00057 // rewrite the query by transforming all named parameters into 00058 // the postgresql_ numbers ones (:abc -> $1, etc.) 00059 00060 enum { normal, in_quotes, in_name } state = normal; 00061 00062 std::string name; 00063 int position = 1; 00064 00065 for (std::string::const_iterator it = query.begin(), end = query.end(); 00066 it != end; ++it) 00067 { 00068 switch (state) 00069 { 00070 case normal: 00071 if (*it == '\'') 00072 { 00073 query_ += *it; 00074 state = in_quotes; 00075 } 00076 else if (*it == ':') 00077 { 00078 // Check whether this is a cast operator (e.g. 23::float) 00079 // and treat it as a special case, not as a named binding 00080 const std::string::const_iterator next_it = it + 1; 00081 if ((next_it != end) && (*next_it == ':')) 00082 { 00083 query_ += "::"; 00084 ++it; 00085 } 00086 else 00087 { 00088 state = in_name; 00089 } 00090 } 00091 else // regular character, stay in the same state 00092 { 00093 query_ += *it; 00094 } 00095 break; 00096 case in_quotes: 00097 if (*it == '\'') 00098 { 00099 query_ += *it; 00100 state = normal; 00101 } 00102 else // regular quoted character 00103 { 00104 query_ += *it; 00105 } 00106 break; 00107 case in_name: 00108 if (std::isalnum(*it) || *it == '_') 00109 { 00110 name += *it; 00111 } 00112 else // end of name 00113 { 00114 names_.push_back(name); 00115 name.clear(); 00116 std::ostringstream ss; 00117 ss << '$' << position++; 00118 query_ += ss.str(); 00119 query_ += *it; 00120 state = normal; 00121 00122 // Check whether the named parameter is immediatelly 00123 // followed by a cast operator (e.g. :name::float) 00124 // and handle the additional colon immediately to avoid 00125 // its misinterpretation later on. 00126 if (*it == ':') 00127 { 00128 const std::string::const_iterator next_it = it + 1; 00129 if ((next_it != end) && (*next_it == ':')) 00130 { 00131 query_ += ':'; 00132 ++it; 00133 } 00134 } 00135 } 00136 break; 00137 } 00138 } 00139 00140 if (state == in_name) 00141 { 00142 names_.push_back(name); 00143 std::ostringstream ss; 00144 ss << '$' << position++; 00145 query_ += ss.str(); 00146 } 00147 00148 #endif // SOCI_PGSQL_NOBINDBYNAME 00149 00150 #ifndef SOCI_PGSQL_NOPREPARE 00151 00152 if (stType == st_repeatable_query) 00153 { 00154 statementName_ = session_.get_next_statement_name(); 00155 00156 PGresult * res = PQprepare(session_.conn_, statementName_.c_str(), 00157 query_.c_str(), static_cast<int>(names_.size()), NULL); 00158 if (res == NULL) 00159 { 00160 throw soci_error("Cannot prepare statement."); 00161 } 00162 ExecStatusType status = PQresultStatus(res); 00163 if (status != PGRES_COMMAND_OK) 00164 { 00165 throw soci_error(PQresultErrorMessage(res)); 00166 } 00167 PQclear(res); 00168 } 00169 00170 stType_ = stType; 00171 00172 #endif // SOCI_PGSQL_NOPREPARE 00173 } 00174 00175 statement_backend::exec_fetch_result 00176 postgresql_statement_backend::execute(int number) 00177 { 00178 // If the statement was "just described", then we know that 00179 // it was actually executed with all the use elements 00180 // already bound and pre-used. This means that the result of the 00181 // query is already on the client side, so there is no need 00182 // to re-execute it. 00183 00184 if (justDescribed_ == false) 00185 { 00186 // This object could have been already filled with data before. 00187 clean_up(); 00188 00189 if (number > 1 && hasIntoElements_) 00190 { 00191 throw soci_error( 00192 "Bulk use with single into elements is not supported."); 00193 } 00194 00195 // Since the bulk operations are not natively supported by postgresql_, 00196 // we have to explicitly loop to achieve the bulk operations. 00197 // On the other hand, looping is not needed if there are single 00198 // use elements, even if there is a bulk fetch. 00199 // We know that single use and bulk use elements in the same query are 00200 // not supported anyway, so in the effect the 'number' parameter here 00201 // specifies the size of vectors (into/use), but 'numberOfExecutions' 00202 // specifies the number of loops that need to be performed. 00203 00204 int numberOfExecutions = 1; 00205 if (number > 0) 00206 { 00207 numberOfExecutions = hasUseElements_ ? 1 : number; 00208 } 00209 00210 if ((useByPosBuffers_.empty() == false) || 00211 (useByNameBuffers_.empty() == false)) 00212 { 00213 if ((useByPosBuffers_.empty() == false) && 00214 (useByNameBuffers_.empty() == false)) 00215 { 00216 throw soci_error( 00217 "Binding for use elements must be either by position " 00218 "or by name."); 00219 } 00220 00221 for (int i = 0; i != numberOfExecutions; ++i) 00222 { 00223 std::vector<char *> paramValues; 00224 00225 if (useByPosBuffers_.empty() == false) 00226 { 00227 // use elements bind by position 00228 // the map of use buffers can be traversed 00229 // in its natural order 00230 00231 for (UseByPosBuffersMap::iterator 00232 it = useByPosBuffers_.begin(), 00233 end = useByPosBuffers_.end(); 00234 it != end; ++it) 00235 { 00236 char ** buffers = it->second; 00237 paramValues.push_back(buffers[i]); 00238 } 00239 } 00240 else 00241 { 00242 // use elements bind by name 00243 00244 for (std::vector<std::string>::iterator 00245 it = names_.begin(), end = names_.end(); 00246 it != end; ++it) 00247 { 00248 UseByNameBuffersMap::iterator b 00249 = useByNameBuffers_.find(*it); 00250 if (b == useByNameBuffers_.end()) 00251 { 00252 std::string msg( 00253 "Missing use element for bind by name ("); 00254 msg += *it; 00255 msg += ")."; 00256 throw soci_error(msg); 00257 } 00258 char ** buffers = b->second; 00259 paramValues.push_back(buffers[i]); 00260 } 00261 } 00262 00263 #ifdef SOCI_PGSQL_NOPARAMS 00264 00265 throw soci_error("Queries with parameters are not supported."); 00266 00267 #else 00268 00269 #ifdef SOCI_PGSQL_NOPREPARE 00270 00271 result_ = PQexecParams(session_.conn_, query_.c_str(), 00272 static_cast<int>(paramValues.size()), 00273 NULL, ¶mValues[0], NULL, NULL, 0); 00274 00275 #else 00276 00277 if (stType_ == st_repeatable_query) 00278 { 00279 // this query was separately prepared 00280 00281 result_ = PQexecPrepared(session_.conn_, 00282 statementName_.c_str(), 00283 static_cast<int>(paramValues.size()), 00284 ¶mValues[0], NULL, NULL, 0); 00285 } 00286 else // stType_ == st_one_time_query 00287 { 00288 // this query was not separately prepared and should 00289 // be executed as a one-time query 00290 00291 result_ = PQexecParams(session_.conn_, query_.c_str(), 00292 static_cast<int>(paramValues.size()), 00293 NULL, ¶mValues[0], NULL, NULL, 0); 00294 } 00295 00296 #endif // SOCI_PGSQL_NOPREPARE 00297 00298 #endif // SOCI_PGSQL_NOPARAMS 00299 00300 if (numberOfExecutions > 1) 00301 { 00302 // there are only bulk use elements (no intos) 00303 if (result_ == NULL) 00304 { 00305 throw soci_error("Cannot execute query."); 00306 } 00307 00308 ExecStatusType status = PQresultStatus(result_); 00309 if (status != PGRES_COMMAND_OK) 00310 { 00311 throw soci_error(PQresultErrorMessage(result_)); 00312 } 00313 PQclear(result_); 00314 } 00315 } 00316 00317 if (numberOfExecutions > 1) 00318 { 00319 // it was a bulk operation 00320 result_ = NULL; 00321 return ef_no_data; 00322 } 00323 00324 // otherwise (no bulk), follow the code below 00325 } 00326 else 00327 { 00328 // there are no use elements 00329 // - execute the query without parameter information 00330 00331 #ifdef SOCI_PGSQL_NOPREPARE 00332 00333 result_ = PQexec(session_.conn_, query_.c_str()); 00334 #else 00335 00336 if (stType_ == st_repeatable_query) 00337 { 00338 // this query was separately prepared 00339 00340 result_ = PQexecPrepared(session_.conn_, 00341 statementName_.c_str(), 0, NULL, NULL, NULL, 0); 00342 } 00343 else // stType_ == st_one_time_query 00344 { 00345 result_ = PQexec(session_.conn_, query_.c_str()); 00346 } 00347 00348 #endif // SOCI_PGSQL_NOPREPARE 00349 00350 if (result_ == NULL) 00351 { 00352 throw soci_error("Cannot execute query."); 00353 } 00354 } 00355 } 00356 else 00357 { 00358 // The optimization based on the existing results 00359 // from the row description can be performed only once. 00360 // If the same statement is re-executed, 00361 // it will be *really* re-executed, without reusing existing data. 00362 00363 justDescribed_ = false; 00364 } 00365 00366 ExecStatusType status = PQresultStatus(result_); 00367 if (status == PGRES_TUPLES_OK) 00368 { 00369 currentRow_ = 0; 00370 rowsToConsume_ = 0; 00371 00372 numberOfRows_ = PQntuples(result_); 00373 if (numberOfRows_ == 0) 00374 { 00375 return ef_no_data; 00376 } 00377 else 00378 { 00379 if (number > 0) 00380 { 00381 // prepare for the subsequent data consumption 00382 return fetch(number); 00383 } 00384 else 00385 { 00386 // execute(0) was meant to only perform the query 00387 return ef_success; 00388 } 00389 } 00390 } 00391 else if (status == PGRES_COMMAND_OK) 00392 { 00393 return ef_no_data; 00394 } 00395 else 00396 { 00397 throw soci_error(PQresultErrorMessage(result_)); 00398 } 00399 } 00400 00401 statement_backend::exec_fetch_result 00402 postgresql_statement_backend::fetch(int number) 00403 { 00404 // Note: This function does not actually fetch anything from anywhere 00405 // - the data was already retrieved from the server in the execute() 00406 // function, and the actual consumption of this data will take place 00407 // in the postFetch functions, called for each into element. 00408 // Here, we only prepare for this to happen (to emulate "the Oracle way"). 00409 00410 // forward the "cursor" from the last fetch 00411 currentRow_ += rowsToConsume_; 00412 00413 if (currentRow_ >= numberOfRows_) 00414 { 00415 // all rows were already consumed 00416 return ef_no_data; 00417 } 00418 else 00419 { 00420 if (currentRow_ + number > numberOfRows_) 00421 { 00422 rowsToConsume_ = numberOfRows_ - currentRow_; 00423 00424 // this simulates the behaviour of Oracle 00425 // - when EOF is hit, we return ef_no_data even when there are 00426 // actually some rows fetched 00427 return ef_no_data; 00428 } 00429 else 00430 { 00431 rowsToConsume_ = number; 00432 return ef_success; 00433 } 00434 } 00435 } 00436 00437 int postgresql_statement_backend::get_number_of_rows() 00438 { 00439 return numberOfRows_ - currentRow_; 00440 } 00441 00442 std::string postgresql_statement_backend::rewrite_for_procedure_call( 00443 std::string const & query) 00444 { 00445 std::string newQuery("select "); 00446 newQuery += query; 00447 return newQuery; 00448 } 00449 00450 int postgresql_statement_backend::prepare_for_describe() 00451 { 00452 execute(1); 00453 justDescribed_ = true; 00454 00455 int columns = PQnfields(result_); 00456 return columns; 00457 } 00458 00459 void postgresql_statement_backend::describe_column(int colNum, data_type & type, 00460 std::string & columnName) 00461 { 00462 // In postgresql_ column numbers start from 0 00463 int const pos = colNum - 1; 00464 00465 unsigned long const typeOid = PQftype(result_, pos); 00466 switch (typeOid) 00467 { 00468 // Note: the following list of OIDs was taken from the pg_type table 00469 // we do not claim that this list is exchaustive or even correct. 00470 00471 // from pg_type: 00472 00473 case 25: // text 00474 case 1043: // varchar 00475 case 2275: // cstring 00476 case 18: // char 00477 case 1042: // bpchar 00478 type = dt_string; 00479 break; 00480 00481 case 702: // abstime 00482 case 703: // reltime 00483 case 1082: // date 00484 case 1083: // time 00485 case 1114: // timestamp 00486 case 1184: // timestamptz 00487 case 1266: // timetz 00488 type = dt_date; 00489 break; 00490 00491 case 700: // float4 00492 case 701: // float8 00493 case 1700: // numeric 00494 type = dt_double; 00495 break; 00496 00497 case 16: // bool 00498 case 21: // int2 00499 case 23: // int4 00500 type = dt_integer; 00501 break; 00502 00503 case 20: // int8 00504 type = dt_long_long; 00505 break; 00506 00507 case 26: // oid 00508 type = dt_unsigned_long; 00509 break; 00510 00511 default: 00512 throw soci_error("Unknown data type."); 00513 } 00514 00515 columnName = PQfname(result_, pos); 00516 } 00517 00518 postgresql_standard_into_type_backend * 00519 postgresql_statement_backend::make_into_type_backend() 00520 { 00521 hasIntoElements_ = true; 00522 return new postgresql_standard_into_type_backend(*this); 00523 } 00524 00525 postgresql_standard_use_type_backend * 00526 postgresql_statement_backend::make_use_type_backend() 00527 { 00528 hasUseElements_ = true; 00529 return new postgresql_standard_use_type_backend(*this); 00530 } 00531 00532 postgresql_vector_into_type_backend * 00533 postgresql_statement_backend::make_vector_into_type_backend() 00534 { 00535 hasVectorIntoElements_ = true; 00536 return new postgresql_vector_into_type_backend(*this); 00537 } 00538 00539 postgresql_vector_use_type_backend * 00540 postgresql_statement_backend::make_vector_use_type_backend() 00541 { 00542 hasVectorUseElements_ = true; 00543 return new postgresql_vector_use_type_backend(*this); 00544 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1