Add date/time support, minor security improvements
This commit is contained in:
parent
7082aff6ac
commit
cf9e75feef
@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <firebird/Interface.h>
|
#include <cassert>
|
||||||
#include <firebird/Message.h>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -24,11 +22,11 @@ namespace fbsqlxx2 {
|
|||||||
|
|
||||||
#ifdef FBSQLXX_DEBUG
|
#ifdef FBSQLXX_DEBUG
|
||||||
template <typename ...Args>
|
template <typename ...Args>
|
||||||
void debug(Args&& ...args)
|
inline void debug(Args&& ...args)
|
||||||
{
|
{
|
||||||
(std::cout << ... << std::forward<Args>(args)) << std::endl;
|
(std::cout << ... << std::forward<Args>(args)) << std::endl;
|
||||||
}
|
}
|
||||||
void debug(const char* s)
|
inline void debug(const char* s)
|
||||||
{
|
{
|
||||||
std::cout << s << std::endl;
|
std::cout << s << std::endl;
|
||||||
}
|
}
|
||||||
@ -64,67 +62,24 @@ inline void print_metadata(Firebird::IMessageMetadata* md, Firebird::ThrowStatus
|
|||||||
#endif // FBSQLXX_DEBUG
|
#endif // FBSQLXX_DEBUG
|
||||||
|
|
||||||
|
|
||||||
class connection;
|
// static functions
|
||||||
class transaction;
|
inline Firebird::IMaster* master()
|
||||||
class statement;
|
|
||||||
class result_set;
|
|
||||||
|
|
||||||
static inline Firebird::IMaster* master()
|
|
||||||
{
|
{
|
||||||
static Firebird::IMaster* _master = Firebird::fb_get_master_interface();
|
static Firebird::IMaster* _master = Firebird::fb_get_master_interface();
|
||||||
return _master;
|
return _master;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Firebird::IUtil* util()
|
inline Firebird::IUtil* util()
|
||||||
{
|
{
|
||||||
static Firebird::IUtil* _util = master()->getUtilInterface();
|
static Firebird::IUtil* _util = master()->getUtilInterface();
|
||||||
return _util;
|
return _util;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int64_t portable_integer(const uint8_t* p, short length)
|
inline int64_t portable_integer(const uint8_t* p, short length)
|
||||||
{
|
{
|
||||||
return isc_portable_integer(p, length);
|
return isc_portable_integer(p, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// exceptions
|
|
||||||
class error : public std::runtime_error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
error(const char* msg)
|
|
||||||
: std::runtime_error(msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class sql_error : public error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sql_error(const char* msg, const Firebird::FbException* cause)
|
|
||||||
: error(msg), m_cause{ cause }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const Firebird::FbException* cause() const { return m_cause; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Firebird::FbException* m_cause;
|
|
||||||
};
|
|
||||||
|
|
||||||
class logic_error : public error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
logic_error(const char* msg)
|
|
||||||
: error(msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CATCH_SQL \
|
|
||||||
catch (const Firebird::FbException& ex) { \
|
|
||||||
char buf[FBSQLXX_EXCEPTION_BUFFER_SIZE]; \
|
|
||||||
util()->formatStatus(buf, sizeof(buf), ex.getStatus()); \
|
|
||||||
throw sql_error{ buf, &ex }; }
|
|
||||||
|
|
||||||
|
|
||||||
inline std::string type_name(unsigned int type)
|
inline std::string type_name(unsigned int type)
|
||||||
{
|
{
|
||||||
switch (type & ~1u)
|
switch (type & ~1u)
|
||||||
@ -191,14 +146,149 @@ inline std::string type_name(unsigned int type)
|
|||||||
|
|
||||||
case SQL_VARYING:
|
case SQL_VARYING:
|
||||||
return "VARCHAR";
|
return "VARCHAR";
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// types
|
||||||
|
class connection;
|
||||||
|
class transaction;
|
||||||
|
class statement;
|
||||||
|
class result_set;
|
||||||
|
|
||||||
|
using octets = std::vector<uint8_t>;
|
||||||
|
|
||||||
|
struct date
|
||||||
|
{
|
||||||
|
unsigned year;
|
||||||
|
unsigned month;
|
||||||
|
unsigned day;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct time
|
||||||
|
{
|
||||||
|
unsigned hours;
|
||||||
|
unsigned minutes;
|
||||||
|
unsigned seconds;
|
||||||
|
unsigned fractions;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct time_tz
|
||||||
|
{
|
||||||
|
time utc_time;
|
||||||
|
ISC_USHORT time_zone;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct time_tz_ex
|
||||||
|
{
|
||||||
|
time utc_time;
|
||||||
|
ISC_USHORT time_zone;
|
||||||
|
short ext_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct timestamp
|
||||||
|
{
|
||||||
|
date date;
|
||||||
|
time time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct timestamp_tz
|
||||||
|
{
|
||||||
|
timestamp utc_timestamp;
|
||||||
|
ISC_USHORT time_zone;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct timestamp_tz_ex
|
||||||
|
{
|
||||||
|
timestamp utc_timestamp;
|
||||||
|
ISC_USHORT time_zone;
|
||||||
|
short ext_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// exceptions
|
||||||
|
class error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
error(const char* msg)
|
||||||
|
: std::runtime_error(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class sql_error : public error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sql_error(const char* msg, const Firebird::FbException* cause)
|
||||||
|
: error(msg), m_cause{ cause }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const Firebird::FbException* cause() const { return m_cause; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Firebird::FbException* m_cause;
|
||||||
|
};
|
||||||
|
|
||||||
|
class logic_error : public error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
logic_error(const char* msg)
|
||||||
|
: error(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CATCH_SQL \
|
||||||
|
catch (const Firebird::FbException& ex) { \
|
||||||
|
char buf[FBSQLXX_EXCEPTION_BUFFER_SIZE]; \
|
||||||
|
util()->formatStatus(buf, sizeof(buf), ex.getStatus()); \
|
||||||
|
throw sql_error{ buf, &ex }; }
|
||||||
|
|
||||||
|
|
||||||
|
namespace cvt {
|
||||||
|
|
||||||
|
inline date to_date(ISC_DATE isc_date)
|
||||||
|
{
|
||||||
|
date d{};
|
||||||
|
util()->decodeDate(isc_date, &d.year, &d.month, &d.day);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time to_time(ISC_TIME isc_time)
|
||||||
|
{
|
||||||
|
time t{};
|
||||||
|
util()->decodeTime(isc_time, &t.hours, &t.minutes, &t.seconds, &t.fractions);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline time_tz to_time_tz(ISC_TIME_TZ isc_time)
|
||||||
|
{
|
||||||
|
time t{};
|
||||||
|
util()->decodeTime(isc_time.utc_time, &t.hours, &t.minutes, &t.seconds, &t.fractions);
|
||||||
|
return time_tz{ t, isc_time.time_zone };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline timestamp to_timestamp(ISC_TIMESTAMP isc_ts)
|
||||||
|
{
|
||||||
|
date d{};
|
||||||
|
time t{};
|
||||||
|
util()->decodeDate(isc_ts.timestamp_date, &d.year, &d.month, &d.day);
|
||||||
|
util()->decodeTime(isc_ts.timestamp_time, &t.hours, &t.minutes, &t.seconds, &t.fractions);
|
||||||
|
return timestamp{ d, t };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline timestamp_tz to_timestamp(ISC_TIMESTAMP_TZ isc_ts)
|
||||||
|
{
|
||||||
|
date d{};
|
||||||
|
time t{};
|
||||||
|
util()->decodeDate(isc_ts.utc_timestamp.timestamp_date, &d.year, &d.month, &d.day);
|
||||||
|
util()->decodeTime(isc_ts.utc_timestamp.timestamp_time, &t.hours, &t.minutes, &t.seconds, &t.fractions);
|
||||||
|
return timestamp_tz{ {d, t}, isc_ts.time_zone };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cvt
|
||||||
|
|
||||||
|
|
||||||
namespace _details {
|
namespace _details {
|
||||||
|
|
||||||
@ -345,12 +435,36 @@ public:
|
|||||||
cast<double>(offset) = param.double_value;
|
cast<double>(offset) = param.double_value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SQL_FLOAT:
|
||||||
|
cast<float>(offset) = param.float_value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_TYPE_DATE:
|
||||||
|
cast<ISC_DATE>(offset) = param.date_value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_TYPE_TIME:
|
||||||
|
cast<ISC_TIME>(offset) = param.time_value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_TIME_TZ:
|
||||||
|
cast<ISC_TIME_TZ>(offset) = param.time_tz_value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_TIMESTAMP:
|
||||||
|
cast<ISC_TIMESTAMP>(offset) = param.timestamp_value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_TIMESTAMP_TZ:
|
||||||
|
cast<ISC_TIMESTAMP_TZ>(offset) = param.timestamp_tz_value;
|
||||||
|
break;
|
||||||
|
|
||||||
case SQL_TEXT:
|
case SQL_TEXT:
|
||||||
memcpy(((void*)offset), param.str_value.data(), param.str_value.size());
|
memcpy(((void*)offset), param.str_value.data(), param.str_value.size());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SQL_VARYING:
|
case SQL_VARYING:
|
||||||
cast<short>(offset) = static_cast<short>(param.str_value.size());
|
cast<int16_t>(offset) = static_cast<int16_t>(param.str_value.size());
|
||||||
memcpy(offset + 2, param.str_value.data(), param.str_value.size());
|
memcpy(offset + 2, param.str_value.data(), param.str_value.size());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -359,10 +473,12 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
{
|
||||||
std::string msg = "Not implemented parameter type: ";
|
std::string msg = "Not implemented parameter type: ";
|
||||||
msg += type_name(param.type);
|
msg += type_name(param.type);
|
||||||
throw logic_error(msg.data());
|
throw logic_error(msg.data());
|
||||||
}
|
}
|
||||||
|
} // switch (param.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
print_metadata(md, m_status);
|
print_metadata(md, m_status);
|
||||||
@ -372,7 +488,7 @@ public:
|
|||||||
|
|
||||||
void setNull(unsigned int i)
|
void setNull(unsigned int i)
|
||||||
{
|
{
|
||||||
m_parameters[i] = {};
|
m_parameters.at(i) = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, int16_t value, int scale)
|
void set(unsigned int i, int16_t value, int scale)
|
||||||
@ -381,7 +497,7 @@ public:
|
|||||||
p.type = SQL_SHORT;
|
p.type = SQL_SHORT;
|
||||||
p.scale = -scale;
|
p.scale = -scale;
|
||||||
p.int16_value = value;
|
p.int16_value = value;
|
||||||
m_parameters[i] = p;
|
m_parameters.at(i) = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, int32_t value, int scale)
|
void set(unsigned int i, int32_t value, int scale)
|
||||||
@ -390,7 +506,7 @@ public:
|
|||||||
p.type = SQL_LONG;
|
p.type = SQL_LONG;
|
||||||
p.scale = -scale;
|
p.scale = -scale;
|
||||||
p.int32_value = value;
|
p.int32_value = value;
|
||||||
m_parameters[i] = p;
|
m_parameters.at(i) = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, int64_t value, int scale)
|
void set(unsigned int i, int64_t value, int scale)
|
||||||
@ -399,7 +515,7 @@ public:
|
|||||||
p.type = SQL_INT64;
|
p.type = SQL_INT64;
|
||||||
p.scale = -scale;
|
p.scale = -scale;
|
||||||
p.int64_value = value;
|
p.int64_value = value;
|
||||||
m_parameters[i] = p;
|
m_parameters.at(i) = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, double value)
|
void set(unsigned int i, double value)
|
||||||
@ -407,7 +523,51 @@ public:
|
|||||||
parameter p{};
|
parameter p{};
|
||||||
p.type = SQL_DOUBLE;
|
p.type = SQL_DOUBLE;
|
||||||
p.double_value = value;
|
p.double_value = value;
|
||||||
m_parameters[i] = p;
|
m_parameters.at(i) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(unsigned int i, date value)
|
||||||
|
{
|
||||||
|
parameter p{};
|
||||||
|
p.type = SQL_TYPE_DATE;
|
||||||
|
p.date_value = util()->encodeDate(value.year, value.month, value.day);
|
||||||
|
m_parameters.at(i) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(unsigned int i, time value)
|
||||||
|
{
|
||||||
|
parameter p{};
|
||||||
|
p.type = SQL_TYPE_TIME;
|
||||||
|
p.time_value = util()->encodeTime(value.hours, value.minutes, value.seconds, value.fractions);
|
||||||
|
m_parameters.at(i) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(unsigned int i, time_tz value)
|
||||||
|
{
|
||||||
|
parameter p{};
|
||||||
|
p.type = SQL_TIME_TZ;
|
||||||
|
p.time_tz_value.utc_time = util()->encodeTime(value.utc_time.hours, value.utc_time.minutes, value.utc_time.seconds, value.utc_time.fractions);
|
||||||
|
p.time_tz_value.time_zone = value.time_zone;
|
||||||
|
m_parameters.at(i) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(unsigned int i, timestamp value)
|
||||||
|
{
|
||||||
|
parameter p{};
|
||||||
|
p.type = SQL_TIMESTAMP;
|
||||||
|
p.timestamp_value.timestamp_date = util()->encodeDate(value.date.year, value.date.month, value.date.day);
|
||||||
|
p.timestamp_value.timestamp_time = util()->encodeTime(value.time.hours, value.time.minutes, value.time.seconds, value.time.fractions);
|
||||||
|
m_parameters.at(i) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(unsigned int i, timestamp_tz value)
|
||||||
|
{
|
||||||
|
parameter p{};
|
||||||
|
p.type = SQL_TIMESTAMP_TZ;
|
||||||
|
p.timestamp_tz_value.utc_timestamp.timestamp_date = util()->encodeDate(value.utc_timestamp.date.year, value.utc_timestamp.date.month, value.utc_timestamp.date.day);
|
||||||
|
p.timestamp_tz_value.utc_timestamp.timestamp_time = util()->encodeTime(value.utc_timestamp.time.hours, value.utc_timestamp.time.minutes, value.utc_timestamp.time.seconds, value.utc_timestamp.time.fractions);
|
||||||
|
p.timestamp_tz_value.time_zone = value.time_zone;
|
||||||
|
m_parameters.at(i) = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, const char* value)
|
void set(unsigned int i, const char* value)
|
||||||
@ -415,7 +575,7 @@ public:
|
|||||||
parameter p{};
|
parameter p{};
|
||||||
p.type = SQL_TEXT;
|
p.type = SQL_TEXT;
|
||||||
p.str_value = value;
|
p.str_value = value;
|
||||||
m_parameters[i] = std::move(p);
|
m_parameters.at(i) = std::move(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(unsigned int i, const std::vector<uint8_t>& value)
|
void set(unsigned int i, const std::vector<uint8_t>& value)
|
||||||
@ -423,7 +583,7 @@ public:
|
|||||||
parameter p{};
|
parameter p{};
|
||||||
p.type = MY_SQL_OCTETS;
|
p.type = MY_SQL_OCTETS;
|
||||||
p.octets_value = value;
|
p.octets_value = value;
|
||||||
m_parameters[i] = std::move(p);
|
m_parameters.at(i) = std::move(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -431,31 +591,40 @@ private:
|
|||||||
{
|
{
|
||||||
const auto count = static_cast<unsigned>(m_parameters.size());
|
const auto count = static_cast<unsigned>(m_parameters.size());
|
||||||
auto builder = make_autodestroy(master()->getMetadataBuilder(m_status, count));
|
auto builder = make_autodestroy(master()->getMetadataBuilder(m_status, count));
|
||||||
|
|
||||||
for (unsigned i = 0; i < count; ++i)
|
for (unsigned i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
auto const& param = m_parameters[i];
|
auto const& param = m_parameters[i];
|
||||||
|
|
||||||
if (param.type) // not null
|
if (param.type) // not null
|
||||||
{
|
{
|
||||||
if (param.type == SQL_TEXT)
|
switch (param.type)
|
||||||
{
|
{
|
||||||
|
case SQL_TEXT:
|
||||||
|
{
|
||||||
|
builder->setType(m_status, i, SQL_TEXT + 1);
|
||||||
auto length = static_cast<unsigned>(param.str_value.size());
|
auto length = static_cast<unsigned>(param.str_value.size());
|
||||||
builder->setLength(m_status, i, length);
|
builder->setLength(m_status, i, length);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (param.type == MY_SQL_OCTETS)
|
case MY_SQL_OCTETS:
|
||||||
{
|
{
|
||||||
builder->setType(m_status, i, SQL_TEXT + 1);
|
builder->setType(m_status, i, SQL_TEXT + 1);
|
||||||
auto length = static_cast<unsigned>(param.octets_value.size());
|
auto length = static_cast<unsigned>(param.str_value.size());
|
||||||
builder->setLength(m_status, i, length);
|
builder->setLength(m_status, i, length);
|
||||||
}
|
}
|
||||||
else
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
{
|
{
|
||||||
builder->setType(m_status, i, param.type + 1);
|
builder->setType(m_status, i, param.type + 1);
|
||||||
builder->setSubType(m_status, i, param.subtype);
|
builder->setSubType(m_status, i, param.subtype);
|
||||||
if (param.scale)
|
if (param.scale)
|
||||||
builder->setScale(m_status, i, param.scale);
|
builder->setScale(m_status, i, param.scale);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
} // switch (param.type)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
builder->setType(m_status, i, SQL_BOOLEAN + 1); // null, type not important?
|
builder->setType(m_status, i, SQL_BOOLEAN + 1); // null, type not important?
|
||||||
@ -644,8 +813,9 @@ public:
|
|||||||
/// <returns>true if column is null</returns>
|
/// <returns>true if column is null</returns>
|
||||||
bool is_null(unsigned index) const
|
bool is_null(unsigned index) const
|
||||||
{
|
{
|
||||||
|
assert(index >= 1);
|
||||||
auto const& f = m_fields.at(index - 1);
|
auto const& f = m_fields.at(index - 1);
|
||||||
return 0 != cast<int16_t>(m_buffer.data() + f.null);
|
return 0 != cast<int16_t>(m_buffer.data() + f.null_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -657,14 +827,10 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<!std::is_integral_v<T>, T> as(unsigned index) const
|
std::enable_if_t<!std::is_integral_v<T>, T> as(unsigned index) const
|
||||||
{
|
{
|
||||||
|
assert(index >= 1);
|
||||||
auto const& f = m_fields.at(index - 1);
|
auto const& f = m_fields.at(index - 1);
|
||||||
|
check_field_null(f.name, f.null_offset);
|
||||||
if (sizeof(T) > f.length)
|
check_field_size(f.name, f.length, sizeof(T));
|
||||||
{
|
|
||||||
// XXX: field value size override
|
|
||||||
throw logic_error("!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return cast<T>(m_buffer.data() + f.offset);
|
return cast<T>(m_buffer.data() + f.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,15 +843,11 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral_v<T>, T> as(unsigned index) const
|
std::enable_if_t<std::is_integral_v<T>, T> as(unsigned index) const
|
||||||
{
|
{
|
||||||
|
assert(index >= 1);
|
||||||
auto const& f = m_fields.at(index - 1);
|
auto const& f = m_fields.at(index - 1);
|
||||||
|
check_field_null(f.name, f.null_offset);
|
||||||
if (sizeof(T) > f.length)
|
check_field_size(f.name, f.length, sizeof(T));
|
||||||
{
|
return static_cast<T>(portable_integer(m_buffer.data() + f.offset, sizeof(T)));
|
||||||
// XXX: field value size override
|
|
||||||
throw logic_error("!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return portable_integer(m_buffer.data() + f.offset, sizeof(T));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Firebird::IResultSet* operator->() const
|
Firebird::IResultSet* operator->() const
|
||||||
@ -701,10 +863,33 @@ private:
|
|||||||
return *((const T*)offset);
|
return *((const T*)offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_field_null(const char* name, unsigned null_offset) const
|
||||||
|
{
|
||||||
|
if (0 != cast<int16_t>(m_buffer.data() + null_offset))
|
||||||
|
{
|
||||||
|
auto e = std::string("Field is null: [")
|
||||||
|
+ (name ? name : "")
|
||||||
|
+ "]";
|
||||||
|
throw logic_error(e.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_field_size(const char* name, size_t actual, size_t requested) const
|
||||||
|
{
|
||||||
|
if (requested > actual)
|
||||||
|
{
|
||||||
|
auto e = std::string("Field value size override: [")
|
||||||
|
+ (name ? name : "")
|
||||||
|
+ "], length=" + std::to_string(actual)
|
||||||
|
+ ", requested=" + std::to_string(requested);
|
||||||
|
throw logic_error(e.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct field
|
struct field
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
unsigned type, subtype, length, offset, null;
|
unsigned type, subtype, length, offset, null_offset;
|
||||||
int scale;
|
int scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -719,7 +904,7 @@ private:
|
|||||||
f.subtype = m_meta->getSubType(m_status, i);
|
f.subtype = m_meta->getSubType(m_status, i);
|
||||||
f.length = m_meta->getLength(m_status, i);
|
f.length = m_meta->getLength(m_status, i);
|
||||||
f.offset = m_meta->getOffset(m_status, i);
|
f.offset = m_meta->getOffset(m_status, i);
|
||||||
f.null = m_meta->getNullOffset(m_status, i);
|
f.null_offset = m_meta->getNullOffset(m_status, i);
|
||||||
f.scale = m_meta->getScale(m_status, i);
|
f.scale = m_meta->getScale(m_status, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -778,7 +963,9 @@ private:
|
|||||||
template <>
|
template <>
|
||||||
inline std::string result_set::as<std::string>(unsigned i) const
|
inline std::string result_set::as<std::string>(unsigned i) const
|
||||||
{
|
{
|
||||||
|
assert(i >= 1);
|
||||||
auto const& f = m_fields.at(i - 1);
|
auto const& f = m_fields.at(i - 1);
|
||||||
|
check_field_null(f.name, f.null_offset);
|
||||||
auto start = m_buffer.data() + f.offset;
|
auto start = m_buffer.data() + f.offset;
|
||||||
return std::string(start, start + f.length);
|
return std::string(start, start + f.length);
|
||||||
}
|
}
|
||||||
@ -791,7 +978,9 @@ inline std::string result_set::as<std::string>(unsigned i) const
|
|||||||
template <>
|
template <>
|
||||||
inline std::vector<uint8_t> result_set::as<std::vector<uint8_t>>(unsigned i) const
|
inline std::vector<uint8_t> result_set::as<std::vector<uint8_t>>(unsigned i) const
|
||||||
{
|
{
|
||||||
|
assert(i >= 1);
|
||||||
auto const& f = m_fields.at(i - 1);
|
auto const& f = m_fields.at(i - 1);
|
||||||
|
check_field_null(f.name, f.null_offset);
|
||||||
auto start = m_buffer.data() + f.offset;
|
auto start = m_buffer.data() + f.offset;
|
||||||
return std::vector<uint8_t>(start, start + f.length);
|
return std::vector<uint8_t>(start, start + f.length);
|
||||||
}
|
}
|
||||||
@ -939,6 +1128,41 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statement& set(unsigned i, date value)
|
||||||
|
{
|
||||||
|
debug("statement: ", this, ", set #", i, " -> date");
|
||||||
|
get_input_parameters()->set(i - 1, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
statement& set(unsigned i, time value)
|
||||||
|
{
|
||||||
|
debug("statement: ", this, ", set #", i, " -> time");
|
||||||
|
get_input_parameters()->set(i - 1, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
statement& set(unsigned i, time_tz value)
|
||||||
|
{
|
||||||
|
debug("statement: ", this, ", set #", i, " -> time_tz");
|
||||||
|
get_input_parameters()->set(i - 1, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
statement& set(unsigned i, timestamp value)
|
||||||
|
{
|
||||||
|
debug("statement: ", this, ", set #", i, " -> timestamp");
|
||||||
|
get_input_parameters()->set(i - 1, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
statement& set(unsigned i, timestamp_tz value)
|
||||||
|
{
|
||||||
|
debug("statement: ", this, ", set #", i, " -> timestamp_tz");
|
||||||
|
get_input_parameters()->set(i - 1, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
statement& set(unsigned i, const char* value)
|
statement& set(unsigned i, const char* value)
|
||||||
{
|
{
|
||||||
debug("statement: ", this, ", set #", i, " -> ", value);
|
debug("statement: ", this, ", set #", i, " -> ", value);
|
||||||
@ -1393,4 +1617,5 @@ inline connection connect(const char* database, const char* user, const char* pa
|
|||||||
} // namespace fbsqlxx2
|
} // namespace fbsqlxx2
|
||||||
|
|
||||||
#undef debug
|
#undef debug
|
||||||
|
#undef print_metadata
|
||||||
#undef CATCH_SQL
|
#undef CATCH_SQL
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user