00001 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney 00002 // Distributed under the Boost Software License, Version 1.0. 00003 // (See accompanying file LICENSE_1_0.txt or copy at 00004 // http://www.boost.org/LICENSE_1_0.txt) 00005 00006 #define SOCI_ODBC_SOURCE 00007 #include "soci-odbc.h" 00008 #include <cctype> 00009 #include <cstdio> 00010 #include <cstring> 00011 #include <ctime> 00012 #include <sstream> 00013 00014 using namespace soci; 00015 using namespace soci::details; 00016 00017 void odbc_standard_use_type_backend::prepare_for_bind( 00018 void *&data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType) 00019 { 00020 switch (type_) 00021 { 00022 // simple cases 00023 case x_short: 00024 sqlType = SQL_SMALLINT; 00025 cType = SQL_C_SSHORT; 00026 size = sizeof(short); 00027 break; 00028 case x_integer: 00029 sqlType = SQL_INTEGER; 00030 cType = SQL_C_SLONG; 00031 size = sizeof(int); 00032 break; 00033 case x_unsigned_long: 00034 sqlType = SQL_BIGINT; 00035 cType = SQL_C_ULONG; 00036 size = sizeof(unsigned long); 00037 break; 00038 case x_double: 00039 sqlType = SQL_DOUBLE; 00040 cType = SQL_C_DOUBLE; 00041 size = sizeof(double); 00042 break; 00043 00044 // cases that require adjustments and buffer management 00045 case x_char: 00046 sqlType = SQL_CHAR; 00047 cType = SQL_C_CHAR; 00048 size = sizeof(char)+1; 00049 buf_ = new char[size]; 00050 data = buf_; 00051 indHolder_ = SQL_NTS; 00052 break; 00053 case x_stdstring: 00054 { 00055 // TODO: No textual value is assigned here! 00056 00057 std::string* s = static_cast<std::string*>(data); 00058 sqlType = SQL_VARCHAR; 00059 cType = SQL_C_CHAR; 00060 size = 255; // !FIXME this is not sufficent 00061 buf_ = new char[size]; 00062 data = buf_; 00063 indHolder_ = SQL_NTS; 00064 } 00065 break; 00066 case x_stdtm: 00067 sqlType = SQL_TIMESTAMP; 00068 cType = SQL_C_TIMESTAMP; 00069 buf_ = new char[sizeof(TIMESTAMP_STRUCT)]; 00070 data = buf_; 00071 size = 19; // This number is not the size in bytes, but the number 00072 // of characters in the date if it was written out 00073 // yyyy-mm-dd hh:mm:ss 00074 break; 00075 00076 case x_blob: 00077 { 00078 // sqlType = SQL_VARBINARY; 00079 // cType = SQL_C_BINARY; 00080 00081 // BLOB *b = static_cast<BLOB *>(data); 00082 00083 // odbc_blob_backend *bbe 00084 // = static_cast<odbc_blob_backend *>(b->getBackEnd()); 00085 00086 // size = 0; 00087 // indHolder_ = size; 00088 //TODO data = &bbe->lobp_; 00089 } 00090 break; 00091 case x_statement: 00092 case x_rowid: 00093 break; 00094 } 00095 } 00096 00097 void odbc_standard_use_type_backend::bind_helper(int &position, void *data, exchange_type type) 00098 { 00099 data_ = data; // for future reference 00100 type_ = type; // for future reference 00101 00102 SQLSMALLINT sqlType; 00103 SQLSMALLINT cType; 00104 SQLLEN size; 00105 00106 prepare_for_bind(data, size, sqlType, cType); 00107 00108 SQLRETURN rc = SQLBindParameter(statement_.hstmt_, 00109 static_cast<SQLUSMALLINT>(position++), 00110 SQL_PARAM_INPUT, 00111 cType, sqlType, size, 0, data, 0, &indHolder_); 00112 00113 if (is_odbc_error(rc)) 00114 { 00115 throw odbc_soci_error(SQL_HANDLE_STMT, statement_.hstmt_, 00116 "Binding"); 00117 } 00118 } 00119 00120 void odbc_standard_use_type_backend::bind_by_pos( 00121 int &position, void *data, exchange_type type, bool /* readOnly */) 00122 { 00123 if (statement_.boundByName_) 00124 { 00125 throw soci_error( 00126 "Binding for use elements must be either by position or by name."); 00127 } 00128 00129 bind_helper(position, data, type); 00130 00131 statement_.boundByPos_ = true; 00132 } 00133 00134 void odbc_standard_use_type_backend::bind_by_name( 00135 std::string const &name, void *data, exchange_type type, bool /* readOnly */) 00136 { 00137 if (statement_.boundByPos_) 00138 { 00139 throw soci_error( 00140 "Binding for use elements must be either by position or by name."); 00141 } 00142 00143 int position = -1; 00144 int count = 1; 00145 00146 for (std::vector<std::string>::iterator it = statement_.names_.begin(); 00147 it != statement_.names_.end(); ++it) 00148 { 00149 if (*it == name) 00150 { 00151 position = count; 00152 break; 00153 } 00154 count++; 00155 } 00156 00157 if (position != -1) 00158 { 00159 bind_helper(position, data, type); 00160 } 00161 else 00162 { 00163 std::ostringstream ss; 00164 ss << "Unable to find name '" << name << "' to bind to"; 00165 throw soci_error(ss.str().c_str()); 00166 } 00167 00168 statement_.boundByName_ = true; 00169 } 00170 00171 void odbc_standard_use_type_backend::pre_use(indicator const *ind) 00172 { 00173 // first deal with data 00174 if (type_ == x_char) 00175 { 00176 char *c = static_cast<char*>(data_); 00177 buf_[0] = *c; 00178 buf_[1] = '\0'; 00179 } 00180 else if (type_ == x_stdstring) 00181 { 00182 std::string *s = static_cast<std::string *>(data_); 00183 00184 std::size_t const bufSize = 4000; 00185 std::size_t const sSize = s->size(); 00186 std::size_t const toCopy = 00187 sSize < bufSize -1 ? sSize + 1 : bufSize - 1; 00188 strncpy(buf_, s->c_str(), toCopy); 00189 buf_[toCopy] = '\0'; 00190 } 00191 else if (type_ == x_stdtm) 00192 { 00193 std::tm *t = static_cast<std::tm *>(data_); 00194 TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf_); 00195 00196 ts->year = static_cast<SQLSMALLINT>(t->tm_year + 1900); 00197 ts->month = static_cast<SQLUSMALLINT>(t->tm_mon + 1); 00198 ts->day = static_cast<SQLUSMALLINT>(t->tm_mday); 00199 ts->hour = static_cast<SQLUSMALLINT>(t->tm_hour); 00200 ts->minute = static_cast<SQLUSMALLINT>(t->tm_min); 00201 ts->second = static_cast<SQLUSMALLINT>(t->tm_sec); 00202 ts->fraction = 0; 00203 } 00204 00205 // then handle indicators 00206 if (ind != NULL && *ind == i_null) 00207 { 00208 indHolder_ = SQL_NULL_DATA; // null 00209 } 00210 } 00211 00212 void odbc_standard_use_type_backend::post_use(bool gotData, indicator *ind) 00213 { 00214 // TODO: Is it possible to have the bound element being overwritten 00215 // by the database? (looks like yes) 00216 // If not, then nothing to do here, please remove this comment 00217 // and most likely the code below is also unnecessary. 00218 // If yes, then use the value of the readOnly parameter: 00219 // - true: the given object should not be modified and the backend 00220 // should detect if the modification was performed on the 00221 // isolated buffer and throw an exception if the buffer was modified 00222 // (this indicates logic error, because the user used const object 00223 // and executed a query that attempted to modified it) 00224 // - false: the modification should be propagated to the given object (as below). 00225 // 00226 // From the code below I conclude that ODBC allows the database to modify the bound object 00227 // and the code below correctly deals with readOnly == false. 00228 // The point is that with readOnly == true the propagation of modification should not 00229 // take place and in addition the attempt of modification should be detected and reported. 00230 // ... 00231 00232 // first, deal with data 00233 if (gotData) 00234 { 00235 if (type_ == x_char) 00236 { 00237 char *c = static_cast<char*>(data_); 00238 *c = buf_[0]; 00239 } 00240 else if (type_ == x_stdstring) 00241 { 00242 std::string *s = static_cast<std::string *>(data_); 00243 00244 *s = buf_; 00245 } 00246 else if (type_ == x_stdtm) 00247 { 00248 std::tm *t = static_cast<std::tm *>(data_); 00249 TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf_); 00250 t->tm_isdst = -1; 00251 t->tm_year = ts->year - 1900; 00252 t->tm_mon = ts->month - 1; 00253 t->tm_mday = ts->day; 00254 t->tm_hour = ts->hour; 00255 t->tm_min = ts->minute; 00256 t->tm_sec = ts->second; 00257 00258 // normalize and compute the remaining fields 00259 std::mktime(t); 00260 } 00261 } 00262 00263 if (ind != NULL) 00264 { 00265 if (gotData) 00266 { 00267 if (indHolder_ == 0) 00268 { 00269 *ind = i_ok; 00270 } 00271 else if (indHolder_ == SQL_NULL_DATA) 00272 { 00273 *ind = i_null; 00274 } 00275 else 00276 { 00277 *ind = i_truncated; 00278 } 00279 } 00280 } 00281 } 00282 00283 void odbc_standard_use_type_backend::clean_up() 00284 { 00285 if (buf_ != NULL) 00286 { 00287 delete [] buf_; 00288 buf_ = NULL; 00289 } 00290 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1