File indexing completed on 2025-02-23 05:15:11

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 #ifndef SOCI_FIREBIRD_COMMON_H_INCLUDED
0009 #define SOCI_FIREBIRD_COMMON_H_INCLUDED
0010 
0011 #include "soci/firebird/soci-firebird.h"
0012 #include "soci-compiler.h"
0013 #include <cstdlib>
0014 #include <cstring>
0015 #include <ctime>
0016 #include <limits>
0017 #include <sstream>
0018 #include <iomanip>
0019 #include <string>
0020 #include <vector>
0021 #include <algorithm>
0022 
0023 namespace soci
0024 {
0025 
0026 namespace details
0027 {
0028 
0029 namespace firebird
0030 {
0031 
0032 char * allocBuffer(XSQLVAR* var);
0033 
0034 void tmEncode(short type, std::tm * src, void * dst);
0035 
0036 void tmDecode(short type, void * src, std::tm * dst);
0037 
0038 void setTextParam(char const * s, std::size_t size, char * buf_,
0039     XSQLVAR * var);
0040 
0041 std::string getTextParam(XSQLVAR const *var);
0042 
0043 // Copy contents of a BLOB in buf into the given string.
0044 void copy_from_blob(firebird_statement_backend &st, char *buf, std::string &out);
0045 
0046 template <typename IntType>
0047 const char *str2dec(const char * s, IntType &out, short &scale)
0048 {
0049     int sign = 1;
0050     if ('+' == *s)
0051         ++s;
0052     else if ('-' == *s)
0053     {
0054         sign = -1;
0055         ++s;
0056     }
0057     scale = 0;
0058     bool period = false;
0059     IntType res = 0;
0060     for (out = 0; *s; ++s, out = res)
0061     {
0062         if (*s == '.')
0063         {
0064             if (period)
0065                 return s;
0066             period = true;
0067             continue;
0068         }
0069         int d = *s - '0';
0070         if (d < 0 || d > 9)
0071             return s;
0072         res = res * 10 + static_cast<IntType>(d * sign);
0073         if (1 == sign)
0074         {
0075             if (res < out)
0076                 return s;
0077         }
0078         else
0079         {
0080             if (res > out)
0081                 return s;
0082         }
0083         if (period)
0084             ++scale;
0085     }
0086     return s;
0087 }
0088 
0089 template <typename T>
0090 inline
0091 T round_for_isc(T value)
0092 {
0093   return value;
0094 }
0095 
0096 inline
0097 double round_for_isc(double value)
0098 {
0099   // Unfortunately all the rounding functions are C99 and so are not supported
0100   // by MSVC, so do it manually.
0101   return value < 0 ? value - 0.5 : value + 0.5;
0102 }
0103 
0104 //helper template to generate proper code based on compile time type check
0105 template<bool cond> struct cond_to_isc {};
0106 template<> struct cond_to_isc<false>
0107 {
0108     static void checkInteger(short scale, short type)
0109     {
0110         if( scale >= 0 && (type == SQL_SHORT || type == SQL_LONG || type == SQL_INT64) )
0111             throw soci_error("Can't convert non-integral value to integral column type");
0112     }
0113 };
0114 template<> struct cond_to_isc<true>
0115 {
0116     static void checkInteger(short scale,short type) { SOCI_UNUSED(scale) SOCI_UNUSED(type) }
0117 };
0118 
0119 template<typename T1>
0120 void to_isc(void * val, XSQLVAR * var, short x_scale = 0)
0121 {
0122     T1 value = *reinterpret_cast<T1*>(val);
0123     short scale = var->sqlscale + x_scale;
0124     short type = var->sqltype & ~1;
0125     long long divisor = 1, multiplier = 1;
0126 
0127     cond_to_isc<std::numeric_limits<T1>::is_integer>::checkInteger(scale,type);
0128 
0129     for (int i = 0; i > scale; --i)
0130         multiplier *= 10;
0131     for (int i = 0; i < scale; ++i)
0132         divisor *= 10;
0133 
0134     switch (type)
0135     {
0136     case SQL_SHORT:
0137         {
0138             short tmp = static_cast<short>(round_for_isc(value*multiplier)/divisor);
0139             std::memcpy(var->sqldata, &tmp, sizeof(short));
0140         }
0141         break;
0142     case SQL_LONG:
0143         {
0144             int tmp = static_cast<int>(round_for_isc(value*multiplier)/divisor);
0145             std::memcpy(var->sqldata, &tmp, sizeof(int));
0146         }
0147         break;
0148     case SQL_INT64:
0149         {
0150             long long tmp = static_cast<long long>(round_for_isc(value*multiplier)/divisor);
0151             std::memcpy(var->sqldata, &tmp, sizeof(long long));
0152         }
0153         break;
0154     case SQL_FLOAT:
0155         {
0156             float sql_value = static_cast<float>(value);
0157             std::memcpy(var->sqldata, &sql_value, sizeof(float));
0158         }
0159         break;
0160     case SQL_DOUBLE:
0161         {
0162             double sql_value = static_cast<double>(value);
0163             std::memcpy(var->sqldata, &sql_value, sizeof(double));
0164         }
0165         break;
0166     default:
0167         throw soci_error("Incorrect data type for numeric conversion");
0168     }
0169 }
0170 
0171 template<typename IntType, typename UIntType>
0172 void parse_decimal(void * val, XSQLVAR * var, const char * s)
0173 {
0174     short scale;
0175     UIntType t1;
0176     IntType t2;
0177     if (!*str2dec(s, t1, scale))
0178         std::memcpy(val, &t1, sizeof(t1));
0179     else if (!*str2dec(s, t2, scale))
0180         std::memcpy(val, &t2, sizeof(t2));
0181     else
0182         throw soci_error("Could not parse decimal value.");
0183     to_isc<IntType>(val, var, scale);
0184 }
0185 
0186 template<typename IntType>
0187 std::string format_decimal(const void *sqldata, int sqlscale)
0188 {
0189     IntType x = *reinterpret_cast<const IntType *>(sqldata);
0190     std::stringstream out;
0191     out << x;
0192     std::string r = out.str();
0193     if (sqlscale < 0)
0194     {
0195         if (static_cast<int>(r.size()) - (x < 0) <= -sqlscale)
0196         {
0197             r = std::string(size_t(x < 0), '-') +
0198                 std::string(-sqlscale - (r.size() - (x < 0)) + 1, '0') +
0199                 r.substr(size_t(x < 0), std::string::npos);
0200         }
0201         return r.substr(0, r.size() + sqlscale) + '.' +
0202             r.substr(r.size() + sqlscale, std::string::npos);
0203     }
0204     return r + std::string(sqlscale, '0');
0205 }
0206 
0207 
0208 template<bool cond> struct cond_from_isc {};
0209 template<> struct cond_from_isc<true> {
0210     static void checkInteger(short scale)
0211     {
0212         std::ostringstream msg;
0213         msg << "Can't convert value with scale " << -scale
0214             << " to integral type";
0215         throw soci_error(msg.str());
0216     }
0217 };
0218 template<> struct cond_from_isc<false>
0219 {
0220     static void checkInteger(short scale) { SOCI_UNUSED(scale) }
0221 };
0222 
0223 template<typename T1>
0224 T1 from_isc(XSQLVAR * var)
0225 {
0226     short scale = var->sqlscale;
0227     T1 tens = 1;
0228 
0229     if (scale < 0)
0230     {
0231         cond_from_isc<std::numeric_limits<T1>::is_integer>::checkInteger(scale);
0232         for (int i = 0; i > scale; --i)
0233         {
0234             tens *= 10;
0235         }
0236     }
0237 
0238     SOCI_GCC_WARNING_SUPPRESS(cast-align)
0239 
0240     switch (var->sqltype & ~1)
0241     {
0242     case SQL_SHORT:
0243         return static_cast<T1>(*reinterpret_cast<short*>(var->sqldata)/tens);
0244     case SQL_LONG:
0245         return static_cast<T1>(*reinterpret_cast<int*>(var->sqldata)/tens);
0246     case SQL_INT64:
0247         return static_cast<T1>(*reinterpret_cast<long long*>(var->sqldata)/tens);
0248     case SQL_FLOAT:
0249         return static_cast<T1>(*reinterpret_cast<float*>(var->sqldata));
0250     case SQL_DOUBLE:
0251         return static_cast<T1>(*reinterpret_cast<double*>(var->sqldata));
0252     default:
0253         throw soci_error("Incorrect data type for numeric conversion");
0254     }
0255 
0256     SOCI_GCC_WARNING_RESTORE(cast-align)
0257 }
0258 
0259 } // namespace firebird
0260 
0261 } // namespace details
0262 
0263 } // namespace soci
0264 
0265 #endif // SOCI_FIREBIRD_COMMON_H_INCLUDED