/*
This file is part of libodbc++.
Copyright (C) 1999-2000 Manush Dodunekov <manush@stendahls.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef __ODBCXX_TYPES_H
#define __ODBCXX_TYPES_H
#include <odbc++/setup.h>
#include <exception>
#if !defined(ODBCXX_QT)
# include <string>
# else
# include <qstring.h>
#endif
#include <ctime>
#if defined(ODBCXX_QT)
class QIODevice;
#endif
#if defined(ODBCXX_HAVE_ISQL_H) && defined(ODBCXX_HAVE_ISQLEXT_H)
# include <isql.h>
# include <isqlext.h>
#elif defined(ODBCXX_HAVE_SQL_H) && defined(ODBCXX_HAVE_SQLEXT_H)
# include <sql.h>
# include <sqlext.h>
#else
# error "Whoops. Can not recognize the ODBC subsystem."
#endif
// fixups for current iODBC, which kindly doesn't provide SQL_TRUE and
// SQL_FALSE macros
#if !defined(SQL_TRUE)
# define SQL_TRUE 1
#endif
#if !defined(SQL_FALSE)
# define SQL_FALSE 0
#endif
// MS ODBC SDK misses this in some releases
#if ODBCVER >= 0x0300 && !defined(SQL_NOT_DEFERRABLE)
# define SQL_NOT_DEFERRABLE 7
#endif
// Setup our ODBC3_C (odbc3 conditional) macro
#if ODBCVER >= 0x0300
# define ODBC3_C(odbc3_value,old_value) odbc3_value
#else
# define ODBC3_C(odbc3_value,old_value) old_value
#endif
// ODBC3_DC (odbc3 dynamic conditional)
// Every context using this macro should provide
// a this->_getDriverInfo() method returning
// a const DriverInfo*
#if ODBCVER >= 0x0300
# define ODBC3_DC(odbc3_value,old_value) \
(this->_getDriverInfo()->getMajorVersion()>=3?odbc3_value:old_value)
#else
# define ODBC3_DC(odbc3_value,old_value) old_value
#endif
#if defined(ODBCXX_HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include <vector>
namespace odbc {
// We want Long to be at least 64 bits
#if defined(WIN32)
typedef __int64 Long;
#elif defined(ODBCXX_HAVE_INTTYPES_H)
typedef int64_t Long;
#else
# if ODBCXX_SIZEOF_INT == 8
typedef int Long;
# elif ODBCXX_SIZEOF_LONG == 8
typedef long Long;
# elif ODBCXX_SIZEOF_LONG_LONG == 8
typedef long long Long;
# else
# error "Can't find an appropriate at-least-64-bit integer"
# endif
#endif
//constants:
//how much we try to fetch with each SQLGetData call
const int GETDATA_CHUNK_SIZE=4*1024;
//how much we write with each SQLPutData call
const int PUTDATA_CHUNK_SIZE=GETDATA_CHUNK_SIZE;
//how much we read/write in string<->stream conversion
//better names for those?
const int STRING_TO_STREAM_CHUNK_SIZE=1024;
const int STREAM_TO_STRING_CHUNK_SIZE=STRING_TO_STREAM_CHUNK_SIZE;
struct Types {
enum SQLType {
BIGINT = SQL_BIGINT,
BINARY = SQL_BINARY,
BIT = SQL_BIT,
CHAR = SQL_CHAR,
DATE = ODBC3_C(SQL_TYPE_DATE,SQL_DATE),
DECIMAL = SQL_DECIMAL,
DOUBLE = SQL_DOUBLE,
FLOAT = SQL_FLOAT,
INTEGER = SQL_INTEGER,
LONGVARBINARY = SQL_LONGVARBINARY,
LONGVARCHAR = SQL_LONGVARCHAR,
NUMERIC = SQL_NUMERIC,
REAL = SQL_REAL,
SMALLINT = SQL_SMALLINT,
TIME = ODBC3_C(SQL_TYPE_TIME,SQL_TIME),
TIMESTAMP = ODBC3_C(SQL_TYPE_TIMESTAMP,SQL_TIMESTAMP),
TINYINT = SQL_TINYINT,
VARBINARY = SQL_VARBINARY,
VARCHAR = SQL_VARCHAR
};
};
#if !defined(ODBCXX_QT)
class ODBCXX_EXPORT Bytes {
private:
struct Rep {
signed char* buf_;
size_t len_;
int refCount_;
Rep(const signed char* b, size_t l)
:len_(l), refCount_(0) {
if(len_>0) {
buf_=new signed char[len_];
memcpy((void*)buf_,(void*)b,len_);
} else {
buf_=NULL;
}
}
~Rep() {
delete buf_;
}
};
Rep* rep_;
public:
Bytes()
:rep_(new Rep(NULL,0)) {
rep_->refCount_++;
}
Bytes(const signed char* data, size_t dataLen)
:rep_(new Rep(data,dataLen)) {
rep_->refCount_++;
}
Bytes(const Bytes& b)
:rep_(b.rep_) {
rep_->refCount_++;
}
Bytes& operator=(const Bytes& b) {
if(--rep_->refCount_==0) {
delete rep_;
}
rep_=b.rep_;
rep_->refCount_++;
return *this;
}
~Bytes() {
if(--rep_->refCount_==0) {
delete rep_;
}
}
const signed char* getData() const {
return rep_->buf_;
}
size_t getSize() const {
return rep_->len_;
}
};
#endif
class ODBCXX_EXPORT Date {
protected:
int year_;
int month_;
int day_;
virtual void _invalid(const char* what, int value);
int _validateYear(int y) {
return y;
}
int _validateMonth(int m) {
if(m<1 || m>12) {
this->_invalid("month",m);
}
return m;
}
int _validateDay(int d) {
if(d<1 || d>31) {
this->_invalid("day",d);
}
return d;
}
public:
Date(int year, int month, int day) {
this->setYear(year);
this->setMonth(month);
this->setDay(day);
}
explicit Date();
Date(time_t t) {
this->setTime(t);
}
Date(const ODBCXX_STRING& str) {
this->parse(str);
}
Date(const Date& d)
:year_(d.year_),
month_(d.month_),
day_(d.day_) {}
Date& operator=(const Date& d) {
year_=d.year_;
month_=d.month_;
day_=d.day_;
return *this;
}
virtual ~Date() {}
virtual void setTime(time_t t);
time_t getTime() const;
void parse(const ODBCXX_STRING& str);
int getYear() const {
return year_;
}
int getMonth() const {
return month_;
}
int getDay() const {
return day_;
}
void setYear(int year) {
year_=this->_validateYear(year);
}
void setMonth(int month) {
month_=this->_validateMonth(month);
}
void setDay(int day) {
day_=this->_validateDay(day);
}
virtual ODBCXX_STRING toString() const;
};
class ODBCXX_EXPORT Time {
protected:
int hour_;
int minute_;
int second_;
virtual void _invalid(const char* what, int value);
int _validateHour(int h) {
if(h<0 || h>23) {
this->_invalid("hour",h);
}
return h;
}
int _validateMinute(int m) {
if(m<0 || m>59) {
this->_invalid("minute",m);
}
return m;
}
int _validateSecond(int s) {
if(s<0 || s>61) {
this->_invalid("second",s);
}
return s;
}
public:
Time(int hour, int minute, int second) {
this->setHour(hour);
this->setMinute(minute);
this->setSecond(second);
}
explicit Time();
Time(time_t t) {
this->setTime(t);
}
Time(const ODBCXX_STRING& str) {
this->parse(str);
}
Time(const Time& t)
:hour_(t.hour_),
minute_(t.minute_),
second_(t.second_) {}
Time& operator=(const Time& t) {
hour_=t.hour_;
minute_=t.minute_;
second_=t.second_;
return *this;
}
virtual ~Time() {}
virtual void setTime(time_t t);
time_t getTime() const;
void parse(const ODBCXX_STRING& str);
int getHour() const {
return hour_;
}
int getMinute() const {
return minute_;
}
int getSecond() const {
return second_;
}
void setHour(int h) {
hour_=this->_validateHour(h);
}
void setMinute(int m) {
minute_=this->_validateMinute(m);
}
void setSecond(int s) {
second_=this->_validateSecond(s);
}
virtual ODBCXX_STRING toString() const;
};
class ODBCXX_EXPORT Timestamp : public Date, public Time {
private:
int nanos_;
virtual void _invalid(const char* what, int value);
int _validateNanos(int n) {
if(n<0) {
this->_invalid("nanoseconds",n);
}
return n;
}
public:
Timestamp(int year, int month, int day,
int hour, int minute, int second,
int nanos =0)
:Date(year,month,day), Time(hour,minute,second) {
this->setNanos(nanos);
}
explicit Timestamp();
Timestamp(time_t t) {
this->setTime(t);
}
Timestamp(const ODBCXX_STRING& s) {
this->parse(s);
}
Timestamp(const Timestamp& t)
:Date(t),Time(t),nanos_(t.nanos_) {}
Timestamp& operator=(const Timestamp& t) {
Date::operator=(t);
Time::operator=(t);
nanos_=t.nanos_;
return *this;
}
virtual ~Timestamp() {}
virtual void setTime(time_t t);
virtual time_t getTime() {
return Date::getTime()+Time::getTime();
}
void parse(const ODBCXX_STRING& s);
int getNanos() const {
return nanos_;
}
void setNanos(int nanos) {
nanos_=this->_validateNanos(nanos);
}
virtual ODBCXX_STRING toString() const;
};
//this is used for several 'lists of stuff' below
//expects T to be a pointer-to-something, and
//the contents will get deleted when the vector
//itself is deleted
template <class T> class CleanVector : public std::vector<T> {
private:
CleanVector(const CleanVector<T>&); //forbid
CleanVector<T>& operator=(const CleanVector<T>&); //forbid
public:
explicit CleanVector() {}
virtual ~CleanVector() {
typename std::vector<T>::iterator i=this->begin();
typename std::vector<T>::iterator end=this->end();
while(i!=end) {
delete *i;
++i;
}
this->clear();
}
};
class ODBCXX_EXPORT DriverMessage {
friend class ErrorHandler;
private:
char state_[SQL_SQLSTATE_SIZE+1];
char description_[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER nativeCode_;
DriverMessage() {}
public:
virtual ~DriverMessage() {}
const char* getSQLState() const {
return state_;
}
const char* getDescription() const {
return description_;
}
int getNativeCode() const {
return nativeCode_;
}
};
class SQLException : public std::exception {
private:
ODBCXX_STRING reason_;
ODBCXX_STRING sqlState_;
int errorCode_;
#if defined(ODBCXX_QT)
QCString reason8_;
#endif
public:
SQLException(const ODBCXX_STRING& reason ="",
const ODBCXX_STRING& sqlState ="",
int vendorCode =0)
:reason_(reason),
sqlState_(sqlState),
errorCode_(vendorCode)
#if defined(ODBCXX_QT)
,reason8_(reason.local8Bit())
#endif
{}
SQLException(const DriverMessage& dm)
:reason_(dm.getDescription()),
sqlState_(dm.getSQLState()),
errorCode_(dm.getNativeCode()) {}
virtual ~SQLException() {}
int getErrorCode() const {
return errorCode_;
}
const ODBCXX_STRING& getSQLState() const {
return sqlState_;
}
const ODBCXX_STRING& getMessage() const {
return reason_;
}
virtual const char* what() const {
// the conversion from QString involves a temporary, which
// doesn't survive this scope. So here, we do a conditional
#if defined(ODBCXX_QT)
return reason8_.data();
#else
return reason_.c_str();
#endif
}
};
class SQLWarning : public SQLException {
SQLWarning(const SQLWarning&); //forbid
SQLWarning& operator=(const SQLWarning&); //forbid
public:
SQLWarning(const ODBCXX_STRING& reason ="",
const ODBCXX_STRING& sqlState ="",
int vendorCode =0)
:SQLException(reason,sqlState,vendorCode) {}
SQLWarning(const DriverMessage& dm)
:SQLException(dm) {}
virtual ~SQLWarning() {}
};
typedef CleanVector<SQLWarning*> WarningList;
template <class T> class Deleter {
private:
T* ptr_;
bool isArray_;
Deleter(const Deleter<T>&);
Deleter<T>& operator=(const Deleter<T>&);
public:
explicit Deleter(T* ptr, bool isArray =false)
:ptr_(ptr), isArray_(isArray) {}
~Deleter() {
if(!isArray_) {
delete ptr_;
} else {
delete[] ptr_;
}
}
};
}; // namespace odbc
#endif // __ODBCXX_TYPES_H