File indexing completed on 2025-02-23 05:15:16
0001 // 0002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski 0003 // Distributed under the Boost Software License, Version 1.0. 0004 // (See accompanying file LICENSE_1_0.txt or copy at 0005 // http://www.boost.org/LICENSE_1_0.txt) 0006 // 0007 0008 #define SOCI_FIREBIRD_SOURCE 0009 #include "soci/firebird/soci-firebird.h" 0010 #include "firebird/error-firebird.h" 0011 0012 using namespace soci; 0013 using namespace soci::details::firebird; 0014 0015 firebird_blob_backend::firebird_blob_backend(firebird_session_backend &session) 0016 : session_(session), bid_(), from_db_(false), bhp_(0), data_(), 0017 loaded_(false), max_seg_size_(0) 0018 {} 0019 0020 firebird_blob_backend::~firebird_blob_backend() 0021 { 0022 cleanUp(); 0023 } 0024 0025 std::size_t firebird_blob_backend::get_len() 0026 { 0027 if (from_db_ && bhp_ == 0) 0028 { 0029 open(); 0030 } 0031 0032 return data_.size(); 0033 } 0034 0035 std::size_t firebird_blob_backend::read_from_start(char * buf, std::size_t toRead, std::size_t offset) 0036 { 0037 if (from_db_ && (loaded_ == false)) 0038 { 0039 // this is blob fetched from database, but not loaded yet 0040 load(); 0041 } 0042 0043 std::size_t size = data_.size(); 0044 0045 if (offset > size) 0046 { 0047 throw soci_error("Can't read past-the-end of BLOB data"); 0048 } 0049 0050 char * itr = buf; 0051 std::size_t limit = size - offset < toRead ? size - offset : toRead; 0052 std::size_t index = 0; 0053 0054 while (index < limit) 0055 { 0056 *itr = data_[offset+index]; 0057 ++index; 0058 ++itr; 0059 } 0060 0061 return limit; 0062 } 0063 0064 std::size_t firebird_blob_backend::write_from_start(char const * buf, std::size_t toWrite, std::size_t offset) 0065 { 0066 if (from_db_ && (loaded_ == false)) 0067 { 0068 // this is blob fetched from database, but not loaded yet 0069 load(); 0070 } 0071 0072 std::size_t size = data_.size(); 0073 0074 if (offset > size) 0075 { 0076 throw soci_error("Can't write past-the-end of BLOB data"); 0077 } 0078 0079 // make sure there is enough space in buffer 0080 if (toWrite > (size - offset)) 0081 { 0082 data_.resize(size + (toWrite - (size - offset))); 0083 } 0084 0085 writeBuffer(offset, buf, toWrite); 0086 0087 return toWrite; 0088 } 0089 0090 std::size_t firebird_blob_backend::append( 0091 char const * buf, std::size_t toWrite) 0092 { 0093 if (from_db_ && (loaded_ == false)) 0094 { 0095 // this is blob fetched from database, but not loaded yet 0096 load(); 0097 } 0098 0099 std::size_t size = data_.size(); 0100 data_.resize(size + toWrite); 0101 0102 writeBuffer(size, buf, toWrite); 0103 0104 return toWrite; 0105 } 0106 0107 void firebird_blob_backend::trim(std::size_t newLen) 0108 { 0109 if (from_db_ && (loaded_ == false)) 0110 { 0111 // this is blob fetched from database, but not loaded yet 0112 load(); 0113 } 0114 0115 data_.resize(newLen); 0116 } 0117 0118 void firebird_blob_backend::writeBuffer(std::size_t offset, 0119 char const * buf, std::size_t toWrite) 0120 { 0121 char const * itr = buf; 0122 char const * end_itr = buf + toWrite; 0123 0124 while (itr!=end_itr) 0125 { 0126 data_[offset++] = *itr++; 0127 } 0128 } 0129 0130 void firebird_blob_backend::open() 0131 { 0132 if (bhp_ != 0) 0133 { 0134 // BLOB already opened 0135 return; 0136 } 0137 0138 ISC_STATUS stat[20]; 0139 0140 if (isc_open_blob2(stat, &session_.dbhp_, session_.current_transaction(), 0141 &bhp_, &bid_, 0, NULL)) 0142 { 0143 bhp_ = 0L; 0144 throw_iscerror(stat); 0145 } 0146 0147 // get basic blob info 0148 long blob_size = getBLOBInfo(); 0149 0150 data_.resize(blob_size); 0151 } 0152 0153 void firebird_blob_backend::cleanUp() 0154 { 0155 from_db_ = false; 0156 loaded_ = false; 0157 max_seg_size_ = 0; 0158 data_.resize(0); 0159 0160 if (bhp_ != 0) 0161 { 0162 // close blob 0163 ISC_STATUS stat[20]; 0164 if (isc_close_blob(stat, &bhp_)) 0165 { 0166 throw_iscerror(stat); 0167 } 0168 bhp_ = 0; 0169 } 0170 } 0171 0172 // loads blob data into internal buffer 0173 void firebird_blob_backend::load() 0174 { 0175 if (bhp_ == 0) 0176 { 0177 open(); 0178 } 0179 0180 // The blob is empty. 0181 if (data_.empty()) 0182 { 0183 return; 0184 } 0185 0186 ISC_STATUS stat[20]; 0187 unsigned short bytes; 0188 std::vector<char>::size_type total_bytes = 0; 0189 bool keep_reading = false; 0190 0191 do 0192 { 0193 bytes = 0; 0194 // next segment of data 0195 // data_ is large-enough because we know total size of blob 0196 isc_get_segment(stat, &bhp_, &bytes, static_cast<short>(max_seg_size_), 0197 &data_[total_bytes]); 0198 0199 total_bytes += bytes; 0200 0201 if (total_bytes == data_.size()) 0202 { 0203 // we have all BLOB data 0204 keep_reading = false; 0205 } 0206 else if (stat[1] == 0 || stat[1] == isc_segment) 0207 { 0208 // there is more data to read from current segment (0) 0209 // or there is next segment (isc_segment) 0210 keep_reading = true; 0211 } 0212 else if (stat[1] == isc_segstr_eof) 0213 { 0214 // BLOB is shorter then we expected ??? 0215 keep_reading = false; 0216 } 0217 else 0218 { 0219 // an error has occured 0220 throw_iscerror(stat); 0221 } 0222 } 0223 while (keep_reading); 0224 0225 loaded_ = true; 0226 } 0227 0228 // this method saves BLOB content to database 0229 // (a new BLOB will be created at this point) 0230 // BLOB will be closed after save. 0231 void firebird_blob_backend::save() 0232 { 0233 // close old blob if necessary 0234 ISC_STATUS stat[20]; 0235 if (bhp_ != 0) 0236 { 0237 if (isc_close_blob(stat, &bhp_)) 0238 { 0239 throw_iscerror(stat); 0240 } 0241 bhp_ = 0; 0242 } 0243 0244 // create new blob 0245 if (isc_create_blob(stat, &session_.dbhp_, session_.current_transaction(), 0246 &bhp_, &bid_)) 0247 { 0248 throw_iscerror(stat); 0249 } 0250 0251 if (data_.size() > 0) 0252 { 0253 // write data 0254 size_t size = data_.size(); 0255 size_t offset = 0; 0256 // Segment Size : Specifying the BLOB segment is throwback to times past, when applications for working 0257 // with BLOB data were written in C(Embedded SQL) with the help of the gpre pre - compiler. 0258 // Nowadays, it is effectively irrelevant.The segment size for BLOB data is determined by the client side and is usually larger than the data page size, 0259 // in any case. 0260 do 0261 { 0262 unsigned short segmentSize = 0xFFFF; //last unsigned short number 0263 if (size - offset < segmentSize) //if content size is less than max segment size or last data segment is about to be written 0264 segmentSize = static_cast<unsigned short>(size - offset); 0265 //write segment 0266 if (isc_put_segment(stat, &bhp_, segmentSize, &data_[0]+offset)) 0267 { 0268 throw_iscerror(stat); 0269 } 0270 offset += segmentSize; 0271 } 0272 while (offset < size); 0273 } 0274 cleanUp(); 0275 from_db_ = true; 0276 } 0277 0278 // retrives number of segments and total length of BLOB 0279 // returns total length of BLOB 0280 long firebird_blob_backend::getBLOBInfo() 0281 { 0282 char blob_items[] = {isc_info_blob_max_segment, isc_info_blob_total_length}; 0283 char res_buffer[20], *p, item; 0284 short length; 0285 long total_length = 0; 0286 0287 ISC_STATUS stat[20]; 0288 0289 if (isc_blob_info(stat, &bhp_, sizeof(blob_items), blob_items, 0290 sizeof(res_buffer), res_buffer)) 0291 { 0292 throw_iscerror(stat); 0293 } 0294 0295 for (p = res_buffer; *p != isc_info_end ;) 0296 { 0297 item = *p++; 0298 length = static_cast<short>(isc_vax_integer(p, 2)); 0299 p += 2; 0300 switch (item) 0301 { 0302 case isc_info_blob_max_segment: 0303 max_seg_size_ = isc_vax_integer(p, length); 0304 break; 0305 case isc_info_blob_total_length: 0306 total_length = isc_vax_integer(p, length); 0307 break; 0308 case isc_info_truncated: 0309 throw soci_error("Fatal Error: BLOB info truncated!"); 0310 break; 0311 default: 0312 break; 0313 } 0314 p += length; 0315 } 0316 0317 return total_length; 0318 }