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 00012 using namespace soci; 00013 using namespace soci::details::firebird; 00014 00015 firebird_blob_backend::firebird_blob_backend(firebird_session_backend &session) 00016 : session_(session), from_db_(false), bhp_(0), loaded_(false), 00017 max_seg_size_(0) 00018 {} 00019 00020 firebird_blob_backend::~firebird_blob_backend() 00021 { 00022 cleanUp(); 00023 } 00024 00025 std::size_t firebird_blob_backend::get_len() 00026 { 00027 if (from_db_ && bhp_ == 0) 00028 { 00029 open(); 00030 } 00031 00032 return data_.size(); 00033 } 00034 00035 std::size_t firebird_blob_backend::read( 00036 std::size_t offset, char * buf, std::size_t toRead) 00037 { 00038 if (from_db_ && (loaded_ == false)) 00039 { 00040 // this is blob fetched from database, but not loaded yet 00041 load(); 00042 } 00043 00044 std::size_t size = data_.size(); 00045 00046 if (offset > size) 00047 { 00048 throw soci_error("Can't read past-the-end of BLOB data"); 00049 } 00050 00051 char * itr = buf; 00052 std::size_t limit = size - offset < toRead ? size - offset : toRead; 00053 std::size_t index = 0; 00054 00055 while (index < limit) 00056 { 00057 *itr = data_[offset+index]; 00058 ++index; 00059 ++itr; 00060 } 00061 00062 return limit; 00063 } 00064 00065 std::size_t firebird_blob_backend::write(std::size_t offset, char const * buf, 00066 std::size_t toWrite) 00067 { 00068 if (from_db_ && (loaded_ == false)) 00069 { 00070 // this is blob fetched from database, but not loaded yet 00071 load(); 00072 } 00073 00074 std::size_t size = data_.size(); 00075 00076 if (offset > size) 00077 { 00078 throw soci_error("Can't write past-the-end of BLOB data"); 00079 } 00080 00081 // make sure there is enough space in buffer 00082 if (toWrite > (size - offset)) 00083 { 00084 data_.resize(size + (toWrite - (size - offset))); 00085 } 00086 00087 writeBuffer(offset, buf, toWrite); 00088 00089 return toWrite; 00090 } 00091 00092 std::size_t firebird_blob_backend::append( 00093 char const * buf, std::size_t toWrite) 00094 { 00095 if (from_db_ && (loaded_ == false)) 00096 { 00097 // this is blob fetched from database, but not loaded yet 00098 load(); 00099 } 00100 00101 std::size_t size = data_.size(); 00102 data_.resize(size + toWrite); 00103 00104 writeBuffer(size, buf, toWrite); 00105 00106 return toWrite; 00107 } 00108 00109 void firebird_blob_backend::trim(std::size_t newLen) 00110 { 00111 if (from_db_ && (loaded_ == false)) 00112 { 00113 // this is blob fetched from database, but not loaded yet 00114 load(); 00115 } 00116 00117 data_.resize(newLen); 00118 } 00119 00120 void firebird_blob_backend::writeBuffer(std::size_t offset, 00121 char const * buf, std::size_t toWrite) 00122 { 00123 char const * itr = buf; 00124 char const * end_itr = buf + toWrite; 00125 00126 while (itr!=end_itr) 00127 { 00128 data_[offset++] = *itr++; 00129 } 00130 } 00131 00132 void firebird_blob_backend::open() 00133 { 00134 if (bhp_ != 0) 00135 { 00136 // BLOB already opened 00137 return; 00138 } 00139 00140 ISC_STATUS stat[20]; 00141 00142 if (isc_open_blob2(stat, &session_.dbhp_, &session_.trhp_, &bhp_, 00143 &bid_, 0, NULL)) 00144 { 00145 bhp_ = 0L; 00146 throw_iscerror(stat); 00147 } 00148 00149 // get basic blob info 00150 long blob_size = getBLOBInfo(); 00151 00152 data_.resize(blob_size); 00153 } 00154 00155 void firebird_blob_backend::cleanUp() 00156 { 00157 from_db_ = false; 00158 loaded_ = false; 00159 max_seg_size_ = 0; 00160 data_.resize(0); 00161 00162 if (bhp_ != 0) 00163 { 00164 // close blob 00165 ISC_STATUS stat[20]; 00166 if (isc_close_blob(stat, &bhp_)) 00167 { 00168 throw_iscerror(stat); 00169 } 00170 bhp_ = 0; 00171 } 00172 } 00173 00174 // loads blob data into internal buffer 00175 void firebird_blob_backend::load() 00176 { 00177 if (bhp_ == 0) 00178 { 00179 open(); 00180 } 00181 00182 ISC_STATUS stat[20]; 00183 unsigned short bytes; 00184 std::vector<char>::size_type total_bytes = 0; 00185 bool keep_reading = false; 00186 00187 do 00188 { 00189 bytes = 0; 00190 // next segment of data 00191 // data_ is large-enough because we know total size of blob 00192 isc_get_segment(stat, &bhp_, &bytes, static_cast<short>(max_seg_size_), 00193 &data_[total_bytes]); 00194 00195 total_bytes += bytes; 00196 00197 if (total_bytes == data_.size()) 00198 { 00199 // we have all BLOB data 00200 keep_reading = false; 00201 } 00202 else if (stat[1] == 0 || stat[1] == isc_segment) 00203 { 00204 // there is more data to read from current segment (0) 00205 // or there is next segment (isc_segment) 00206 keep_reading = true; 00207 } 00208 else if (stat[1] == isc_segstr_eof) 00209 { 00210 // BLOB is shorter then we expected ??? 00211 keep_reading = false; 00212 } 00213 else 00214 { 00215 // an error has occured 00216 throw_iscerror(stat); 00217 } 00218 } 00219 while (keep_reading); 00220 00221 loaded_ = true; 00222 } 00223 00224 // this method saves BLOB content to database 00225 // (a new BLOB will be created at this point) 00226 // BLOB will be closed after save. 00227 void firebird_blob_backend::save() 00228 { 00229 // close old blob if necessary 00230 ISC_STATUS stat[20]; 00231 if (bhp_ != 0) 00232 { 00233 if (isc_close_blob(stat, &bhp_)) 00234 { 00235 throw_iscerror(stat); 00236 } 00237 bhp_ = 0; 00238 } 00239 00240 // create new blob 00241 if (isc_create_blob(stat, &session_.dbhp_, &session_.trhp_, 00242 &bhp_, &bid_)) 00243 { 00244 throw_iscerror(stat); 00245 } 00246 00247 if (data_.size() > 0) 00248 { 00249 // write data 00250 if (isc_put_segment(stat, &bhp_, 00251 static_cast<unsigned short>(data_.size()), &data_[0])) 00252 { 00253 throw_iscerror(stat); 00254 } 00255 } 00256 00257 cleanUp(); 00258 from_db_ = true; 00259 } 00260 00261 // retrives number of segments and total length of BLOB 00262 // returns total length of BLOB 00263 long firebird_blob_backend::getBLOBInfo() 00264 { 00265 char blob_items[] = {isc_info_blob_max_segment, isc_info_blob_total_length}; 00266 char res_buffer[20], *p, item; 00267 short length; 00268 long total_length = 0; 00269 00270 ISC_STATUS stat[20]; 00271 00272 if (isc_blob_info(stat, &bhp_, sizeof(blob_items), blob_items, 00273 sizeof(res_buffer), res_buffer)) 00274 { 00275 throw_iscerror(stat); 00276 } 00277 00278 for (p = res_buffer; *p != isc_info_end ;) 00279 { 00280 item = *p++; 00281 length = static_cast<short>(isc_vax_integer(p, 2)); 00282 p += 2; 00283 switch (item) 00284 { 00285 case isc_info_blob_max_segment: 00286 max_seg_size_ = isc_vax_integer(p, length); 00287 break; 00288 case isc_info_blob_total_length: 00289 total_length = isc_vax_integer(p, length); 00290 break; 00291 case isc_info_truncated: 00292 throw soci_error("Fatal Error: BLOB info truncated!"); 00293 break; 00294 default: 00295 break; 00296 } 00297 p += length; 00298 } 00299 00300 return total_length; 00301 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1