ultimatepp/bazaar/Firebird/Firebird.cpp
novo 2172128568 Firebird. Fix compilation in Linux.
git-svn-id: svn://ultimatepp.org/upp/trunk@7842 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2014-10-30 12:56:42 +00:00

1894 lines
38 KiB
C++

#include "Firebird.h"
#include <time.h>
NAMESPACE_UPP
TransAuto::TransAuto(SqlSession& s) : abort(true), isSession(true), session(&s)
{
session->Begin();
}
TransAuto::TransAuto(Sql& s) : abort(true), isSession(false), sql(&s)
{
sql->Begin();
}
TransAuto::~TransAuto()
{
try {
if (isSession)
{
if(abort) {
session->Rollback();
} else {
session->Commit();
}
} else {
if(abort) {
sql->Rollback();
} else {
sql->Commit();
}
}
} catch(...) {
// Ignore all exceptions in destructor.
ASSERT(false);
}
}
class TransRetain
{
public:
TransRetain(FBSession& s) : abort(true), session(&s)
{
}
~TransRetain()
{
try {
if(abort) {
session->RollbackRetaining();
} else {
session->CommitRetaining();
}
} catch(...) {
// Ignore all exceptions in the destructor.
ASSERT(false);
}
}
public:
operator bool() const { return abort; }
void Finish() { abort = false; }
protected:
bool abort:1;
FBSession* session;
};
#define TRANSACTION_RETAIN(trans) \
for(TransRetain auto_trans_##__LINE__(trans); \
auto_trans_##__LINE__; \
auto_trans_##__LINE__.Finish() )
struct FBValue {
short nullInd;
XSQLVAR* pXSQLVar;
FBValue(XSQLVAR& v);
virtual ~FBValue();
bool CanBeNull() const { return pXSQLVar->sqltype & 1; }
bool IsNull() const { return nullInd == -1; }
void SetNull(bool flag = true) { nullInd = flag ? -1 : 0; }
virtual void GetValue(Ref r) const = 0;
virtual void SetValue(const Value& v) = 0;
};
FBValue::FBValue(XSQLVAR& v)
{
pXSQLVar = &v;
if (CanBeNull())
{
v.sqlind = &nullInd;
nullInd = -1;
}
}
FBValue::~FBValue()
{
}
struct FBInt : public FBValue {
int value;
FBInt(XSQLVAR& v);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
};
FBInt::FBInt(XSQLVAR& v) : FBValue(v)
{
v.sqltype = SQL_LONG + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
}
void FBInt::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
switch(r.GetType())
{
case INT_V:
RefInt(r) = value;
break;
case DOUBLE_V:
RefDouble(r) = static_cast<double>(value);
break;
case STRING_V:
RefString(r) = FormatInt(value);
break;
case VALUE_V:
RefValue(r) = Value(value);
break;
case WSTRING_V:
RefWString(r) = FormatInt(value).ToWString();
break;
case INT64_V:
RefInt64(r) = value;
break;
case BOOL_V:
RefBool(r) = value != 0;
break;
case DATE_V:
case TIME_V:
default:
ASSERT(false);
break;
}
}
}
void FBInt::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
switch (v.GetType())
{
case INT_V:
value = ValueTo<int>(v);
break;
case DOUBLE_V:
value = static_cast<int>(ValueTo<double>(v));
break;
case STRING_V:
value = ScanInt(ValueTo<String>(v));
break;
// case DATE_V:
// case TIME_V:
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
value = ScanInt(ValueTo<WString>(v).ToString());
break;
case INT64_V:
value = static_cast<int>(ValueTo<int64>(v));
break;
case BOOL_V:
value = ValueTo<bool>(v) ? 1 : 0;
break;
default:
ASSERT(false);
break;
}
}
struct FBInt64 : public FBValue {
int64 value;
FBInt64(XSQLVAR& v);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
};
FBInt64::FBInt64(XSQLVAR& v) : FBValue(v)
{
v.sqltype = SQL_INT64 + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
}
void FBInt64::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
switch(r.GetType())
{
case INT_V:
RefInt(r) = static_cast<int>(value);
break;
case DOUBLE_V:
RefDouble(r) = static_cast<double>(value);
break;
case STRING_V:
RefString(r) = FormatInt64(value);
break;
case VALUE_V:
RefValue(r) = Value(value);
break;
case WSTRING_V:
RefWString(r) = FormatInt64(value).ToWString();
break;
case INT64_V:
RefInt64(r) = value;
break;
case BOOL_V:
RefBool(r) = value != 0;
break;
case DATE_V:
case TIME_V:
default:
ASSERT(false);
break;
}
}
}
void FBInt64::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
switch (v.GetType())
{
case INT_V:
value = ValueTo<int>(v);
break;
case DOUBLE_V:
value = static_cast<int64>(ValueTo<double>(v));
break;
case STRING_V:
value = ScanInt64(ValueTo<String>(v));
break;
// case DATE_V:
// case TIME_V:
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
value = ScanInt64(ValueTo<WString>(v).ToString());
break;
case INT64_V:
value = ValueTo<int64>(v);
break;
case BOOL_V:
value = ValueTo<bool>(v) ? 1 : 0;
break;
default:
ASSERT(false);
break;
}
}
struct FBDouble : public FBValue {
double value;
FBDouble(XSQLVAR& v);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
};
FBDouble::FBDouble(XSQLVAR& v) : FBValue(v)
{
v.sqltype = SQL_DOUBLE + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
v.sqlscale = 18;
}
void FBDouble::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
switch(r.GetType())
{
case INT_V:
RefInt(r) = static_cast<int>(value);
break;
case DOUBLE_V:
RefDouble(r) = value;
break;
case STRING_V:
RefString(r) = FormatDouble(value);
break;
case VALUE_V:
RefValue(r) = Value(value);
break;
case WSTRING_V:
RefWString(r) = FormatDouble(value).ToWString();
break;
case INT64_V:
RefInt64(r) = static_cast<int64>(value);
break;
case BOOL_V:
RefBool(r) = value != 0.0;
break;
case DATE_V:
case TIME_V:
default:
ASSERT(false);
break;
}
}
}
void FBDouble::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
switch (v.GetType())
{
case INT_V:
value = ValueTo<int>(v);
break;
case DOUBLE_V:
value = ValueTo<double>(v);
break;
case STRING_V:
value = ScanDouble(ValueTo<String>(v));
break;
// case DATE_V:
// case TIME_V:
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
value = ScanDouble(ValueTo<WString>(v).ToString());
break;
case INT64_V:
value = static_cast<double>(ValueTo<int64>(v));
break;
case BOOL_V:
value = ValueTo<bool>(v) ? 1.0 : 0.0;
break;
default:
ASSERT(false);
break;
}
}
// This macro is used to declare structures representing SQL VARCHAR types
#define SQL_VARCHAR(len) struct {short vary_length; char vary_string[(len) + 1];}
struct FBVarying : public FBValue {
// enum { max_varchar_size = 8 * 1024 };
enum { max_varchar_size = 32765 };
//String value;
SQL_VARCHAR(32765) buff;
FBVarying(XSQLVAR& v);
void SetValue(const String& str);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
};
FBVarying::FBVarying(XSQLVAR& v) : FBValue(v)
{
v.sqltype = SQL_VARYING + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&buff);
v.sqllen = v.sqllen == 0 ? max_varchar_size : v.sqllen;
}
void FBVarying::SetValue(const String& str)
{
ASSERT(pXSQLVar);
buff.vary_length = min<short>(str.GetCount(), pXSQLVar->sqllen);
strncpy(buff.vary_string, ~str, buff.vary_length);
}
void FBVarying::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
switch(r.GetType()) {
case INT_V:
{
const char* e = buff.vary_string + buff.vary_length;
RefInt(r) = ScanInt(buff.vary_string, &e);
}
break;
case DOUBLE_V:
{
const char* e = buff.vary_string + buff.vary_length;
RefDouble(r) = ScanDouble(buff.vary_string, &e);
}
break;
case STRING_V:
RefString(r) = String(buff.vary_string, buff.vary_length);
break;
case DATE_V:
r.SetValue(UPP::Scan(DATE_V, String(buff.vary_string, buff.vary_length)));
break;
case TIME_V:
r.SetValue(UPP::Scan(TIME_V, String(buff.vary_string, buff.vary_length)));
break;
case VALUE_V:
RefValue(r) = Value(String(buff.vary_string, buff.vary_length));
break;
case WSTRING_V:
RefWString(r) = WString(buff.vary_string, buff.vary_length);
break;
case INT64_V:
{
const char* e = buff.vary_string + buff.vary_length;
RefInt64(r) = ScanInt64(buff.vary_string, &e);
}
break;
case BOOL_V:
{
const char* e = buff.vary_string + buff.vary_length;
RefBool(r) = ScanInt(buff.vary_string, &e) != 0;
}
break;
default:
ASSERT(false);
break;
}
}
}
void FBVarying::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
switch (v.GetType())
{
case INT_V:
SetValue(AsString(ValueTo<int>(v)));
break;
case DOUBLE_V:
SetValue(AsString(ValueTo<double>(v)));
break;
case STRING_V:
SetValue(ValueTo<String>(v));
break;
// case DATE_V:
// case TIME_V:
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
SetValue(ValueTo<WString>(v).ToString());
break;
case INT64_V:
SetValue(AsString(ValueTo<int64>(v)));
break;
case BOOL_V:
SetValue(IntStr((int)ValueTo<bool>(v)));
break;
default:
ASSERT(false);
break;
}
}
struct FBTimeStamp : public FBValue {
ISC_TIMESTAMP value;
FBTimeStamp(XSQLVAR& v, T_FB& dll);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
T_FB& dll;
};
FBTimeStamp::FBTimeStamp(XSQLVAR& v, T_FB& dll) : FBValue(v), dll(dll)
{
v.sqltype = SQL_TIMESTAMP + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
}
void FBTimeStamp::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
const LanguageInfo& li = GetLanguageInfo();
struct tm time;
dll.isc_decode_timestamp(&value, &time);
const Time t(time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec);
switch(r.GetType()) {
case STRING_V:
RefString(r) = li.FormatTime(t);
break;
case DATE_V:
RefDate(r) = t;
break;
case TIME_V:
RefTime(r) = t;
break;
case VALUE_V:
RefValue(r) = t;
break;
case WSTRING_V:
RefWString(r) = li.FormatTime(t).ToWString();
break;
case INT64_V:
RefInt64(r) = t.Get();
break;
case DOUBLE_V:
RefDouble(r) = static_cast<double>(t.Get());
break;
case INT_V:
case BOOL_V:
default:
ASSERT(false);
break;
}
}
}
void FBTimeStamp::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
Time t;
switch (v.GetType())
{
case INT_V:
t.Set(ValueTo<int>(v));
break;
case DOUBLE_V:
t.Set((int64)ValueTo<double>(v));
case STRING_V:
t = UPP::Scan(TIME_V, ValueTo<String>(v));
break;
case DATE_V:
{
const Date& d = ValueTo<Date>(v);
t = Time(d.year, d.month, d.day);
}
break;
case TIME_V:
t = ValueTo<Time>(v);
break;
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
t = UPP::Scan(TIME_V, ValueTo<WString>(v).ToString());
break;
case INT64_V:
t.Set(ValueTo<int64>(v));
break;
case BOOL_V:
default:
ASSERT(false);
break;
}
struct tm time;
time.tm_year = t.year - 1900;
time.tm_mon = t.month - 1;
time.tm_mday = t.day;
time.tm_hour = t.hour;
time.tm_min = t.minute;
time.tm_sec = t.second;
time.tm_isdst = -1;
dll.isc_encode_timestamp(&time, &value);
}
struct FBDate : public FBValue {
ISC_DATE value;
FBDate(XSQLVAR& v, T_FB& dll);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
T_FB& dll;
};
FBDate::FBDate(XSQLVAR& v, T_FB& dll) : FBValue(v), dll(dll)
{
v.sqltype = SQL_DATE + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
}
void FBDate::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
const LanguageInfo& li = GetLanguageInfo();
struct tm time;
dll.isc_decode_sql_date(&value, &time);
const Date d(time.tm_year + 1900, time.tm_mon + 1, time.tm_mday);
switch(r.GetType()) {
case STRING_V:
RefString(r) = li.FormatDate(d);
break;
case DATE_V:
RefDate(r) = d;
break;
case TIME_V:
RefTime(r) = Time(d.year, d.month, d.day);
break;
case VALUE_V:
RefValue(r) = d;
break;
case WSTRING_V:
RefWString(r) = li.FormatDate(d).ToWString();
break;
case INT64_V:
RefInt64(r) = d.Get();
break;
case INT_V:
RefInt(r) = d.Get();
break;
case DOUBLE_V:
RefDouble(r) = d.Get();
break;
case BOOL_V:
default:
ASSERT(false);
break;
}
}
}
void FBDate::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
Date d;
switch (v.GetType())
{
case INT_V:
d.Set(ValueTo<int>(v));
break;
case DOUBLE_V:
d.Set((int)ValueTo<double>(v));
case STRING_V:
d = UPP::Scan(DATE_V, ValueTo<String>(v));
break;
case DATE_V:
d = ValueTo<Date>(v);
break;
case TIME_V:
d = ValueTo<Time>(v);
break;
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
d = UPP::Scan(DATE_V, ValueTo<WString>(v).ToString());
break;
case INT64_V:
d.Set((int)ValueTo<int64>(v));
break;
case BOOL_V:
default:
ASSERT(false);
break;
}
struct tm time;
time.tm_year = d.year - 1900;
time.tm_mon = d.month - 1;
time.tm_mday = d.day;
time.tm_hour = 0;
time.tm_min = 0;
time.tm_sec = 0;
time.tm_isdst = -1;
dll.isc_encode_sql_date(&time, &value);
}
struct FBBlob : public FBValue {
mutable ibpp::SegmentBlob value;
FBBlob(FBSession& s, XSQLVAR& v);
long GetLength() const
{
return value.getInfo().getLenght();
}
String GetEntire() const;
void SetEntire(const char* data_ptr, int data_len);
virtual void GetValue(Ref r) const;
virtual void SetValue(const Value& v);
};
FBBlob::FBBlob(FBSession& s, XSQLVAR& v)
: FBValue(v)
, value(s.db, s.tr, s.dll, s.ib_error)
{
v.sqltype = SQL_BLOB + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value.getID());
v.sqllen = sizeof(GDS_QUAD);
}
String FBBlob::GetEntire() const
{
// Max value is 2 >> 16 ...
const unsigned short db_segment_size = 0xFFFF;
String data;
unsigned short actualSize;
char buff[db_segment_size];
value.open();
while (value.getSegment(actualSize, sizeof(buff), buff))
data.Cat(buff, actualSize);
value.close();
return data;
}
void FBBlob::SetEntire(const char* data_ptr, int data_len)
{
value.create();
value.put_segment(data_ptr, data_len);
value.close();
}
void FBBlob::GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
unsigned short actualSize;
value.open();
switch(r.GetType()) {
case INT_V:
{
int v;
if (value.getSegment(actualSize, sizeof(v), reinterpret_cast<char*>(&v)))
RefInt(r) = v;
}
break;
case DOUBLE_V:
{
double v;
if (value.getSegment(actualSize, sizeof(v), reinterpret_cast<char*>(&v)))
RefDouble(r) = v;
}
break;
case STRING_V:
RefString(r) = GetEntire();
break;
case DATE_V:
r.SetValue(UPP::Scan(DATE_V, GetEntire()));
break;
case TIME_V:
r.SetValue(UPP::Scan(TIME_V, GetEntire()));
break;
case VALUE_V:
r.SetValue(GetEntire());
break;
case WSTRING_V:
RefWString(r) = GetEntire().ToWString();
break;
case INT64_V:
{
int64 v;
if (value.getSegment(actualSize, sizeof(v), reinterpret_cast<char*>(&v)))
RefInt64(r) = v;
}
break;
case BOOL_V:
default:
ASSERT(false);
break;
}
value.close();
}
}
void FBBlob::SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
const LanguageInfo& li = GetLanguageInfo();
switch (v.GetType())
{
case INT_V:
SetEntire(reinterpret_cast<const char*>(&ValueTo<int>(v)), sizeof(int));
break;
case DOUBLE_V:
SetEntire(reinterpret_cast<const char*>(&ValueTo<double>(v)), sizeof(double));
break;
case STRING_V:
{
String s = ValueTo<String>(v);
SetEntire(~s, s.GetCount());
}
break;
case DATE_V:
{
String s = li.FormatDate(ValueTo<Date>(v));
SetEntire(~s, s.GetCount());
}
break;
case TIME_V:
{
String s = li.FormatTime(ValueTo<Time>(v));
SetEntire(~s, s.GetCount());
}
break;
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
{
String s = ValueTo<WString>(v).ToString();
SetEntire(~s, s.GetCount());
}
break;
case INT64_V:
SetEntire(reinterpret_cast<const char*>(&ValueTo<int64>(v)), sizeof(int64));
break;
case BOOL_V:
default:
ASSERT(false);
break;
}
}
struct FBArray : public FBValue {
ISC_QUAD value;
FBArray(XSQLVAR& v) : FBValue(v)
{
v.sqltype = SQL_ARRAY + (v.sqltype & 1);
v.sqldata = reinterpret_cast<ISC_SCHAR*>(&value);
v.sqllen = sizeof(value);
}
virtual void GetValue(Ref r) const
{
if (IsNull())
r = Null;
else {
switch(r.GetType()) {
// case INT_V:
// {
// const char* e = buff.vary_string + buff.vary_length;
// RefInt(r) = ScanInt(buff.vary_string, &e);
// }
// break;
// case DOUBLE_V:
// {
// const char* e = buff.vary_string + buff.vary_length;
// RefDouble(r) = ScanDouble(buff.vary_string, &e);
// }
// break;
// case STRING_V:
// RefString(r) = String(buff.vary_string, buff.vary_length);
// break;
// case DATE_V:
// r.SetValue(UPP::Scan(DATE_V, String(buff.vary_string, buff.vary_length)));
// break;
// case TIME_V:
// r.SetValue(UPP::Scan(TIME_V, String(buff.vary_string, buff.vary_length)));
// break;
// case VALUE_V:
// r.SetValue(String(buff.vary_string, buff.vary_length));
// break;
// case WSTRING_V:
// RefWString(r) = WString(buff.vary_string, buff.vary_length);
// break;
// case INT64_V:
// {
// const char* e = buff.vary_string + buff.vary_length;
// RefInt64(r) = ScanInt64(buff.vary_string, &e);
// }
// break;
case BOOL_V:
default:
ASSERT(false);
break;
}
}
}
virtual void SetValue(const Value& v)
{
SetNull(v.IsNull());
if (v.IsNull())
return;
switch (v.GetType())
{
case INT_V:
break;
case DOUBLE_V:
case STRING_V:
case DATE_V:
case TIME_V:
case VALUE_V:
SetValue(ValueTo<Value>(v));
break;
case WSTRING_V:
case INT64_V:
case BOOL_V:
default:
ASSERT(false);
break;
}
}
};
class FBConnection : public SqlConnection {
public:
enum StatementType
{
stNone,
stSelect,
stSelect4Update,
stInsert,
stDelete,
stUpdate,
stDDL,
stTrBegin,
stTrCommit,
stTrRollback,
// stCreate,
// stCreateDB,
// stDrop,
// stAlter,
// stFunction,
// stSet,
// stSetTransaction
};
public:
FBConnection(FBSession& s);
protected:
virtual void Cancel();
virtual bool Execute();
virtual bool Fetch();
virtual void GetColumn(int i, Ref r) const;
// virtual Value GetInsertedId() const;
// virtual int GetRowsProcessed() const;
virtual SqlSession& GetSession() const;
virtual String GetUser() const;
virtual void SetParam(int i, const Value& r);
virtual String ToString() const;
protected:
bool IsDML() const { return st == stInsert || st == stDelete || st == stUpdate; }
// bool IsDDL() const { return st == stCreate || st == stDrop || st == stAlter; }
bool IsDDL() const { return st == stDDL; }
bool IsSelect() const { return st == stSelect || st == stSelect4Update; }
StatementType GetST() const { return st; }
void Prepare();
void CloseCursor();
void SetupConverters(ibpp::SQLDataArray& from, Array<FBValue>& to) const;
protected:
bool executed_:1;
StatementType st;
int bindNum;
int defineNum;
FBSession& session;
ibpp::DynamicSQL dSQL_;
Array<FBValue> defineBind;
Array<FBValue> bindBind;
};
FBConnection::FBConnection(FBSession& s)
: executed_(false)
, st(stNone)
, session(s)
, dSQL_(s.db, s.tr, s.dll, s.ib_error)
{
}
String FBConnection::GetUser() const
{
Sql stmt(session);
String result;
Ref rResult(result);
stmt.SetStatement(
"SELECT current_user FROM rdb$database"
);
stmt.RunX();
if (stmt.Fetch())
stmt.GetColumn(0, rResult);
return result;
}
void FBConnection::SetParam(int i, const Value& v)
{
Prepare();
bindBind[i].SetValue(v);
}
void FBConnection::SetupConverters(ibpp::SQLDataArray& from, Array<FBValue>& to) const
{
const int size = from.getSize();
for (int i = 0; i < size; ++i)
{
XSQLVAR& var = from[i];
switch (var.sqltype & ~1)
{
case SQL_TEXT:
case SQL_VARYING:
to.Add(new FBVarying(var));
break;
case SQL_SHORT:
case SQL_LONG:
if (var.sqlscale < 0)
to.Add(new FBDouble(var));
else
to.Add(new FBInt(var));
break;
case SQL_INT64:
if (var.sqlscale < 0)
to.Add(new FBDouble(var));
else
to.Add(new FBInt64(var));
break;
case SQL_FLOAT:
case SQL_D_FLOAT:
case SQL_DOUBLE:
to.Add(new FBDouble(var));
break;
case SQL_DATE:
case SQL_TYPE_TIME:
to.Add(new FBTimeStamp(var, session.dll));
break;
case SQL_TYPE_DATE:
to.Add(new FBDate(var, session.dll));
break;
case SQL_BLOB:
to.Add(new FBBlob(session, var));
break;
case SQL_ARRAY:
to.Add(new FBArray(var));
break;
// break;
// case SQL_QUAD:
// break;
default:
ASSERT(false);
break;
}
}
}
void FBConnection::Prepare()
{
if (parse)
{
CloseCursor();
// Special case. Statement should not be prepared in case of
// "CREATE DATABASE" and "SET TRANSACTION"
dSQL_.prepare(statement, session.tmpDataArray);
const int stmt_type = dSQL_.getStmtType();
switch(stmt_type)
{
case isc_info_sql_stmt_select:
st = stSelect;
break;
case isc_info_sql_stmt_insert:
st = stInsert;
break;
case isc_info_sql_stmt_update:
st = stUpdate;
break;
case isc_info_sql_stmt_delete:
st = stDelete;
break;
case isc_info_sql_stmt_ddl:
st = stDDL;
break;
// case isc_info_sql_stmt_get_segment:
// st = ???;
// break;
// case isc_info_sql_stmt_put_segment:
// st = ???;
// break;
// case isc_info_sql_stmt_exec_procedure:
// st = ???;
// break;
case isc_info_sql_stmt_start_trans:
st = stTrBegin;
break;
case isc_info_sql_stmt_commit:
st = stTrCommit;
break;
case isc_info_sql_stmt_rollback:
st = stTrRollback;
break;
case isc_info_sql_stmt_select_for_upd:
st = stSelect4Update;
break;
// case isc_info_sql_stmt_set_generator:
// st = ???;
// break;
// case isc_info_sql_stmt_savepoint:
// st = ???;
// break;
default:
break;
}
executed_ = false;
defineBind.Clear();
defineNum = session.tmpDataArray.getRequredSize();
defineBind.Reserve(defineNum);
ibpp::SQLDataArray& out = dSQL_.GetOut();
out.setSize(defineNum);
if (defineNum > 0)
{
dSQL_.describe_define(out);
SetupConverters(out, defineBind);
}
dSQL_.describe_bind(session.tmpDataArray);
bindNum = session.tmpDataArray.getRequredSize();
bindBind.Clear();
bindBind.Reserve(bindNum);
ibpp::SQLDataArray& in = dSQL_.GetIn();
in.setSize(bindNum);
if (bindNum)
{
dSQL_.describe_bind(in);
SetupConverters(in, bindBind);
}
parse = false;
}
}
void FBConnection::CloseCursor()
{
if (executed_ && IsSelect() && !session.CursorIsClosed())
{
dSQL_.close_cursor();
session.SetCursorIsClosed();
}
}
bool FBConnection::Execute()
{
try {
Prepare();
// Special case. Statement should not be prepared in case of
// "CREATE DATABASE" and "SET TRANSACTION"
// if (GetST() == stCreateDB || GetST() == stSetTransaction)
// dSQL_.execute_immediate_no_trans(statement);
// else
if (IsDDL()) {
// Rollback transaction and start another.
// This seems to be a default behaviour with other databases.
session.RollbackRetaining();
TRANSACTION_RETAIN(session) {
dSQL_.execute();
}
} else if (IsSelect()) {
CloseCursor();
dSQL_.execute();
executed_ = true;
} else {
// For EXECUTE PROCEDURE, for example ...
if(session.IsExplicitTrans())
dSQL_.execute2();
else {
// Commit/Rollback implicitly.
TRANSACTION_RETAIN(session) {
dSQL_.execute2();
}
}
}
} catch(ibpp::DbExc& e) {
session.SetError(~e, statement, e.GetErrNum());
return false;
}
return true;
}
bool FBConnection::Fetch()
{
ASSERT(executed_);
const bool rc = dSQL_.fetch();
session.SetCursorIsClosed(false);
return rc;
}
void FBConnection::GetColumn(int i, Ref r) const
{
defineBind[i].GetValue(r);
}
void FBConnection::Cancel()
{
}
String FBConnection::ToString() const
{
return String();
}
SqlSession& FBConnection::GetSession() const
{
return session;
}
FBSession::FBSession(const String& dllName)
: SvcConnected(false)
, DbConnected(false)
, TrStarted(false)
, CursIsClosed(true)
, TrExplicit(false)
, tmpDataArray(1)
, dll(FB())
, ib_error(dll)
, svc(dll, ib_error)
, db(dll, ib_error)
, tr(dll, ib_error)
{
if (!dllName.IsEmpty())
dll.SetLibName(dllName);
}
FBSession::~FBSession()
{
if (TrExplicit)
RollbackInternal();
else
CommitInternal();
if (DbConnected)
db.dettach();
if (SvcConnected)
svc.detach();
}
void FBSession::Connect(
const char* dbname,
const char* host,
const char* user,
const char* pswd,
const String& dllName,
ibpp::network_protocol_t protocol
)
{
if (!dllName.IsEmpty())
dll.SetLibName(dllName);
if (!dll.Load())
throw ibpp::DbExc(dll.GetLibName() + " wasn't found.");
// Service
svc.attach(host, user, pswd, protocol);
SvcConnected = true;
// Database
db.setUserName(user);
db.setPassword(pswd);
db.attach(dbname);
DbConnected = true;
DbName = dbname;
// Transaction
tr.setVersion3();
tr.setReadWrite();
tr.setReadCommitted();
tr.setRecVersion();
BeginInternal();
}
SqlConnection * FBSession::CreateConnection()
{
return new FBConnection(*this);
}
Vector<SqlColumnInfo> FBSession::EnumColumns(String /*database*/, String table)
{
Vector<SqlColumnInfo> result;
Sql stmt(*this);
String name;
int null_flag;
int field_type;
int field_width;
int field_precision;
int field_scale;
Ref rName(name);
Ref rNullFlag(null_flag);
Ref rFieldType(field_type);
Ref rFieldWidth(field_width);
Ref rFieldPrecision(field_precision);
Ref rFieldScale(field_scale);
stmt.SetStatement(
" SELECT f.rdb$field_name, \n"
" f.rdb$field_position, \n"
" f.rdb$null_flag, \n"
" fs.rdb$field_type, \n"
" fs.rdb$field_length, \n"
" fs.rdb$field_precision, \n"
" fs.rdb$field_scale, \n"
" fs.rdb$field_sub_type, \n"
" fs.rdb$computed_source, \n"
" fs.rdb$character_length \n"
" FROM rdb$relation_fields f \n"
" LEFT JOIN rdb$fields fs ON fs.rdb$field_name = f.rdb$field_source \n"
" WHERE f.rdb$relation_name = ? \n"
" ORDER BY f.rdb$field_position"
);
stmt.SetParam(0, table);
stmt.RunX();
while (stmt.Fetch())
{
stmt.GetColumn(0, rName);
stmt.GetColumn(2, rNullFlag);
stmt.GetColumn(3, rFieldType);
stmt.GetColumn(4, rFieldWidth);
stmt.GetColumn(5, rFieldPrecision);
stmt.GetColumn(6, rFieldScale);
SqlColumnInfo& ci = result.Add();
ci.name = name;
ci.type = field_type;
ci.width = field_width;
ci.precision = field_precision;
ci.scale = field_scale;
ci.nullable = rNullFlag != 1;
}
return result;
}
Vector<String> FBSession::EnumDatabases()
{
Vector<String> result;
result.Add(DbName);
return result;
}
Vector<String> FBSession::EnumPrimaryKey(String /*database*/, String table)
{
Vector<String> result;
Sql stmt(*this);
String name;
Ref rName(name);
stmt.SetStatement(
" SELECT i.rdb$field_name, \n"
" i.rdb$field_position \n"
" FROM rdb$relation_constraints rc, rdb$index_segments i \n"
" WHERE (i.rdb$index_name = rc.rdb$index_name) \n"
" AND (rc.rdb$constraint_type = 'PRIMARY KEY') \n"
" AND (rc.rdb$relation_name = ?) \n"
" ORDER BY i.rdb$field_position"
);
stmt.SetParam(0, table);
stmt.RunX();
while (stmt.Fetch())
{
stmt.GetColumn(0, rName);
result.Add(name);
}
return result;
}
namespace {
static const char *rwlist[] = {
"ACTION", "ACTIVE", "ADD",
"ADMIN", "AFTER", "ALL",
"ALTER", "AND", "ANY",
"AS", "ASC", "ASCENDING",
"AT", "AUTO", "AUTODDL",
"AVG", "BASED", "BASENAME",
"BASE_NAME", "BEFORE", "BEGIN",
"BETWEEN", "BLOB", "BLOBEDIT",
"BUFFER", "BY", "CACHE",
"CASCADE", "CAST", "CHAR",
"CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH",
"CHECK", "CHECK_POINT_LEN", "CHECK_POINT_LENGTH",
"COLLATE", "COLLATION", "COLUMN",
"COMMIT", "COMMITTED", "COMPILETIME",
"COMPUTED", "CLOSE", "CONDITIONAL",
"CONNECT", "CONSTRAINT", "CONTAINING",
"CONTINUE", "COUNT", "CREATE",
"CSTRING", "CURRENT", "CURRENT_DATE",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURSOR",
"DATABASE", "DATE", "DAY",
"DB_KEY", "DEBUG", "DEC",
"DECIMAL", "DECLARE", "DEFAULT",
"DELETE", "DESC", "DESCENDING",
"DESCRIBE", "DESCRIPTOR", "DISCONNECT",
"DISPLAY", "DISTINCT", "DO",
"DOMAIN", "DOUBLE", "DROP",
"ECHO", "EDIT", "ELSE",
"END", "ENTRY_POINT", "ESCAPE",
"EVENT", "EXCEPTION", "EXECUTE",
"EXISTS", "EXIT", "EXTERN",
"EXTERNAL", "EXTRACT", "FETCH",
"FILE", "FILTER", "FLOAT",
"FOR", "FOREIGN", "FOUND",
"FREE_IT", "FROM", "FULL",
"FUNCTION", "GDSCODE", "GENERATOR",
"GEN_ID", "GLOBAL", "GOTO",
"GRANT", "GROUP", "GROUP_COMMIT_WAIT",
"GROUP_COMMIT_", "WAIT_TIME", "HAVING",
"HELP", "HOUR", "IF",
"IMMEDIATE", "IN", "INACTIVE",
"INDEX", "INDICATOR", "INIT",
"INNER", "INPUT", "INPUT_TYPE",
"INSERT", "INT", "INTEGER",
"INTO", "IS", "ISOLATION",
"ISQL", "JOIN", "KEY",
"LC_MESSAGES", "LC_TYPE", "LEFT",
"LENGTH", "LEV", "LEVEL",
"LIKE", "LOGFILE", "LOG_BUFFER_SIZE",
"LOG_BUF_SIZE", "LONG", "MANUAL",
"MAX", "MAXIMUM", "MAXIMUM_SEGMENT",
"MAX_SEGMENT", "MERGE", "MESSAGE",
"MIN", "MINIMUM", "MINUTE",
"MODULE_NAME", "MONTH", "NAMES",
"NATIONAL", "NATURAL", "NCHAR",
"NO", "NOAUTO", "NOT",
"NULL", "NUMERIC", "NUM_LOG_BUFS",
"NUM_LOG_BUFFERS", "OCTET_LENGTH", "OF",
"ON", "ONLY", "OPEN",
"OPTION", "OR", "ORDER",
"OUTER", "OUTPUT", "OUTPUT_TYPE",
"OVERFLOW", "PAGE", "PAGELENGTH",
"PAGES", "PAGE_SIZE", "PARAMETER",
"PASSWORD", "PLAN", "POSITION",
"POST_EVENT", "PRECISION", "PREPARE",
"PROCEDURE", "PROTECTED", "PRIMARY",
"PRIVILEGES", "PUBLIC", "QUIT",
"RAW_PARTITIONS", "RDB$DB_KEY", "READ",
"REAL", "RECORD_VERSION", "REFERENCES",
"RELEASE", "RESERV", "RESERVING",
"RESTRICT", "RETAIN", "RETURN",
"RETURNING_VALUES", "RETURNS", "REVOKE",
"RIGHT", "ROLE", "ROLLBACK",
"RUNTIME", "SCHEMA", "SECOND",
"SEGMENT", "SELECT", "SET",
"SHADOW", "SHARED", "SHELL",
"SHOW", "SINGULAR", "SIZE",
"SMALLINT", "SNAPSHOT", "SOME",
"SORT", "SQLCODE", "SQLERROR",
"SQLWARNING", "STABILITY", "STARTING",
"STARTS", "STATEMENT", "STATIC",
"STATISTICS", "SUB_TYPE", "SUM",
"SUSPEND", "TABLE", "TERMINATOR",
"THEN", "TIME", "TIMESTAMP",
"TO", "TRANSACTION", "TRANSLATE",
"TRANSLATION", "TRIGGER", "TRIM",
"TYPE", "UNCOMMITTED", "UNION",
"UNIQUE", "UPDATE", "UPPER",
"USER", "USING", "VALUE",
"VALUES", "VARCHAR", "VARIABLE",
"VARYING", "VERSION", "VIEW",
"WAIT", "WEEKDAY", "WHEN",
"WHENEVER", "WHERE", "WHILE",
"WITH", "WORK", "WRITE",
"YEAR", "YEARDAY",
};
}
Vector<String> FBSession::EnumReservedWords()
{
Vector<String> result;
result.SetCount(__countof(rwlist));
for(int i = 0; i < __countof(rwlist); i++)
result[i] = rwlist[i];
return result;
}
Vector<String> FBSession::EnumSequences(String /*database*/)
{
Vector<String> result;
Sql stmt(*this);
String name;
Ref rName(name);
stmt.SetStatement(
"SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS WHERE (RDB$SYSTEM_FLAG is NULL) or (RDB$SYSTEM_FLAG <> 1)"
);
stmt.RunX();
while (stmt.Fetch())
{
stmt.GetColumn(0, rName);
result.Add(name);
}
return result;
}
Vector<String> FBSession::EnumTables(String /*database*/)
{
Vector<String> result;
Sql stmt(*this);
String name;
Ref rName(name);
stmt.SetStatement(
"SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE (RDB$VIEW_BLR IS NULL) AND (RDB$SYSTEM_FLAG = 0)"
);
stmt.RunX();
while (stmt.Fetch())
{
stmt.GetColumn(0, rName);
result.Add(name);
}
return result;
}
Vector<String> FBSession::EnumUsers()
{
using namespace ibpp;
using namespace ibpp::svc_cmd;
Vector<String> result;
get_users users(svc);
users.execute();
const Vector<user_info>& ui = users.get_info();
for(int i = 0; i < ui.GetCount(); ++i)
result.Add(ui[i].user_name);
return result;
}
Vector<String> FBSession::EnumViews(String /*database*/)
{
Vector<String> result;
Sql stmt(*this);
String name;
Ref rName(name);
stmt.SetStatement(
"SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE NOT (RDB$VIEW_BLR IS NULL)"
);
stmt.RunX();
while (stmt.Fetch())
{
stmt.GetColumn(0, rName);
result.Add(name);
}
return result;
}
void FBSession::RollbackTo(const String& savepoint)
{
Sql stmt(*this);
stmt.SetStatement(
"ROLLBACK TO SAVEPOINT " + savepoint
);
stmt.RunX();
}
String FBSession::Savepoint()
{
static dword i;
i = (i + 1) & 0x8fffffff;
String s = Sprintf("SESSION_SAVEPOINT_%d", i);
Sql stmt(*this);
stmt.SetStatement(
"SAVEPOINT " + s
);
stmt.RunX();
return s;
}
inline
static bool IsIdentChar(int s)
{
return IsAlNum(s) || s == '_';
}
inline
static void SkipSpaces(const char*& s)
{
while (IsSpace(*s)) ++s;
}
class BackTrack
{
public:
BackTrack(const char*& s) : Pos(s), BtPos(s) {}
bool Check(bool v) const
{
if (!v)
Pos = BtPos;
return v;
}
private:
const char*& Pos;
const char* BtPos;
};
static bool scmpw(const char*& s, const char *w)
{
BackTrack bt(s);
for(;;) {
if(*w == '\0')
return bt.Check(!IsIdentChar(*s));
if(ToUpper(*s++) != ToUpper(*w++))
return bt.Check(false);
}
}
static bool SkipOrAlter(const char*& s)
{
SkipSpaces(s);
if (scmpw(s, "or"))
{
SkipSpaces(s);
scmpw(s, "alter");
}
return true;
}
bool FirebirdPerformScript(const String& text, StatementExecutor& exe, Gate2<int, int> progress_canceled)
{
String statement;
String term(";");
String new_term;
const int size = text.GetCount();
const char* s = text;
bool found;
do {
found = false;
bool found_block = true;
bool prev_ident_char = false;
bool execute = false;
int level = 0;
const char* b = s;
while (*s) {
// Multi-line comment.
if (*s == '/' && *(s+1) == '*') {
s += 2;
while (*s && (*s++ != '*' || *s != '/'));
if (*s) ++s;
}
// One-line comment.
else if (*s == '-' && *(s+1) == '-') {
s += 2;
while (*s && *s != '\n') ++s;
if (*s) ++s;
}
// String.
else if (*s == '\'') {
while(*++s && (*s != '\'' || *++s == '\''));
if (*s) ++s;
}
else if (scmpw(s, term) && level == 0) {
if (!new_term.IsEmpty()) {
term = new_term;
new_term = Null;
found = true;
// Statement starts from here.
b = s;
break;
}
else if (found_block) {
statement.Cat(b, s);
found = true;
execute = true;
break;
}
}
else if (!prev_ident_char && level == 0 && scmpw(s, "set")) {
SkipSpaces(s);
if (scmpw(s, "term"))
{
SkipSpaces(s);
const char* b = s;
while (!IsSpace(*s)) ++s;
new_term.Cat(b, s);
}
}
else if (!prev_ident_char && scmpw(s, "begin")) {
found_block = true;
++level;
}
else if (!prev_ident_char && scmpw(s, "end")) {
--level;
}
else if (!prev_ident_char && scmpw(s, "alter") || scmpw(s, "recreate") || (scmpw(s, "create") && SkipOrAlter(s)))
{
SkipSpaces(s);
if (scmpw(s, "procedure") || scmpw(s, "trigger"))
found_block = false;
}
else
{
prev_ident_char = IsIdentChar(*s);
++s;
}
}
if (found && execute)
{
if(!exe.Execute(statement)) return false;
statement = Null;
}
} while (found);
return true;
}
RunScript FBSession::GetRunScript() const
{
return &FirebirdPerformScript;
}
void FBSession::Begin()
{
BeginInternal();
TrExplicit = true;
}
void FBSession::Commit()
{
CommitRetaining();
TrExplicit = false;
}
void FBSession::Rollback()
{
RollbackRetaining();
TrExplicit = false;
}
void FBSession::CommitRetaining()
{
if (TrStarted)
{
tr.commit_retaining();
SetCursorIsClosed(); // By transaction.
}
}
void FBSession::RollbackRetaining()
{
if (TrStarted)
{
tr.rollback_retaining();
SetCursorIsClosed(); // By transaction.
}
}
void FBSession::BeginInternal()
{
if (!TrStarted)
{
tr.start(db);
TrStarted = true;
}
}
void FBSession::CommitInternal()
{
if (TrStarted)
{
tr.commit();
TrStarted = false;
SetCursorIsClosed(); // By transaction.
}
}
void FBSession::RollbackInternal()
{
if (TrStarted)
{
tr.rollback();
TrStarted = false;
SetCursorIsClosed(); // By transaction.
}
}
bool FBSession::IsOpen() const
{
return DbConnected;
}
Value FBSequence::Get() {
static SqlId ONE_RECORD_TABLE("RDB$DATABASE");
#ifndef NOAPPSQL
Sql sql(session ? *session : SQL.GetSession());
#else
ASSERT(session);
Sql sql(*session);
#endif
sql.SetStatement(
"SELECT GEN_ID(" + seq.ToString() + ", 1) FROM RDB$DATABASE"
);
if (sql.Run() && sql.Fetch())
return sql[0];
return ErrorValue();
}
END_UPP_NAMESPACE