8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-29 06:43:03 +01:00
firebird-mirror/src/jrd/extds/IscDS.cpp

1662 lines
41 KiB
C++

/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Vlad Khorsun
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2007 Vlad Khorsun <hvlad@users.sourceforge.net>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "fb_types.h"
#include "../common/common.h"
#include "../../include/fb_blk.h"
#include "fb_exception.h"
#include "iberror.h"
#include "../align.h"
#include "../common/dsc.h"
#include "../exe.h"
#include "IscDS.h"
#include "../tra.h"
#include "../blb_proto.h"
#include "../evl_proto.h"
#include "../exe_proto.h"
#include "../intl_proto.h"
#include "../mov_proto.h"
using namespace Jrd;
using namespace Firebird;
namespace EDS
{
const char* FIREBIRD_PROVIDER_NAME = "Firebird";
class RegisterFBProvider
{
public:
RegisterFBProvider()
{
Provider* provider = new FBProvider(FIREBIRD_PROVIDER_NAME);
Manager::addProvider(provider);
}
};
static RegisterFBProvider reg;
static bool isConnectionBrokenError(ISC_STATUS status);
static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array<dsc>& descs);
static UCHAR sqlTypeToDscType(SSHORT sqlType);
// IscProvider
void IscProvider::getRemoteError(const ISC_STATUS* status, string& err) const
{
err = "";
// We can't use safe fb_interpret here as we have no idea what implementation
// of ISC API is used by current provider. We can test for existence of
// fb_interpret and use it if present, but I don't want to complicate code.
// So, buffer should be big enough to please old isc_interprete.
// Probably in next version we should use fb_interpret only.
char buff[1024];
const ISC_STATUS* p = status;
const ISC_STATUS* const end = status + ISC_STATUS_LENGTH;
while (p < end)
{
const ISC_STATUS code = *p ? p[1] : 0;
if (!m_api.isc_interprete(buff, &p))
break;
string rem_err;
rem_err.printf("%lu : %s\n", code, buff);
err += rem_err;
}
}
Connection* IscProvider::doCreateConnection()
{
return new IscConnection(*this);
}
// IscConnection
IscConnection::IscConnection(IscProvider& prov) :
Connection(prov),
m_iscProvider(prov),
m_handle(0)
{
}
IscConnection::~IscConnection()
{
}
void IscConnection::attach(thread_db* tdbb, const string& dbName, const string& user,
const string& pwd, const string& role)
{
m_dbName = dbName;
generateDPB(tdbb, m_dpb, user, pwd, role);
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, *this);
m_iscProvider.isc_attach_database(status, m_dbName.length(), m_dbName.c_str(),
&m_handle, m_dpb.getBufferLength(),
reinterpret_cast<const char*>(m_dpb.getBuffer()));
}
if (status[1]) {
raise(status, tdbb, "attach");
}
char buff[16];
{
EngineCallbackGuard guard(tdbb, *this);
const char info[] = {isc_info_db_sql_dialect, isc_info_end};
m_iscProvider.isc_database_info(status, &m_handle, sizeof(info), info, sizeof(buff), buff);
}
if (status[1]) {
raise(status, tdbb, "isc_database_info");
}
const char* p = buff, *end = buff + sizeof(buff);
while (p < end)
{
const UCHAR item = *p++;
const USHORT len = m_iscProvider.isc_vax_integer(p, sizeof(USHORT));
p += sizeof(USHORT);
switch (item)
{
case isc_info_db_sql_dialect:
m_sqlDialect = m_iscProvider.isc_vax_integer(p, len);
break;
case isc_info_error:
if (*p == isc_info_db_sql_dialect)
{
const ULONG err = m_iscProvider.isc_vax_integer(p + 1, len - 1);
if (err == isc_infunk)
{
// Remote server don't understand isc_info_db_sql_dialect.
// Consider it as pre-IB6 server and use SQL dialect 1 to work with it.
m_sqlDialect = 1;
break;
}
}
// fall thru
case isc_info_truncated:
ERR_post(Arg::Gds(isc_random) << Arg::Str("Unexpected error in isc_database_info"));
case isc_info_end:
p = end;
break;
}
p += len;
}
}
void IscConnection::doDetach(thread_db* tdbb)
{
ISC_STATUS_ARRAY status = {0};
if (m_handle)
{
EngineCallbackGuard guard(tdbb, *this);
FB_API_HANDLE h = m_handle;
m_handle = 0;
m_iscProvider.isc_detach_database(status, &h);
m_handle = h;
}
if (status[1] && !isConnectionBrokenError(status[1]))
raise(status, tdbb, "detach");
}
bool IscConnection::cancelExecution(thread_db* /*tdbb*/)
{
ISC_STATUS_ARRAY status = {0, 0, 0};
if (m_handle)
{
m_iscProvider.fb_cancel_operation(status, &m_handle, fb_cancel_raise);
if (m_handle && status[1] == isc_wish_list)
{
fb_utils::init_status(status);
m_iscProvider.fb_cancel_operation(status, &m_handle, fb_cancel_abort);
}
}
return (status[1] == 0);
}
// this ISC connection instance is available for the current execution context if it
// a) has no active statements or supports many active statements
// and
// b) has no active transactions or has active transaction of given
// TraScope bound to current jrd transaction or supports many active
// transactions
bool IscConnection::isAvailable(thread_db* tdbb, TraScope traScope) const
{
const int flags = m_provider.getFlags();
if (m_used_stmts && !(flags & prvMultyStmts))
return false;
if (m_transactions.getCount() && !(flags & prvMultyTrans) && !findTransaction(tdbb, traScope))
{
return false;
}
return true;
}
Blob* IscConnection::createBlob()
{
return new IscBlob(*this);
}
Transaction* IscConnection::doCreateTransaction()
{
return new IscTransaction(*this);
}
Statement* IscConnection::doCreateStatement()
{
return new IscStatement(*this);
}
// IscTransaction
void IscTransaction::doStart(ISC_STATUS* status, thread_db* tdbb, Firebird::ClumpletWriter& tpb)
{
fb_assert(!m_handle);
FB_API_HANDLE& db_handle = m_iscConnection.getAPIHandle();
EngineCallbackGuard guard(tdbb, *this);
m_iscProvider.isc_start_transaction(status, &m_handle, 1, &db_handle,
tpb.getBufferLength(), tpb.getBuffer());
}
void IscTransaction::doPrepare(ISC_STATUS* /*status*/, thread_db* /*tdbb*/, int /*info_len*/, const char* /*info*/)
{
}
void IscTransaction::doCommit(ISC_STATUS* status, thread_db* tdbb, bool retain)
{
EngineCallbackGuard guard(tdbb, *this);
if (retain)
m_iscProvider.isc_commit_retaining(status, &m_handle);
else
m_iscProvider.isc_commit_transaction(status, &m_handle);
fb_assert(retain && m_handle || !retain && !m_handle || status[1] && m_handle);
}
void IscTransaction::doRollback(ISC_STATUS* status, thread_db* tdbb, bool retain)
{
EngineCallbackGuard guard(tdbb, *this);
if (retain)
m_iscProvider.isc_rollback_retaining(status, &m_handle);
else
m_iscProvider.isc_rollback_transaction(status, &m_handle);
if (status[1] && isConnectionBrokenError(status[1]) && !retain)
{
m_handle = 0;
fb_utils::init_status(status);
}
fb_assert(retain && m_handle || !retain && !m_handle || status[1] && m_handle);
}
// IscStatement
IscStatement::IscStatement(IscConnection& conn) :
Statement(conn),
m_iscProvider(*(IscProvider*) conn.getProvider()),
m_iscConnection(conn),
m_handle(0),
m_in_xsqlda(NULL),
m_out_xsqlda(NULL)
{
}
IscStatement::~IscStatement()
{
delete[] (char*) m_in_xsqlda;
delete[] (char*) m_out_xsqlda;
}
void IscStatement::doPrepare(thread_db* tdbb, const string& sql)
{
FB_API_HANDLE& h_conn = m_iscConnection.getAPIHandle();
FB_API_HANDLE& h_tran = getIscTransaction()->getAPIHandle();
ISC_STATUS_ARRAY status = {0};
// prepare and get output parameters
if (!m_out_xsqlda)
{
m_out_xsqlda = (XSQLDA*) FB_NEW (getPool()) char [XSQLDA_LENGTH(1)];
m_out_xsqlda->sqln = 1;
m_out_xsqlda->version = 1;
}
const char* sWhereError = NULL;
{
EngineCallbackGuard guard(tdbb, *this);
if (!m_handle)
{
fb_assert(!m_allocated);
if (m_iscProvider.isc_dsql_allocate_statement(status, &h_conn, &m_handle)) {
sWhereError = "isc_dsql_allocate_statement";
}
m_allocated = (m_handle != 0);
}
if (!sWhereError) {
if (m_iscProvider.isc_dsql_prepare(status, &h_tran, &m_handle, sql.length(),
sql.c_str(), m_connection.getSqlDialect(), m_out_xsqlda))
{
sWhereError = "isc_dsql_prepare";
}
}
}
if (sWhereError) {
raise(status, tdbb, sWhereError, &sql);
}
// adjust output parameters
if (m_out_xsqlda->sqld > m_out_xsqlda->sqln)
{
const int n = m_out_xsqlda->sqld;
delete[] (char*) m_out_xsqlda;
m_out_xsqlda = (XSQLDA*) FB_NEW (getPool()) char [XSQLDA_LENGTH(n)];
m_out_xsqlda->sqln = n;
m_out_xsqlda->version = 1;
EngineCallbackGuard guard(tdbb, *this);
if (m_iscProvider.isc_dsql_describe(status, &m_handle, 1, m_out_xsqlda))
{
sWhereError = "isc_dsql_describe";
}
}
if (sWhereError) {
raise(status, tdbb, sWhereError, &sql);
}
parseSQLDA(m_out_xsqlda, m_out_buffer, m_outDescs);
m_outputs = m_out_xsqlda ? m_out_xsqlda->sqld : 0;
// get input parameters
if (!m_in_xsqlda)
{
m_in_xsqlda = (XSQLDA*) FB_NEW (getPool()) char [XSQLDA_LENGTH(1)];
m_in_xsqlda->sqln = 1;
m_in_xsqlda->version = 1;
}
{
EngineCallbackGuard guard(tdbb, *this);
if (m_iscProvider.isc_dsql_describe_bind(status, &m_handle, 1, m_in_xsqlda))
{
sWhereError = "isc_dsql_describe_bind";
}
}
if (sWhereError) {
raise(status, tdbb, sWhereError, &sql);
}
// adjust input parameters
if (m_in_xsqlda->sqld > m_in_xsqlda->sqln)
{
const int n = m_in_xsqlda->sqld;
delete[] (char*) m_in_xsqlda;
m_in_xsqlda = (XSQLDA*) FB_NEW (getPool()) char [XSQLDA_LENGTH(n)];
m_in_xsqlda->sqln = n;
m_in_xsqlda->version = 1;
EngineCallbackGuard guard(tdbb, *this);
if (m_iscProvider.isc_dsql_describe_bind(status, &m_handle, 1, m_in_xsqlda))
{
sWhereError = "isc_dsql_describe_bind";
}
}
if (sWhereError) {
raise(status, tdbb, sWhereError, &sql);
}
parseSQLDA(m_in_xsqlda, m_in_buffer, m_inDescs);
m_inputs = m_in_xsqlda ? m_in_xsqlda->sqld : 0;
// get statement type
const char stmt_info[] = {isc_info_sql_stmt_type};
char info_buff[16];
{
EngineCallbackGuard guard(tdbb, *this);
if (m_iscProvider.isc_dsql_sql_info(status, &m_handle, sizeof(stmt_info), stmt_info,
sizeof(info_buff), info_buff))
{
sWhereError = "isc_dsql_sql_info";
}
}
if (sWhereError) {
raise(status, tdbb, sWhereError, &sql);
}
if (info_buff[0] != stmt_info[0])
{
ERR_build_status(status, Arg::Gds(isc_random) << "Unknown statement type");
sWhereError = "isc_dsql_sql_info";
raise(status, tdbb, sWhereError, &sql);
}
{
EngineCallbackGuard guard(tdbb, *this);
const int len = m_iscProvider.isc_vax_integer(&info_buff[1], 2);
const int stmt_type = m_iscProvider.isc_vax_integer(&info_buff[3], len);
m_stmt_selectable = (stmt_type == isc_info_sql_stmt_select ||
stmt_type == isc_info_sql_stmt_select_for_upd);
if (stmt_type == isc_info_sql_stmt_start_trans ||
stmt_type == isc_info_sql_stmt_commit ||
stmt_type == isc_info_sql_stmt_rollback)
{
ERR_build_status(status, Arg::Gds(isc_eds_expl_tran_ctrl));
sWhereError = "isc_dsql_prepare";
raise(status, tdbb, sWhereError, &sql);
}
}
}
void IscStatement::doExecute(thread_db* tdbb)
{
FB_API_HANDLE& h_tran = getIscTransaction()->getAPIHandle();
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, *this);
m_iscProvider.isc_dsql_execute2(status, &h_tran, &m_handle, 1, m_in_xsqlda, m_out_xsqlda);
}
if (status[1]) {
raise(status, tdbb, "isc_dsql_execute2");
}
}
void IscStatement::doOpen(thread_db* tdbb)
{
FB_API_HANDLE& h_tran = getIscTransaction()->getAPIHandle();
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, *this);
m_iscProvider.isc_dsql_execute(status, &h_tran, &m_handle, 1, m_in_xsqlda);
}
if (status[1]) {
raise(status, tdbb, "isc_dsql_execute");
}
}
bool IscStatement::doFetch(thread_db* tdbb)
{
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, *this);
const ISC_STATUS res = m_iscProvider.isc_dsql_fetch(status, &m_handle, 1, m_out_xsqlda);
if (res == 100) {
return false;
}
}
if (status[1]) {
raise(status, tdbb, "isc_dsql_fetch");
}
return true;
}
void IscStatement::doClose(thread_db* tdbb, bool drop)
{
fb_assert(m_handle);
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, *this);
m_iscProvider.isc_dsql_free_statement(status, &m_handle, drop ? DSQL_drop : DSQL_close);
m_allocated = (m_handle != 0);
}
if (status[1])
{
// we can do nothing else with this statement after this point
m_allocated = m_handle = 0;
raise(status, tdbb, "isc_dsql_free_statement");
}
}
void IscStatement::doSetInParams(thread_db* tdbb, unsigned int count, const string* const* names,
const NestConst<Jrd::ValueExprNode>* params)
{
Statement::doSetInParams(tdbb, count, names, params);
if (names)
{
XSQLVAR* xVar = m_in_xsqlda->sqlvar;
for (unsigned int i = 0; i < count; i++, xVar++)
{
const int max_len = sizeof(xVar->sqlname);
const int len = MIN(names[i]->length(), max_len - 1);
xVar->sqlname_length = len;
strncpy(xVar->sqlname, names[i]->c_str(), len);
xVar->sqlname[max_len-1] = 0;
}
}
}
// IscBlob
IscBlob::IscBlob(IscConnection& conn) :
Blob(conn),
m_iscProvider(*(IscProvider*) conn.getProvider()),
m_iscConnection(conn),
m_handle(0)
{
memset(&m_blob_id, 0, sizeof(m_blob_id));
}
IscBlob::~IscBlob()
{
fb_assert(!m_handle);
}
void IscBlob::open(thread_db* tdbb, Transaction& tran, const dsc& desc, const UCharBuffer* bpb)
{
fb_assert(!m_handle);
fb_assert(sizeof(m_blob_id) == desc.dsc_length);
FB_API_HANDLE& h_db = m_iscConnection.getAPIHandle();
FB_API_HANDLE& h_tran = ((IscTransaction&) tran).getAPIHandle();
memcpy(&m_blob_id, desc.dsc_address, sizeof(m_blob_id));
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
ISC_USHORT bpb_len = bpb ? bpb->getCount() : 0;
const ISC_UCHAR* bpb_buff = bpb ? bpb->begin() : NULL;
m_iscProvider.isc_open_blob2(status, &h_db, &h_tran, &m_handle, &m_blob_id,
bpb_len, bpb_buff);
}
if (status[1]) {
m_iscConnection.raise(status, tdbb, "isc_open_blob2");
}
fb_assert(m_handle);
}
void IscBlob::create(thread_db* tdbb, Transaction& tran, dsc& desc, const UCharBuffer* bpb)
{
fb_assert(!m_handle);
fb_assert(sizeof(m_blob_id) == desc.dsc_length);
FB_API_HANDLE& h_db = m_iscConnection.getAPIHandle();
FB_API_HANDLE& h_tran = ((IscTransaction&) tran).getAPIHandle();
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
ISC_USHORT bpb_len = bpb ? bpb->getCount() : 0;
const char* bpb_buff = bpb ? reinterpret_cast<const char*>(bpb->begin()) : NULL;
m_iscProvider.isc_create_blob2(status, &h_db, &h_tran, &m_handle, &m_blob_id,
bpb_len, bpb_buff);
memcpy(desc.dsc_address, &m_blob_id, sizeof(m_blob_id));
}
if (status[1]) {
m_iscConnection.raise(status, tdbb, "isc_create_blob2");
}
fb_assert(m_handle);
}
USHORT IscBlob::read(thread_db* tdbb, UCHAR* buff, USHORT len)
{
fb_assert(m_handle);
USHORT result = 0;
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
m_iscProvider.isc_get_segment(status, &m_handle, &result, len, reinterpret_cast<SCHAR*>(buff));
}
switch (status[1])
{
case isc_segstr_eof:
fb_assert(result == 0);
break;
case isc_segment:
case 0:
break;
default:
m_iscConnection.raise(status, tdbb, "isc_get_segment");
}
return result;
}
void IscBlob::write(thread_db* tdbb, const UCHAR* buff, USHORT len)
{
fb_assert(m_handle);
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
m_iscProvider.isc_put_segment(status, &m_handle, len, reinterpret_cast<const SCHAR*>(buff));
}
if (status[1]) {
m_iscConnection.raise(status, tdbb, "isc_put_segment");
}
}
void IscBlob::close(thread_db* tdbb)
{
fb_assert(m_handle);
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
m_iscProvider.isc_close_blob(status, &m_handle);
}
if (status[1]) {
m_iscConnection.raise(status, tdbb, "isc_close_blob");
}
fb_assert(!m_handle);
}
void IscBlob::cancel(thread_db* tdbb)
{
if (!m_handle)
return;
ISC_STATUS_ARRAY status = {0};
{
EngineCallbackGuard guard(tdbb, m_iscConnection);
m_iscProvider.isc_cancel_blob(status, &m_handle);
}
if (status[1]) {
m_iscConnection.raise(status, tdbb, "isc_close_blob");
}
fb_assert(!m_handle);
}
// IscProvider
// isc api
ISC_STATUS IscProvider::notImplemented(ISC_STATUS* status) const
{
Arg::Gds(isc_unavailable).copyTo(status);
return status[1];
}
ISC_STATUS ISC_EXPORT IscProvider::isc_attach_database(ISC_STATUS* user_status,
short file_length, const char* file_name, isc_db_handle* public_handle,
short dpb_length, const char* dpb)
{
if (!m_api.isc_attach_database)
return notImplemented(user_status);
return (*m_api.isc_attach_database) (user_status, file_length, file_name,
public_handle, dpb_length, dpb);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_gen_sdl(ISC_STATUS* user_status,
const ISC_ARRAY_DESC*, short*, char*, short*)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_get_slice(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
ISC_QUAD *,
const ISC_ARRAY_DESC*,
void *,
ISC_LONG *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_lookup_bounds(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
const char*,
const char*,
ISC_ARRAY_DESC *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_lookup_desc(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
const char*,
const char*,
ISC_ARRAY_DESC *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_set_desc(ISC_STATUS* user_status,
const char*,
const char*,
const short*,
const short*,
const short*,
ISC_ARRAY_DESC *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_array_put_slice(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
ISC_QUAD *,
const ISC_ARRAY_DESC*,
void *,
ISC_LONG *)
{
return notImplemented(user_status);
}
void ISC_EXPORT IscProvider::isc_blob_default_desc(ISC_BLOB_DESC *,
const unsigned char*,
const unsigned char*)
{
return;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_blob_gen_bpb(ISC_STATUS* user_status,
const ISC_BLOB_DESC*,
const ISC_BLOB_DESC*,
unsigned short,
unsigned char *,
unsigned short *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_blob_info(ISC_STATUS* user_status,
isc_blob_handle* blob_handle,
short item_length,
const char* items,
short buffer_length,
char* buffer)
{
if (!m_api.isc_blob_info)
return notImplemented(user_status);
return (*m_api.isc_blob_info) (user_status, blob_handle,
item_length, items, buffer_length, buffer);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_blob_lookup_desc(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
const unsigned char*,
const unsigned char*,
ISC_BLOB_DESC *,
unsigned char *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_blob_set_desc(ISC_STATUS* user_status,
const unsigned char*,
const unsigned char*,
short,
short,
short,
ISC_BLOB_DESC *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_cancel_blob(ISC_STATUS* user_status,
isc_blob_handle* blob_handle)
{
if (!m_api.isc_cancel_blob)
return notImplemented(user_status);
return (*m_api.isc_cancel_blob) (user_status, blob_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_cancel_events(ISC_STATUS* user_status,
isc_db_handle *,
ISC_LONG *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_close_blob(ISC_STATUS* user_status,
isc_blob_handle* blob_handle)
{
if (!m_api.isc_close_blob)
return notImplemented(user_status);
return (*m_api.isc_close_blob) (user_status, blob_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_commit_retaining(ISC_STATUS* user_status,
isc_tr_handle* tra_handle)
{
if (!m_api.isc_commit_retaining)
return notImplemented(user_status);
return (*m_api.isc_commit_retaining) (user_status, tra_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_commit_transaction(ISC_STATUS* user_status,
isc_tr_handle* tra_handle)
{
if (!m_api.isc_commit_transaction)
return notImplemented(user_status);
return (*m_api.isc_commit_transaction) (user_status, tra_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_create_blob(ISC_STATUS* user_status,
isc_db_handle* db_handle,
isc_tr_handle* tr_handle,
isc_blob_handle* blob_handle,
ISC_QUAD* blob_id)
{
if (!m_api.isc_create_blob)
return notImplemented(user_status);
return (*m_api.isc_create_blob) (user_status, db_handle, tr_handle,
blob_handle, blob_id);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_create_blob2(ISC_STATUS* user_status,
isc_db_handle* db_handle,
isc_tr_handle* tr_handle,
isc_blob_handle* blob_handle,
ISC_QUAD* blob_id,
short bpb_length,
const char* bpb)
{
if (!m_api.isc_create_blob2)
return notImplemented(user_status);
return (*m_api.isc_create_blob2) (user_status, db_handle, tr_handle,
blob_handle, blob_id, bpb_length, bpb);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_create_database(ISC_STATUS* user_status,
short,
const char*,
isc_db_handle *,
short,
const char*,
short)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_database_info(ISC_STATUS* user_status,
isc_db_handle* db_handle,
short info_len,
const char* info,
short res_len,
char* res)
{
if (!m_api.isc_database_info)
return notImplemented(user_status);
return (*m_api.isc_database_info) (user_status, db_handle,
info_len, info, res_len, res);
}
void ISC_EXPORT IscProvider::isc_decode_date(const ISC_QUAD*,
void *)
{
return;
}
void ISC_EXPORT IscProvider::isc_decode_sql_date(const ISC_DATE*,
void *)
{
return;
}
void ISC_EXPORT IscProvider::isc_decode_sql_time(const ISC_TIME*,
void *)
{
return;
}
void ISC_EXPORT IscProvider::isc_decode_timestamp(const ISC_TIMESTAMP*,
void *)
{
return;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_detach_database(ISC_STATUS* user_status,
isc_db_handle* public_handle)
{
if (!m_api.isc_detach_database)
return notImplemented(user_status);
return (*m_api.isc_detach_database) (user_status, public_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_drop_database(ISC_STATUS* user_status,
isc_db_handle *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_allocate_statement(ISC_STATUS* user_status,
isc_db_handle* db_handle, isc_stmt_handle* stmt_handle)
{
if (!m_api.isc_dsql_allocate_statement)
return notImplemented(user_status);
return (*m_api.isc_dsql_allocate_statement) (user_status, db_handle, stmt_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_alloc_statement2(ISC_STATUS* user_status,
isc_db_handle* db_handle, isc_stmt_handle* stmt_handle)
{
if (!m_api.isc_dsql_alloc_statement2)
return notImplemented(user_status);
return (*m_api.isc_dsql_alloc_statement2) (user_status, db_handle, stmt_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_describe(ISC_STATUS* user_status,
isc_stmt_handle* stmt_handle, unsigned short dialect, XSQLDA* sqlda)
{
if (!m_api.isc_dsql_describe)
return notImplemented(user_status);
return (*m_api.isc_dsql_describe) (user_status, stmt_handle, dialect, sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_describe_bind(ISC_STATUS* user_status,
isc_stmt_handle* stmt_handle, unsigned short dialect, XSQLDA* sqlda)
{
if (!m_api.isc_dsql_describe_bind)
return notImplemented(user_status);
return (*m_api.isc_dsql_describe_bind) (user_status, stmt_handle, dialect, sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_exec_immed2(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
unsigned short,
const char*,
unsigned short,
const XSQLDA *,
const XSQLDA *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_execute(ISC_STATUS* user_status,
isc_tr_handle* tra_handle, isc_stmt_handle* stmt_handle, unsigned short dialect,
const XSQLDA* sqlda)
{
if (!m_api.isc_dsql_execute)
return notImplemented(user_status);
return (*m_api.isc_dsql_execute) (user_status, tra_handle, stmt_handle, dialect, sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_execute2(ISC_STATUS* user_status,
isc_tr_handle* tra_handle, isc_stmt_handle* stmt_handle, unsigned short dialect,
const XSQLDA* in_sqlda, const XSQLDA* out_sqlda)
{
if (!m_api.isc_dsql_execute2)
return notImplemented(user_status);
return (*m_api.isc_dsql_execute2) (user_status, tra_handle, stmt_handle, dialect,
in_sqlda, out_sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_execute_immediate(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
unsigned short,
const char*,
unsigned short,
const XSQLDA *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_fetch(ISC_STATUS* user_status,
isc_stmt_handle* stmt_handle, unsigned short da_version, const XSQLDA* sqlda)
{
if (!m_api.isc_dsql_fetch)
return notImplemented(user_status);
return (*m_api.isc_dsql_fetch) (user_status, stmt_handle, da_version, sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_finish(isc_db_handle *)
{
return isc_unavailable;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_free_statement(ISC_STATUS* user_status,
isc_stmt_handle* stmt_handle, unsigned short option)
{
if (!m_api.isc_dsql_free_statement)
return notImplemented(user_status);
return (*m_api.isc_dsql_free_statement) (user_status, stmt_handle, option);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_insert(ISC_STATUS* user_status,
isc_stmt_handle *,
unsigned short,
XSQLDA *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_prepare(ISC_STATUS* user_status,
isc_tr_handle* tra_handle, isc_stmt_handle* stmt_handle, unsigned short length,
const char* str, unsigned short dialect, XSQLDA* sqlda)
{
if (!m_api.isc_dsql_prepare)
return notImplemented(user_status);
return (*m_api.isc_dsql_prepare) (user_status, tra_handle, stmt_handle,
length, str, dialect, sqlda);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_set_cursor_name(ISC_STATUS* user_status,
isc_stmt_handle *,
const char*,
unsigned short)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_dsql_sql_info(ISC_STATUS* user_status,
isc_stmt_handle* stmt_handle, short items_len, const char* items,
short buffer_len, char* buffer)
{
if (!m_api.isc_dsql_sql_info)
return notImplemented(user_status);
return (*m_api.isc_dsql_sql_info) (user_status, stmt_handle, items_len, items,
buffer_len, buffer);
}
void ISC_EXPORT IscProvider::isc_encode_date(const void*,
ISC_QUAD *)
{
return;
}
void ISC_EXPORT IscProvider::isc_encode_sql_date(const void*,
ISC_DATE *)
{
return;
}
void ISC_EXPORT IscProvider::isc_encode_sql_time(const void*,
ISC_TIME *)
{
return;
}
void ISC_EXPORT IscProvider::isc_encode_timestamp(const void*,
ISC_TIMESTAMP *)
{
return;
}
ISC_LONG ISC_EXPORT_VARARG IscProvider::isc_event_block(char * *,
char * *,
unsigned short, ...)
{
return 0;
}
void ISC_EXPORT IscProvider::isc_event_counts(ISC_ULONG *,
short,
char *,
const char*)
{
return;
}
// 17 May 2001 - IscProvider::isc_expand_dpb is DEPRECATED
void ISC_EXPORT_VARARG IscProvider::isc_expand_dpb(char * *,
short *, ...)
{
return;
}
int ISC_EXPORT IscProvider::isc_modify_dpb(char * *,
short *,
unsigned short,
const char*,
short)
{
return 0;
}
ISC_LONG ISC_EXPORT IscProvider::isc_free(char *)
{
return 0;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_get_segment(ISC_STATUS* user_status,
isc_blob_handle* blob_handle,
unsigned short* length,
unsigned short buffer_length,
char* buffer)
{
if (!m_api.isc_get_segment)
return notImplemented(user_status);
return (*m_api.isc_get_segment) (user_status, blob_handle, length,
buffer_length, buffer);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_get_slice(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
ISC_QUAD *,
short,
const char*,
short,
const ISC_LONG*,
ISC_LONG,
void *,
ISC_LONG *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_interprete(char *,
const ISC_STATUS * *)
{
return isc_unavailable;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_open_blob(ISC_STATUS* user_status,
isc_db_handle* db_handle,
isc_tr_handle* tr_handle,
isc_blob_handle* blob_handle,
ISC_QUAD* blob_id)
{
if (!m_api.isc_open_blob)
return notImplemented(user_status);
return (*m_api.isc_open_blob) (user_status, db_handle, tr_handle,
blob_handle, blob_id);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_open_blob2(ISC_STATUS* user_status,
isc_db_handle* db_handle,
isc_tr_handle* tr_handle,
isc_blob_handle* blob_handle,
ISC_QUAD* blob_id,
ISC_USHORT bpb_length,
const ISC_UCHAR* bpb)
{
if (!m_api.isc_open_blob2)
return notImplemented(user_status);
return (*m_api.isc_open_blob2) (user_status, db_handle, tr_handle,
blob_handle, blob_id, bpb_length, bpb);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_prepare_transaction2(ISC_STATUS* user_status,
isc_tr_handle *,
ISC_USHORT,
const ISC_UCHAR*)
{
return notImplemented(user_status);
}
void ISC_EXPORT IscProvider::isc_print_sqlerror(ISC_SHORT,
const ISC_STATUS*)
{
return;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_print_status(const ISC_STATUS*)
{
return isc_unavailable;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_put_segment(ISC_STATUS* user_status,
isc_blob_handle* blob_handle,
unsigned short buffer_length,
const char* buffer)
{
if (!m_api.isc_put_segment)
return notImplemented(user_status);
return (*m_api.isc_put_segment) (user_status, blob_handle,
buffer_length, buffer);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_put_slice(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
ISC_QUAD *,
short,
const char*,
short,
const ISC_LONG*,
ISC_LONG,
void *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_que_events(ISC_STATUS* user_status,
isc_db_handle *,
ISC_LONG *,
ISC_USHORT,
const ISC_UCHAR*,
isc_callback,
void *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_rollback_retaining(ISC_STATUS* user_status,
isc_tr_handle* tra_handle)
{
if (!m_api.isc_rollback_retaining)
return notImplemented(user_status);
return (*m_api.isc_rollback_retaining) (user_status, tra_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_rollback_transaction(ISC_STATUS* user_status,
isc_tr_handle* tra_handle)
{
if (!m_api.isc_rollback_transaction)
return notImplemented(user_status);
return (*m_api.isc_rollback_transaction) (user_status, tra_handle);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_start_multiple(ISC_STATUS* user_status,
isc_tr_handle* tra_handle, short count, void* vec)
{
if (!m_api.isc_start_multiple)
return notImplemented(user_status);
return (*m_api.isc_start_multiple) (user_status, tra_handle, count, vec);
}
struct why_teb
{
FB_API_HANDLE* teb_database;
int teb_tpb_length;
const UCHAR* teb_tpb;
};
ISC_STATUS ISC_EXPORT_VARARG IscProvider::isc_start_transaction(ISC_STATUS* user_status,
isc_tr_handle* tra_handle,
short count, ...)
{
if (!m_api.isc_start_multiple) // !!!
return notImplemented(user_status);
Firebird::HalfStaticArray<why_teb, 16> tebs;
why_teb* teb = tebs.getBuffer(count);
const why_teb* const end = teb + count;
va_list ptr;
va_start(ptr, count);
for (why_teb* teb_iter = teb; teb_iter < end; teb_iter++)
{
teb_iter->teb_database = va_arg(ptr, FB_API_HANDLE*);
teb_iter->teb_tpb_length = va_arg(ptr, int);
teb_iter->teb_tpb = va_arg(ptr, UCHAR *);
}
va_end(ptr);
return (*m_api.isc_start_multiple) (user_status, tra_handle, count, teb);
}
ISC_STATUS ISC_EXPORT_VARARG IscProvider::isc_reconnect_transaction(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
short,
const char*)
{
return notImplemented(user_status);
}
ISC_LONG ISC_EXPORT IscProvider::isc_sqlcode(const ISC_STATUS*)
{
return isc_unavailable;
}
void ISC_EXPORT IscProvider::isc_sql_interprete(short,
char *,
short)
{
return;
}
ISC_STATUS ISC_EXPORT IscProvider::isc_transaction_info(ISC_STATUS* user_status,
isc_tr_handle *,
short,
const char*,
short,
char *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_transact_request(ISC_STATUS* user_status,
isc_db_handle *,
isc_tr_handle *,
unsigned short,
char *,
unsigned short,
char *,
unsigned short,
char *)
{
return notImplemented(user_status);
}
ISC_LONG ISC_EXPORT IscProvider::isc_vax_integer(const char* p, short len)
{
return ::isc_vax_integer(p, len);
}
ISC_INT64 ISC_EXPORT IscProvider::isc_portable_integer(const unsigned char* p, short len)
{
return ::isc_portable_integer(p, len);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_seek_blob(ISC_STATUS* user_status,
isc_blob_handle *,
short,
ISC_LONG,
ISC_LONG *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_service_attach(ISC_STATUS* user_status,
unsigned short,
const char*,
isc_svc_handle *,
unsigned short,
const char*)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_service_detach(ISC_STATUS* user_status,
isc_svc_handle *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_service_query(ISC_STATUS* user_status,
isc_svc_handle *,
isc_resv_handle *,
unsigned short,
const char*,
unsigned short,
const char*,
unsigned short,
char *)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::isc_service_start(ISC_STATUS* user_status,
isc_svc_handle *,
isc_resv_handle *,
unsigned short,
const char*)
{
return notImplemented(user_status);
}
ISC_STATUS ISC_EXPORT IscProvider::fb_cancel_operation(ISC_STATUS* user_status,
isc_db_handle* db_handle,
USHORT option)
{
if (m_api.fb_cancel_operation)
return m_api.fb_cancel_operation(user_status, db_handle, option);
else
return notImplemented(user_status);
}
void IscProvider::loadAPI()
{
ISC_STATUS_ARRAY status;
notImplemented(status);
status_exception::raise(status);
}
// FBProvider
#define PROTO(X) (prototype_##X*) &X
static FirebirdApiPointers isc_callbacks =
{
PROTO(isc_attach_database),
PROTO(isc_array_gen_sdl),
PROTO(isc_array_get_slice),
PROTO(isc_array_lookup_bounds),
PROTO(isc_array_lookup_desc),
PROTO(isc_array_set_desc),
PROTO(isc_array_put_slice),
PROTO(isc_blob_default_desc),
PROTO(isc_blob_gen_bpb),
PROTO(isc_blob_info),
PROTO(isc_blob_lookup_desc),
PROTO(isc_blob_set_desc),
PROTO(isc_cancel_blob),
PROTO(isc_cancel_events),
PROTO(isc_close_blob),
PROTO(isc_commit_retaining),
PROTO(isc_commit_transaction),
PROTO(isc_create_blob),
PROTO(isc_create_blob2),
PROTO(isc_create_database),
PROTO(isc_database_info),
PROTO(isc_decode_date),
PROTO(isc_decode_sql_date),
PROTO(isc_decode_sql_time),
PROTO(isc_decode_timestamp),
PROTO(isc_detach_database),
PROTO(isc_drop_database),
PROTO(isc_dsql_allocate_statement),
PROTO(isc_dsql_alloc_statement2),
PROTO(isc_dsql_describe),
PROTO(isc_dsql_describe_bind),
PROTO(isc_dsql_exec_immed2),
PROTO(isc_dsql_execute),
PROTO(isc_dsql_execute2),
PROTO(isc_dsql_execute_immediate),
PROTO(isc_dsql_fetch),
PROTO(isc_dsql_finish),
PROTO(isc_dsql_free_statement),
PROTO(isc_dsql_insert),
PROTO(isc_dsql_prepare),
PROTO(isc_dsql_set_cursor_name),
PROTO(isc_dsql_sql_info),
PROTO(isc_encode_date),
PROTO(isc_encode_sql_date),
PROTO(isc_encode_sql_time),
PROTO(isc_encode_timestamp),
PROTO(isc_event_block),
PROTO(isc_event_counts),
PROTO(isc_expand_dpb),
PROTO(isc_modify_dpb),
PROTO(isc_free),
PROTO(isc_get_segment),
PROTO(isc_get_slice),
PROTO(isc_interprete),
PROTO(isc_open_blob),
PROTO(isc_open_blob2),
PROTO(isc_prepare_transaction2),
PROTO(isc_print_sqlerror),
PROTO(isc_print_status),
PROTO(isc_put_segment),
PROTO(isc_put_slice),
PROTO(isc_que_events),
PROTO(isc_rollback_retaining),
PROTO(isc_rollback_transaction),
PROTO(isc_start_multiple),
PROTO(isc_start_transaction),
PROTO(isc_reconnect_transaction),
PROTO(isc_sqlcode),
PROTO(isc_sql_interprete),
PROTO(isc_transaction_info),
PROTO(isc_transact_request),
PROTO(isc_vax_integer),
PROTO(isc_seek_blob),
PROTO(isc_service_attach),
PROTO(isc_service_detach),
PROTO(isc_service_query),
PROTO(isc_service_start),
PROTO(fb_cancel_operation)
};
void FBProvider::loadAPI()
{
m_api = isc_callbacks;
m_api_loaded = true;
}
static bool isConnectionBrokenError(ISC_STATUS status)
{
switch (status)
{
case isc_att_shutdown:
case isc_network_error:
case isc_net_read_err:
case isc_net_write_err:
return true;
}
return false;
}
static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array<dsc> &descs)
{
size_t offset = 0;
XSQLVAR* xVar = xsqlda->sqlvar;
for (int i = 0; i < xsqlda->sqld; xVar++, i++)
{
const UCHAR dtype = sqlTypeToDscType(xVar->sqltype & ~1);
xVar->sqltype |= 1;
if (type_alignments[dtype])
offset = FB_ALIGN(offset, type_alignments[dtype]);
offset += xVar->sqllen;
const int type = xVar->sqltype & (~1);
if (type == SQL_VARYING) {
offset += sizeof(SSHORT);
}
// null indicator
offset = FB_ALIGN(offset, type_alignments[dtype_short]);
offset += sizeof(SSHORT);
}
descs.resize(xsqlda->sqld * 2);
UCHAR* p = buff.getBuffer(offset);
offset = 0;
xVar = xsqlda->sqlvar;
for (int i = 0; i < xsqlda->sqld; xVar++, i++)
{
const UCHAR dtype = sqlTypeToDscType(xVar->sqltype & ~1);
if (type_alignments[dtype])
offset = FB_ALIGN(offset, type_alignments[dtype]);
xVar->sqldata = (ISC_SCHAR*) (p + offset);
// build the src descriptor
dsc& src = descs[i * 2];
src.dsc_dtype = dtype;
src.dsc_length = xVar->sqllen;
src.dsc_scale = xVar->sqlscale;
src.dsc_sub_type = xVar->sqlsubtype;
src.dsc_address = (UCHAR*) xVar->sqldata;
offset += xVar->sqllen;
const int type = xVar->sqltype & (~1);
if (type == SQL_VARYING)
{
offset += sizeof(SSHORT);
src.dsc_length += sizeof(SSHORT);
}
else if (type == SQL_NULL) {
src.dsc_flags |= DSC_null;
}
// null indicator
offset = FB_ALIGN(offset, type_alignments[dtype_short]);
xVar->sqlind = (SSHORT*) (p + offset);
dsc& null = descs[i * 2 + 1];
null.makeShort(0, xVar->sqlind);
offset += sizeof(SSHORT);
}
}
static UCHAR sqlTypeToDscType(SSHORT sqlType)
{
switch (sqlType)
{
case SQL_VARYING:
return dtype_varying;
case SQL_TEXT:
return dtype_text;
case SQL_NULL:
return dtype_text;
case SQL_DOUBLE:
return dtype_double;
case SQL_FLOAT:
return dtype_real;
case SQL_D_FLOAT:
return dtype_d_float;
case SQL_TYPE_DATE:
return dtype_sql_date;
case SQL_TYPE_TIME:
return dtype_sql_time;
case SQL_TIMESTAMP:
return dtype_timestamp;
case SQL_BLOB:
return dtype_blob;
case SQL_ARRAY:
return dtype_array;
case SQL_LONG:
return dtype_long;
case SQL_SHORT:
return dtype_short;
case SQL_INT64:
return dtype_int64;
case SQL_QUAD:
return dtype_quad;
case SQL_BOOLEAN:
return dtype_boolean;
default:
return dtype_unknown;
}
}
} // namespace EDS