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 }