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 #include "soci/soci-platform.h"
0009 #include "firebird/common.h"
0010 #include "soci/soci-backend.h"
0011 #include "soci-compiler.h"
0012 #include <ibase.h> // FireBird
0013 #include <cstddef>
0014 #include <cstring>
0015 #include <cstdio>
0016 #include <sstream>
0017 #include <iostream>
0018 #include <string>
0019 
0020 namespace soci
0021 {
0022 
0023 namespace details
0024 {
0025 
0026 namespace firebird
0027 {
0028 
0029 char * allocBuffer(XSQLVAR* var)
0030 {
0031     std::size_t size;
0032     int type = var->sqltype & ~1;
0033     if (type == SQL_VARYING)
0034     {
0035         size = var->sqllen + sizeof(short);
0036     }
0037     else if (type == SQL_TIMESTAMP || type == SQL_TYPE_TIME
0038             || type == SQL_TYPE_DATE)
0039     {
0040         size = sizeof(std::tm);
0041     }
0042     else
0043     {
0044         size = var->sqllen;
0045     }
0046 
0047     return new char[size];
0048 }
0049 
0050 void tmEncode(short type, std::tm * src, void * dst)
0051 {
0052     switch (type & ~1)
0053     {
0054         // In Interbase v6 DATE represents a date-only data type,
0055         // in InterBase v5 DATE represents a date+time data type.
0056     case SQL_TIMESTAMP:
0057         isc_encode_timestamp(src, static_cast<ISC_TIMESTAMP*>(dst));
0058         break;
0059     case SQL_TYPE_TIME:
0060         isc_encode_sql_time(src, static_cast<ISC_TIME*>(dst));
0061         break;
0062     case SQL_TYPE_DATE:
0063         isc_encode_sql_date(src, static_cast<ISC_DATE*>(dst));
0064         break;
0065     default:
0066         std::ostringstream msg;
0067         msg << "Unexpected type of date/time field (" << type << ")";
0068         throw soci_error(msg.str());
0069     }
0070 }
0071 
0072 void tmDecode(short type, void * src, std::tm * dst)
0073 {
0074     switch (type & ~1)
0075     {
0076     case SQL_TIMESTAMP:
0077         isc_decode_timestamp(static_cast<ISC_TIMESTAMP*>(src), dst);
0078         break;
0079     case SQL_TYPE_TIME:
0080         isc_decode_sql_time(static_cast<ISC_TIME*>(src), dst);
0081         break;
0082     case SQL_TYPE_DATE:
0083         isc_decode_sql_date(static_cast<ISC_DATE*>(src), dst);
0084         break;
0085     default:
0086         std::ostringstream msg;
0087         msg << "Unexpected type of date/time field (" << type << ")";
0088         throw soci_error(msg.str());
0089     }
0090 }
0091 
0092 void setTextParam(char const * s, std::size_t size, char * buf_,
0093     XSQLVAR * var)
0094 {
0095     int const sqltype = var->sqltype & ~1;
0096 
0097     if (sqltype == SQL_VARYING || sqltype == SQL_TEXT)
0098     {
0099         if (size > static_cast<std::size_t>(var->sqllen))
0100         {
0101             std::ostringstream msg;
0102             msg << "Value \"" << s << "\" is too long ("
0103                 << size << " bytes) to be stored in column of size "
0104                 << var->sqllen << " bytes";
0105             throw soci_error(msg.str());
0106         }
0107 
0108         short const sz = static_cast<short>(size);
0109 
0110         if (sqltype == SQL_VARYING)
0111         {
0112             std::memcpy(buf_, &sz, sizeof(short));
0113             std::memcpy(buf_ + sizeof(short), s, sz);
0114         }
0115         else // sqltype == SQL_TEXT
0116         {
0117             std::memcpy(buf_, s, sz);
0118             if (sz < var->sqllen)
0119             {
0120                 std::memset(buf_+sz, ' ', var->sqllen - sz);
0121             }
0122         }
0123     }
0124     else if (sqltype == SQL_SHORT)
0125     {
0126         parse_decimal<short, unsigned short>(buf_, var, s);
0127     }
0128     else if (sqltype == SQL_LONG)
0129     {
0130         parse_decimal<int, unsigned int>(buf_, var, s);
0131     }
0132     else if (sqltype == SQL_INT64)
0133     {
0134         parse_decimal<long long, unsigned long long>(buf_, var, s);
0135     }
0136     else if (sqltype == SQL_TIMESTAMP
0137             || sqltype == SQL_TYPE_DATE)
0138     {
0139         unsigned short year, month, day, hour, min, sec;
0140         if (std::sscanf(s, "%hu-%hu-%hu %hu:%hu:%hu",
0141                     &year, &month, &day, &hour, &min, &sec) != 6)
0142         {
0143             if (std::sscanf(s, "%hu-%hu-%huT%hu:%hu:%hu",
0144                         &year, &month, &day, &hour, &min, &sec) != 6)
0145             {
0146                 hour = min = sec = 0;
0147                 if (std::sscanf(s, "%hu-%hu-%hu", &year, &month, &day) != 3)
0148                 {
0149                     throw soci_error("Could not parse timestamp value.");
0150                 }
0151             }
0152         }
0153         std::tm t;
0154         std::memset(&t, 0, sizeof(t));
0155         t.tm_year = year - 1900;
0156         t.tm_mon = month - 1;
0157         t.tm_mday = day;
0158         t.tm_hour = hour;
0159         t.tm_min = min;
0160         t.tm_sec = sec;
0161         std::memcpy(buf_, &t, sizeof(t));
0162         tmEncode(var->sqltype, &t, buf_);
0163     }
0164     else if (sqltype == SQL_TYPE_TIME)
0165     {
0166         unsigned short hour, min, sec;
0167         if (std::sscanf(s, "%hu:%hu:%hu", &hour, &min, &sec) != 3)
0168         {
0169             throw soci_error("Could not parse timestamp value.");
0170         }
0171         std::tm t;
0172         std::memset(&t, 0, sizeof(t));
0173         t.tm_hour = hour;
0174         t.tm_min = min;
0175         t.tm_sec = sec;
0176         std::memcpy(buf_, &t, sizeof(t));
0177         tmEncode(var->sqltype, &t, buf_);
0178     }
0179     else
0180     {
0181         throw soci_error("Unexpected string type.");
0182     }
0183 }
0184 
0185 std::string getTextParam(XSQLVAR const *var)
0186 {
0187     //std::cerr << "getTextParam: var->sqltype=" << var->sqltype << std::endl;
0188     short size;
0189     std::size_t offset = 0;
0190 
0191     if ((var->sqltype & ~1) == SQL_VARYING)
0192     {
0193         SOCI_GCC_WARNING_SUPPRESS(cast-align)
0194 
0195         size = *reinterpret_cast<short*>(var->sqldata);
0196 
0197         SOCI_GCC_WARNING_RESTORE(cast-align)
0198 
0199         offset = sizeof(short);
0200     }
0201     else if ((var->sqltype & ~1) == SQL_TEXT)
0202     {
0203         size = var->sqllen;
0204     }
0205     else if ((var->sqltype & ~1) == SQL_SHORT)
0206     {
0207         return format_decimal<short>(var->sqldata, var->sqlscale);
0208     }
0209     else if ((var->sqltype & ~1) == SQL_LONG)
0210     {
0211         return format_decimal<int>(var->sqldata, var->sqlscale);
0212     }
0213     else if ((var->sqltype & ~1) == SQL_INT64)
0214     {
0215         return format_decimal<long long>(var->sqldata, var->sqlscale);
0216     }
0217     else
0218         throw soci_error("Unexpected string type");
0219 
0220     return std::string(var->sqldata + offset, size);
0221 }
0222 
0223 void copy_from_blob(firebird_statement_backend &st, char *buf, std::string &out)
0224 {
0225     firebird_blob_backend blob(st.session_);
0226 
0227     SOCI_GCC_WARNING_SUPPRESS(cast-align)
0228 
0229     blob.assign(*reinterpret_cast<ISC_QUAD*>(buf));
0230 
0231     SOCI_GCC_WARNING_RESTORE(cast-align)
0232 
0233     std::size_t const len_total = blob.get_len();
0234     out.resize(len_total);
0235 
0236     std::size_t const len_read = blob.read_from_start(&out[0], len_total);
0237     if (len_read != len_total)
0238     {
0239         std::ostringstream os;
0240         os << "Read " << len_read << " bytes instead of expected "
0241            << len_total << " from Firebird text blob object";
0242         throw soci_error(os.str());
0243     }
0244 }
0245 
0246 } // namespace firebird
0247 
0248 } // namespace details
0249 
0250 } // namespace soci