2008-04-09 22:18:47 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
2008-12-04 10:29:16 +01:00
|
|
|
* The Original Code was created by Vladyslav Khorsun for the
|
|
|
|
* Firebird Open Source RDBMS project and based on execute_statement
|
2008-04-09 22:18:47 +02:00
|
|
|
* module by Alexander Peshkoff.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2007 Vladyslav Khorsun <hvlad@users.sourceforge.net>
|
|
|
|
* and all contributors signed below.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
|
|
|
#include "fb_types.h"
|
|
|
|
#include "../common.h"
|
|
|
|
#include "../../include/fb_blk.h"
|
|
|
|
#include "fb_exception.h"
|
|
|
|
#include "iberror.h"
|
|
|
|
|
|
|
|
#include "../../dsql/chars.h"
|
|
|
|
#include "../dsc.h"
|
|
|
|
#include "../exe.h"
|
|
|
|
#include "ExtDS.h"
|
|
|
|
#include "../jrd.h"
|
|
|
|
#include "../tra.h"
|
|
|
|
|
|
|
|
#include "../blb_proto.h"
|
|
|
|
#include "../exe_proto.h"
|
|
|
|
#include "../err_proto.h"
|
|
|
|
#include "../evl_proto.h"
|
2008-06-08 22:42:27 +02:00
|
|
|
#include "../intl_proto.h"
|
2008-04-09 22:18:47 +02:00
|
|
|
#include "../mov_proto.h"
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
#include "../jrd/ibase.h"
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
using namespace Jrd;
|
|
|
|
using namespace Firebird;
|
|
|
|
|
|
|
|
|
|
|
|
namespace EDS {
|
|
|
|
// Manager
|
|
|
|
|
|
|
|
GlobalPtr<Manager> Manager::manager;
|
2008-11-28 12:46:37 +01:00
|
|
|
Mutex Manager::m_mutex;
|
2008-04-12 23:20:26 +02:00
|
|
|
Provider* Manager::m_providers = NULL;
|
2008-11-28 12:46:37 +01:00
|
|
|
bool Manager::m_initialized = false;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
Manager::Manager(MemoryPool &pool) :
|
2008-04-12 23:20:26 +02:00
|
|
|
PermanentStorage(pool)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Manager::~Manager()
|
|
|
|
{
|
2008-04-12 23:20:26 +02:00
|
|
|
while (m_providers)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-04-12 23:20:26 +02:00
|
|
|
Provider* to_delete = m_providers;
|
|
|
|
m_providers = m_providers->m_next;
|
|
|
|
delete to_delete;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-28 12:46:37 +01:00
|
|
|
void Manager::init()
|
|
|
|
{
|
|
|
|
fb_shutdown_callback(0, shutdown, fb_shut_preproviders, 0);
|
|
|
|
}
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
void Manager::addProvider(Provider* provider)
|
|
|
|
{
|
2008-04-13 10:11:16 +02:00
|
|
|
for (const Provider* prv = m_providers; prv; prv = prv->m_next) {
|
2008-04-12 23:20:26 +02:00
|
|
|
if (prv->m_name == provider->m_name) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-04-12 23:20:26 +02:00
|
|
|
provider->m_next = m_providers;
|
|
|
|
m_providers = provider;
|
2008-04-09 22:18:47 +02:00
|
|
|
provider->initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Provider* Manager::getProvider(const string &prvName)
|
|
|
|
{
|
2008-04-12 23:20:26 +02:00
|
|
|
for (Provider* prv = m_providers; prv; prv = prv->m_next) {
|
|
|
|
if (prv->m_name == prvName) {
|
|
|
|
return prv;
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
2008-04-12 23:20:26 +02:00
|
|
|
|
2008-06-19 12:45:18 +02:00
|
|
|
// External Data Source provider ''@1'' not found
|
2008-12-04 10:29:16 +01:00
|
|
|
ERR_post(Arg::Gds(isc_eds_provider_not_found) << Arg::Str(prvName));
|
|
|
|
return NULL;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
Connection* Manager::getConnection(thread_db *tdbb, const string &dataSource,
|
2008-04-09 22:18:47 +02:00
|
|
|
const string &user, const string &pwd, TraScope tra_scope)
|
|
|
|
{
|
2008-11-28 12:46:37 +01:00
|
|
|
if (!m_initialized)
|
|
|
|
{
|
|
|
|
MutexLockGuard guard(m_mutex);
|
|
|
|
if (!m_initialized)
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
m_initialized = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
// dataSource : registered data source name
|
2008-04-09 22:18:47 +02:00
|
|
|
// or connection string : provider::database
|
|
|
|
string prvName, dbName;
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (dataSource.isEmpty())
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-06-08 22:42:27 +02:00
|
|
|
prvName = INTERNAL_PROVIDER_NAME;
|
|
|
|
dbName = tdbb->getDatabase()->dbb_database_name.c_str();
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
else
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
size_t pos = dataSource.find("::");
|
|
|
|
if (pos != string::npos)
|
|
|
|
{
|
|
|
|
prvName = dataSource.substr(0, pos);
|
|
|
|
dbName = dataSource.substr(pos + 2);
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
else
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-12-04 10:29:16 +01:00
|
|
|
// search dataSource at registered data sources and get connection
|
2008-04-09 22:18:47 +02:00
|
|
|
// string, user and password from this info if found
|
|
|
|
|
|
|
|
// if not found - treat dataSource as Firebird's connection string
|
|
|
|
prvName = FIREBIRD_PROVIDER_NAME;
|
|
|
|
dbName = dataSource;
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
Provider* prv = getProvider(prvName);
|
|
|
|
return prv->getConnection(tdbb, dbName, user, pwd, tra_scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Manager::jrdAttachmentEnd(thread_db *tdbb, Jrd::Attachment* att)
|
|
|
|
{
|
2008-04-12 23:20:26 +02:00
|
|
|
for (Provider* prv = m_providers; prv; prv = prv->m_next) {
|
|
|
|
prv->jrdAttachmentEnd(tdbb, att);
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
int Manager::shutdown(const int reason, const int mask, void* arg)
|
|
|
|
{
|
|
|
|
thread_db *tdbb = JRD_get_thread_data();
|
|
|
|
for (Provider* prv = m_providers; prv; prv = prv->m_next) {
|
|
|
|
prv->cancelConnections(tdbb);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
// Provider
|
|
|
|
|
|
|
|
Provider::Provider(const char* prvName) :
|
|
|
|
m_name(getPool()),
|
|
|
|
m_connections(getPool()),
|
|
|
|
m_flags(0)
|
|
|
|
{
|
|
|
|
m_name = prvName;
|
|
|
|
}
|
|
|
|
|
|
|
|
Provider::~Provider()
|
|
|
|
{
|
|
|
|
thread_db *tdbb = JRD_get_thread_data();
|
|
|
|
clearConnections(tdbb);
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
Connection* Provider::getConnection(thread_db *tdbb, const string &dbName,
|
2008-04-09 22:18:47 +02:00
|
|
|
const string &user, const string &pwd, TraScope tra_scope)
|
|
|
|
{
|
2008-11-28 12:46:37 +01:00
|
|
|
MutexLockGuard guard(m_mutex);
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
Connection **conn_ptr = m_connections.begin();
|
|
|
|
Connection **end = m_connections.end();
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
for (; conn_ptr < end; conn_ptr++)
|
|
|
|
{
|
|
|
|
Connection *conn = *conn_ptr;
|
|
|
|
if (conn->isSameDatabase(tdbb, dbName, user, pwd) &&
|
|
|
|
conn->isAvailable(tdbb, tra_scope))
|
|
|
|
{
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Connection *conn = doCreateConnection();
|
|
|
|
try {
|
|
|
|
conn->attach(tdbb, dbName, user, pwd);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
Connection::deleteConnection(tdbb, conn);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
m_connections.add(conn);
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
// hvlad: in current implementation I didn't return connections in pool as
|
2008-04-10 21:41:30 +02:00
|
|
|
// I have not implemented way to delete long idle connections.
|
2008-04-09 22:18:47 +02:00
|
|
|
void Provider::releaseConnection(thread_db *tdbb, Connection& conn, bool /*inPool*/)
|
|
|
|
{
|
2008-11-28 12:46:37 +01:00
|
|
|
MutexLockGuard guard(m_mutex);
|
|
|
|
|
2008-04-13 12:03:56 +02:00
|
|
|
size_t pos;
|
|
|
|
if (m_connections.find(&conn, pos))
|
2008-04-10 21:41:30 +02:00
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
m_connections.remove(pos);
|
|
|
|
Connection::deleteConnection(tdbb, &conn);
|
|
|
|
return;
|
2008-04-10 21:41:30 +02:00
|
|
|
}
|
2008-04-13 16:38:39 +02:00
|
|
|
|
2008-04-13 12:03:56 +02:00
|
|
|
fb_assert(false);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Provider::clearConnections(thread_db *tdbb)
|
|
|
|
{
|
2008-11-28 12:46:37 +01:00
|
|
|
MutexLockGuard guard(m_mutex);
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
Connection **ptr = m_connections.begin();
|
|
|
|
Connection **end = m_connections.end();
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
for (; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
Connection::deleteConnection(tdbb, *ptr);
|
|
|
|
*ptr = NULL;
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_connections.clear();
|
|
|
|
}
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
void Provider::cancelConnections(thread_db *tdbb)
|
|
|
|
{
|
2008-11-28 12:46:37 +01:00
|
|
|
MutexLockGuard guard(m_mutex);
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
Connection **ptr = m_connections.begin();
|
|
|
|
Connection **end = m_connections.end();
|
|
|
|
|
|
|
|
for (; ptr < end; ptr++) {
|
|
|
|
(*ptr)->cancelExecution(tdbb);
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
// Connection
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
Connection::Connection(Provider &prov) :
|
|
|
|
PermanentStorage(prov.getPool()),
|
|
|
|
m_provider(prov),
|
|
|
|
m_dbName(getPool()),
|
2008-06-08 22:42:27 +02:00
|
|
|
m_dpb(getPool(), ClumpletReader::Tagged, MAX_DPB_SIZE),
|
2008-04-09 22:18:47 +02:00
|
|
|
m_transactions(getPool()),
|
|
|
|
m_statements(getPool()),
|
|
|
|
m_freeStatements(NULL),
|
|
|
|
m_used_stmts(0),
|
|
|
|
m_free_stmts(0),
|
|
|
|
m_deleting(false),
|
|
|
|
m_sqlDialect(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::deleteConnection(thread_db *tdbb, Connection *conn)
|
|
|
|
{
|
2008-11-28 00:06:48 +01:00
|
|
|
conn->m_deleting = true;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (conn->isConnected())
|
2008-04-09 22:18:47 +02:00
|
|
|
conn->detach(tdbb);
|
|
|
|
|
|
|
|
delete conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connection::~Connection()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-06-08 22:42:27 +02:00
|
|
|
void Connection::generateDPB(thread_db *tdbb, ClumpletWriter &dpb,
|
|
|
|
const string &dbName, const string &user, const string &pwd) const
|
|
|
|
{
|
|
|
|
dpb.reset(isc_dpb_version1);
|
|
|
|
|
|
|
|
Firebird::string &attUser = tdbb->getAttachment()->att_user->usr_user_name;
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if ((m_provider.getFlags() & prvTrustedAuth) &&
|
2008-06-08 22:42:27 +02:00
|
|
|
(user.isEmpty() || user == attUser) && pwd.isEmpty())
|
|
|
|
{
|
|
|
|
dpb.insertString(isc_dpb_trusted_auth, attUser);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!user.isEmpty()) {
|
|
|
|
dpb.insertString(isc_dpb_user_name, user);
|
|
|
|
}
|
|
|
|
if (!pwd.isEmpty()) {
|
|
|
|
dpb.insertString(isc_dpb_password, pwd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CharSet* const cs = INTL_charset_lookup(tdbb, tdbb->getAttachment()->att_charset);
|
|
|
|
if (cs) {
|
|
|
|
dpb.insertString(isc_dpb_lc_ctype, cs->getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
bool Connection::isSameDatabase(thread_db *tdbb, const string &dbName,
|
2008-06-08 22:42:27 +02:00
|
|
|
const string &user, const string &pwd) const
|
|
|
|
{
|
|
|
|
if (m_dbName != dbName)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
|
|
|
generateDPB(tdbb, dpb, dbName, user, pwd);
|
|
|
|
|
|
|
|
return m_dpb.simpleCompare(dpb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
Transaction* Connection::createTransaction()
|
|
|
|
{
|
|
|
|
Transaction* tran = doCreateTransaction();
|
|
|
|
m_transactions.add(tran);
|
|
|
|
return tran;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::deleteTransaction(Transaction *tran)
|
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
size_t pos;
|
|
|
|
if (m_transactions.find(tran, pos))
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
m_transactions.remove(pos);
|
|
|
|
delete tran;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fb_assert(false);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
if (!m_used_stmts && m_transactions.getCount() == 0 && !m_deleting)
|
2008-04-09 22:18:47 +02:00
|
|
|
m_provider.releaseConnection(JRD_get_thread_data(), *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* Connection::createStatement(const string &sql)
|
|
|
|
{
|
|
|
|
m_used_stmts++;
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
for (Statement **stmt_ptr = &m_freeStatements; *stmt_ptr; stmt_ptr = &(*stmt_ptr)->m_nextFree)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
Statement *stmt = *stmt_ptr;
|
|
|
|
if (stmt->getSql() == sql)
|
|
|
|
{
|
|
|
|
*stmt_ptr = stmt->m_nextFree;
|
|
|
|
stmt->m_nextFree = NULL;
|
|
|
|
m_free_stmts--;
|
|
|
|
return stmt;
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (m_freeStatements)
|
|
|
|
{
|
|
|
|
Statement *stmt = m_freeStatements;
|
|
|
|
m_freeStatements = stmt->m_nextFree;
|
|
|
|
stmt->m_nextFree = NULL;
|
|
|
|
m_free_stmts--;
|
|
|
|
return stmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement *stmt = doCreateStatement();
|
|
|
|
m_statements.add(stmt);
|
|
|
|
return stmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::releaseStatement(Jrd::thread_db *tdbb, Statement *stmt)
|
|
|
|
{
|
|
|
|
fb_assert(stmt && !stmt->isActive());
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (stmt->isAllocated() && m_free_stmts < MAX_CACHED_STMTS)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
stmt->m_nextFree = m_freeStatements;
|
|
|
|
m_freeStatements = stmt;
|
|
|
|
m_free_stmts++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
size_t pos;
|
|
|
|
if (m_statements.find(stmt, pos))
|
2008-04-10 21:41:30 +02:00
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
m_statements.remove(pos);
|
|
|
|
Statement::deleteStatement(tdbb, stmt);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fb_assert(false);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_used_stmts--;
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
if (!m_used_stmts && m_transactions.getCount() == 0 && !m_deleting)
|
2008-11-28 00:06:48 +01:00
|
|
|
m_provider.releaseConnection(tdbb, *this);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
2008-11-17 13:30:28 +01:00
|
|
|
void Connection::clearTransactions(Jrd::thread_db *tdbb)
|
|
|
|
{
|
|
|
|
while (m_transactions.getCount())
|
|
|
|
{
|
|
|
|
Transaction *tran = m_transactions[0];
|
|
|
|
tran->rollback(tdbb, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
void Connection::clearStatements(thread_db *tdbb)
|
|
|
|
{
|
|
|
|
Statement **stmt_ptr = m_statements.begin();
|
|
|
|
Statement **end = m_statements.end();
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
for (; stmt_ptr < end; stmt_ptr++)
|
|
|
|
{
|
|
|
|
Statement *stmt = *stmt_ptr;
|
|
|
|
if (stmt->isActive())
|
|
|
|
stmt->close(tdbb);
|
|
|
|
Statement::deleteStatement(tdbb, stmt);
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_statements.clear();
|
|
|
|
|
|
|
|
fb_assert(!m_used_stmts);
|
|
|
|
m_freeStatements = NULL;
|
|
|
|
m_free_stmts = m_used_stmts = 0;
|
|
|
|
}
|
|
|
|
|
2008-11-17 13:30:28 +01:00
|
|
|
void Connection::detach(thread_db *tdbb)
|
|
|
|
{
|
|
|
|
const bool was_deleting = m_deleting;
|
|
|
|
m_deleting = true;
|
|
|
|
|
2009-02-07 16:20:34 +01:00
|
|
|
try
|
|
|
|
{
|
2008-11-18 10:29:56 +01:00
|
|
|
clearStatements(tdbb);
|
|
|
|
clearTransactions(tdbb);
|
|
|
|
m_deleting = was_deleting;
|
2008-12-04 10:29:16 +01:00
|
|
|
}
|
2009-02-07 16:20:34 +01:00
|
|
|
catch (...)
|
|
|
|
{
|
2008-11-18 10:29:56 +01:00
|
|
|
m_deleting = was_deleting;
|
|
|
|
throw;
|
|
|
|
}
|
2008-11-17 13:30:28 +01:00
|
|
|
|
2009-02-06 13:01:34 +01:00
|
|
|
fb_assert(m_used_stmts == 0);
|
|
|
|
fb_assert(m_transactions.getCount() == 0);
|
|
|
|
|
2008-11-17 13:30:28 +01:00
|
|
|
doDetach(tdbb);
|
|
|
|
}
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
Transaction* Connection::findTransaction(thread_db *tdbb, TraScope traScope) const
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
jrd_tra *tran = tdbb->getTransaction();
|
|
|
|
Transaction *ext_tran = NULL;
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
switch (traScope)
|
|
|
|
{
|
|
|
|
case traCommon :
|
|
|
|
ext_tran = tran->tra_ext_common;
|
2008-12-04 10:29:16 +01:00
|
|
|
while (ext_tran)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
if (ext_tran->getConnection() == this)
|
|
|
|
break;
|
|
|
|
ext_tran = ext_tran->m_nextTran;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case traTwoPhase :
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_random) << Arg::Str("2PC transactions not implemented"));
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
//ext_tran = tran->tra_ext_two_phase;
|
|
|
|
// join transaction if not already joined
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ext_tran;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::raise(ISC_STATUS* status, thread_db *tdbb, const char* sWhere)
|
|
|
|
{
|
|
|
|
string rem_err;
|
|
|
|
m_provider.getRemoteError(status, rem_err);
|
|
|
|
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement error at @1 :\n@2Data source : @3
|
2008-12-04 10:29:16 +01:00
|
|
|
ERR_post(Arg::Gds(isc_eds_connection) << Arg::Str(sWhere) <<
|
|
|
|
Arg::Str(rem_err) <<
|
2008-08-27 14:20:47 +02:00
|
|
|
Arg::Str(getDataSourceName()));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
// Transaction
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
Transaction::Transaction(Connection &conn) :
|
|
|
|
PermanentStorage(conn.getProvider()->getPool()),
|
|
|
|
m_provider(*conn.getProvider()),
|
|
|
|
m_connection(conn),
|
|
|
|
m_scope(traCommon),
|
|
|
|
m_nextTran(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Transaction::~Transaction()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
void Transaction::generateTPB(thread_db *tdbb, ClumpletWriter &tpb,
|
2008-04-12 14:19:15 +02:00
|
|
|
TraModes traMode, bool readOnly, bool wait, int lockTimeout) const
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
switch (traMode)
|
|
|
|
{
|
2008-12-04 10:29:16 +01:00
|
|
|
case traReadCommited:
|
2008-04-09 22:18:47 +02:00
|
|
|
tpb.insertTag(isc_tpb_read_committed);
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
|
|
|
case traReadCommitedRecVersions:
|
2008-04-09 22:18:47 +02:00
|
|
|
tpb.insertTag(isc_tpb_read_committed);
|
|
|
|
tpb.insertTag(isc_tpb_rec_version);
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
|
|
|
case traConcurrency:
|
2008-04-09 22:18:47 +02:00
|
|
|
tpb.insertTag(isc_tpb_concurrency);
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
|
|
|
case traConsistency:
|
2008-04-09 22:18:47 +02:00
|
|
|
tpb.insertTag(isc_tpb_consistency);
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
tpb.insertTag(readOnly ? isc_tpb_read : isc_tpb_write);
|
|
|
|
tpb.insertTag(wait ? isc_tpb_wait : isc_tpb_nowait);
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (wait && lockTimeout)
|
|
|
|
tpb.insertInt(isc_tpb_lock_timeout, lockTimeout);
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
void Transaction::start(thread_db *tdbb, TraScope traScope, TraModes traMode,
|
2008-04-09 22:18:47 +02:00
|
|
|
bool readOnly, bool wait, int lockTimeout)
|
|
|
|
{
|
|
|
|
m_scope = traScope;
|
|
|
|
|
|
|
|
ClumpletWriter tpb(ClumpletReader::Tpb, 64, isc_tpb_version3);
|
|
|
|
generateTPB(tdbb, tpb, traMode, readOnly, wait, lockTimeout);
|
|
|
|
|
|
|
|
ISC_STATUS_ARRAY status = {0};
|
|
|
|
doStart(status, tdbb, tpb);
|
|
|
|
|
|
|
|
if (status[1]) {
|
|
|
|
m_connection.raise(status, tdbb, "transaction start");
|
|
|
|
}
|
|
|
|
|
|
|
|
jrd_tra *tran = tdbb->getTransaction();
|
|
|
|
switch (m_scope)
|
|
|
|
{
|
|
|
|
case traCommon :
|
|
|
|
this->m_nextTran = tran->tra_ext_common;
|
2008-11-17 13:30:28 +01:00
|
|
|
this->m_jrdTran = tran;
|
2008-04-09 22:18:47 +02:00
|
|
|
tran->tra_ext_common = this;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case traTwoPhase :
|
2008-12-04 10:29:16 +01:00
|
|
|
// join transaction
|
2008-11-17 13:30:28 +01:00
|
|
|
// this->m_jrdTran = tran;
|
2008-04-09 22:18:47 +02:00
|
|
|
// tran->tra_ext_two_phase = ext_tran;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transaction::prepare(thread_db *tdbb, int info_len, const char* info)
|
|
|
|
{
|
|
|
|
ISC_STATUS_ARRAY status = {0};
|
|
|
|
doPrepare(status, tdbb, info_len, info);
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (status[1]) {
|
|
|
|
m_connection.raise(status, tdbb, "transaction start");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transaction::commit(thread_db *tdbb, bool retain)
|
|
|
|
{
|
|
|
|
ISC_STATUS_ARRAY status = {0};
|
|
|
|
doCommit(status, tdbb, retain);
|
|
|
|
|
|
|
|
if (status[1]) {
|
|
|
|
m_connection.raise(status, tdbb, "transaction commit");
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (!retain)
|
2008-11-17 13:30:28 +01:00
|
|
|
{
|
|
|
|
detachFromJrdTran();
|
2008-04-09 22:18:47 +02:00
|
|
|
m_connection.deleteTransaction(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transaction::rollback(thread_db *tdbb, bool retain)
|
|
|
|
{
|
|
|
|
ISC_STATUS_ARRAY status = {0};
|
|
|
|
doRollback(status, tdbb, retain);
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
Connection &conn = m_connection;
|
2008-12-04 10:29:16 +01:00
|
|
|
if (!retain)
|
2008-11-17 13:30:28 +01:00
|
|
|
{
|
|
|
|
detachFromJrdTran();
|
2008-04-09 22:18:47 +02:00
|
|
|
m_connection.deleteTransaction(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status[1]) {
|
2008-11-28 00:06:48 +01:00
|
|
|
conn.raise(status, tdbb, "transaction rollback");
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
Transaction* Transaction::getTransaction(thread_db *tdbb, Connection *conn, TraScope tra_scope)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
jrd_tra *tran = tdbb->getTransaction();
|
|
|
|
Transaction *ext_tran = conn->findTransaction(tdbb, tra_scope);
|
|
|
|
|
|
|
|
if (!ext_tran)
|
|
|
|
{
|
|
|
|
ext_tran = conn->createTransaction();
|
|
|
|
|
|
|
|
TraModes traMode = traConcurrency;
|
|
|
|
if (tran->tra_flags & TRA_read_committed) {
|
|
|
|
if (tran->tra_flags & TRA_rec_version)
|
|
|
|
traMode = traReadCommitedRecVersions;
|
|
|
|
else
|
|
|
|
traMode = traReadCommited;
|
|
|
|
}
|
|
|
|
else if (tran->tra_flags & TRA_degree3) {
|
|
|
|
traMode = traConsistency;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2008-12-04 10:29:16 +01:00
|
|
|
ext_tran->start(tdbb,
|
|
|
|
tra_scope,
|
|
|
|
traMode,
|
|
|
|
tran->tra_flags & TRA_readonly,
|
2008-04-09 22:18:47 +02:00
|
|
|
tran->getLockWait() == DEFAULT_LOCK_TIMEOUT,
|
2008-12-04 10:29:16 +01:00
|
|
|
tran->getLockWait()
|
2008-04-09 22:18:47 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
conn->deleteTransaction(ext_tran);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ext_tran;
|
|
|
|
}
|
|
|
|
|
2008-11-17 13:30:28 +01:00
|
|
|
void Transaction::detachFromJrdTran()
|
|
|
|
{
|
|
|
|
if (m_scope != traCommon)
|
|
|
|
return;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-11-17 13:30:28 +01:00
|
|
|
fb_assert(m_jrdTran);
|
|
|
|
if (!m_jrdTran)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Transaction **tran_ptr = &m_jrdTran->tra_ext_common;
|
|
|
|
for (; *tran_ptr; tran_ptr = &(*tran_ptr)->m_nextTran)
|
|
|
|
{
|
|
|
|
if (*tran_ptr == this)
|
|
|
|
{
|
|
|
|
*tran_ptr = this->m_nextTran;
|
|
|
|
this->m_nextTran = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
void Transaction::jrdTransactionEnd(thread_db *tdbb, jrd_tra* transaction,
|
|
|
|
bool commit, bool retain, bool force)
|
|
|
|
{
|
2009-02-06 13:01:34 +01:00
|
|
|
Transaction* tran = transaction->tra_ext_common;
|
|
|
|
while (tran)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2009-02-06 13:01:34 +01:00
|
|
|
Transaction* next = tran->m_nextTran;
|
2008-12-04 10:29:16 +01:00
|
|
|
try
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
if (commit)
|
|
|
|
tran->commit(tdbb, retain);
|
|
|
|
else
|
|
|
|
tran->rollback(tdbb, retain);
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
catch (const Exception&)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
if (!force || commit)
|
|
|
|
throw;
|
|
|
|
|
|
|
|
// ignore rollback error
|
2008-08-27 14:20:47 +02:00
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
2008-11-17 13:30:28 +01:00
|
|
|
tran->detachFromJrdTran();
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
2009-02-06 13:01:34 +01:00
|
|
|
tran = next;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statement
|
|
|
|
|
|
|
|
Statement::Statement(Connection &conn) :
|
|
|
|
PermanentStorage(conn.getProvider()->getPool()),
|
|
|
|
m_provider(*conn.getProvider()),
|
|
|
|
m_connection(conn),
|
|
|
|
m_transaction(NULL),
|
|
|
|
m_nextFree(NULL),
|
|
|
|
m_boundReq(NULL),
|
|
|
|
m_ReqImpure(NULL),
|
|
|
|
m_nextInReq(NULL),
|
|
|
|
m_prevInReq(NULL),
|
|
|
|
m_sql(getPool()),
|
|
|
|
m_singleton(false),
|
|
|
|
m_active(false),
|
|
|
|
m_error(false),
|
|
|
|
m_allocated(false),
|
|
|
|
m_stmt_selectable(false),
|
|
|
|
m_inputs(0),
|
|
|
|
m_outputs(0),
|
2008-06-08 22:42:27 +02:00
|
|
|
m_callerPrivileges(false),
|
|
|
|
m_preparedByReq(NULL),
|
2008-04-09 22:18:47 +02:00
|
|
|
m_sqlParamNames(getPool()),
|
|
|
|
m_sqlParamsMap(getPool()),
|
|
|
|
m_in_buffer(getPool()),
|
|
|
|
m_out_buffer(getPool()),
|
|
|
|
m_inDescs(getPool()),
|
|
|
|
m_outDescs(getPool())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement::~Statement()
|
|
|
|
{
|
2008-12-04 10:29:16 +01:00
|
|
|
clearNames();
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::deleteStatement(Jrd::thread_db *tdbb, Statement *stmt)
|
|
|
|
{
|
|
|
|
stmt->deallocate(tdbb);
|
|
|
|
delete stmt;
|
|
|
|
}
|
|
|
|
|
2008-04-12 14:19:15 +02:00
|
|
|
void Statement::prepare(thread_db *tdbb, Transaction *tran, const string& sql, bool named)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
fb_assert(!m_active);
|
|
|
|
|
|
|
|
// already prepared the same non-empty statement
|
2008-12-04 10:29:16 +01:00
|
|
|
if (isAllocated() && (m_sql == sql) && (m_sql != "") &&
|
2008-06-08 22:42:27 +02:00
|
|
|
m_preparedByReq == (m_callerPrivileges ? tdbb->getRequest() : NULL))
|
2008-06-09 03:34:33 +02:00
|
|
|
{
|
2008-04-09 22:18:47 +02:00
|
|
|
return;
|
2008-06-09 03:34:33 +02:00
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
m_error = false;
|
|
|
|
m_transaction = tran;
|
|
|
|
m_sql = "";
|
2008-06-08 22:42:27 +02:00
|
|
|
m_preparedByReq = NULL;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
m_in_buffer.clear();
|
|
|
|
m_out_buffer.clear();
|
|
|
|
m_inDescs.clear();
|
|
|
|
m_outDescs.clear();
|
|
|
|
clearNames();
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
string sql2(getPool());
|
2008-04-12 14:19:15 +02:00
|
|
|
const string* readySql = &sql;
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (named && !(m_provider.getFlags() & prvNamedParams))
|
|
|
|
{
|
|
|
|
preprocess(sql, sql2);
|
|
|
|
readySql = &sql2;
|
|
|
|
}
|
|
|
|
|
2008-10-23 11:13:39 +02:00
|
|
|
doPrepare(tdbb, *readySql);
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
m_sql = sql;
|
|
|
|
m_sql.trim();
|
2008-06-08 22:42:27 +02:00
|
|
|
m_preparedByReq = m_callerPrivileges ? tdbb->getRequest() : NULL;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
void Statement::execute(thread_db* tdbb, Transaction* tran, int in_count,
|
2008-04-13 16:38:39 +02:00
|
|
|
const string* const* in_names, jrd_nod** in_params, int out_count, jrd_nod** out_params)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
fb_assert(isAllocated() && !m_stmt_selectable);
|
|
|
|
fb_assert(!m_error);
|
|
|
|
fb_assert(!m_active);
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_transaction = tran;
|
|
|
|
setInParams(tdbb, in_count, in_names, in_params);
|
|
|
|
doExecute(tdbb);
|
|
|
|
getOutParams(tdbb, out_count, out_params);
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
void Statement::open(thread_db* tdbb, Transaction* tran, int in_count,
|
2008-04-12 14:19:15 +02:00
|
|
|
const string* const* in_names, jrd_nod** in_params, bool singleton)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
fb_assert(isAllocated() && m_stmt_selectable);
|
|
|
|
fb_assert(!m_error);
|
|
|
|
fb_assert(!m_active);
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_singleton = singleton;
|
|
|
|
m_transaction = tran;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
setInParams(tdbb, in_count, in_names, in_params);
|
|
|
|
doOpen(tdbb);
|
|
|
|
m_active = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Statement::fetch(thread_db *tdbb, int out_count, jrd_nod **out_params)
|
|
|
|
{
|
|
|
|
fb_assert(isAllocated() && m_stmt_selectable);
|
|
|
|
fb_assert(!m_error);
|
|
|
|
fb_assert(m_active);
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (!doFetch(tdbb))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
getOutParams(tdbb, out_count, out_params);
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (m_singleton)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
if (doFetch(tdbb))
|
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
ISC_STATUS_ARRAY status;
|
|
|
|
Arg::Gds(isc_sing_select_err).copyTo(status);
|
2008-04-09 22:18:47 +02:00
|
|
|
raise(status, tdbb, "isc_dsql_fetch");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::close(thread_db *tdbb)
|
|
|
|
{
|
2008-11-28 00:06:48 +01:00
|
|
|
// we must stuff exception if and only if this is the first time it occurs
|
|
|
|
// once we stuff exception we must punt
|
|
|
|
|
|
|
|
const bool wasError = m_error;
|
|
|
|
bool doPunt = false;
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (isAllocated() && m_active)
|
|
|
|
{
|
|
|
|
fb_assert(isAllocated() && m_stmt_selectable);
|
2008-11-28 00:06:48 +01:00
|
|
|
try {
|
|
|
|
doClose(tdbb, false);
|
|
|
|
}
|
2008-12-03 02:05:53 +01:00
|
|
|
catch (const Exception& ex)
|
2008-11-28 00:06:48 +01:00
|
|
|
{
|
2008-12-03 02:05:53 +01:00
|
|
|
if (!doPunt && !wasError)
|
2008-11-28 00:06:48 +01:00
|
|
|
{
|
|
|
|
doPunt = true;
|
|
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
m_active = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_boundReq) {
|
|
|
|
unBindFromRequest();
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (m_transaction && m_transaction->getScope() == traAutonomous)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-11-28 00:06:48 +01:00
|
|
|
bool commitFailed = false;
|
2008-04-09 22:18:47 +02:00
|
|
|
if (!m_error) {
|
|
|
|
try {
|
|
|
|
m_transaction->commit(tdbb, false);
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
catch (const Exception& ex)
|
2008-11-28 00:06:48 +01:00
|
|
|
{
|
2008-04-09 22:18:47 +02:00
|
|
|
commitFailed = true;
|
2008-12-04 10:29:16 +01:00
|
|
|
if (!doPunt && !wasError)
|
2008-11-28 00:06:48 +01:00
|
|
|
{
|
|
|
|
doPunt = true;
|
|
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (m_error || commitFailed) {
|
|
|
|
try {
|
|
|
|
m_transaction->rollback(tdbb, false);
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
catch (const Exception& ex)
|
2008-11-28 00:06:48 +01:00
|
|
|
{
|
|
|
|
if (!doPunt && !wasError)
|
|
|
|
{
|
|
|
|
doPunt = true;
|
|
|
|
stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
2008-11-28 00:06:48 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
m_error = false;
|
|
|
|
m_transaction = NULL;
|
2008-11-28 00:06:48 +01:00
|
|
|
m_connection.releaseStatement(tdbb, this);
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (doPunt) {
|
2008-04-09 22:18:47 +02:00
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::deallocate(thread_db *tdbb)
|
|
|
|
{
|
2008-11-28 00:06:48 +01:00
|
|
|
if (isAllocated()) {
|
|
|
|
try {
|
|
|
|
doClose(tdbb, true);
|
|
|
|
}
|
2008-12-03 02:05:53 +01:00
|
|
|
catch (const Exception&) {
|
2008-11-28 00:06:48 +01:00
|
|
|
// ignore
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
fb_assert(!isAllocated());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-17 11:58:24 +01:00
|
|
|
enum TokenType {ttNone, ttWhite, ttComment, ttBrokenComment, ttString, ttParamMark, ttIdent, ttOther};
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
static TokenType getToken(const char **begin, const char *end)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-04-10 21:41:30 +02:00
|
|
|
TokenType ret = ttNone;
|
2008-04-09 22:18:47 +02:00
|
|
|
const char *p = *begin;
|
|
|
|
|
|
|
|
char c = *p++;
|
|
|
|
switch (c)
|
|
|
|
{
|
2008-12-04 10:29:16 +01:00
|
|
|
case ':':
|
2008-04-09 22:18:47 +02:00
|
|
|
case '?':
|
|
|
|
ret = ttParamMark;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
while (p < end)
|
2008-04-13 16:38:39 +02:00
|
|
|
{
|
2008-04-13 12:03:56 +02:00
|
|
|
if (*p++ == c) {
|
2008-04-09 22:18:47 +02:00
|
|
|
ret = ttString;
|
|
|
|
break;
|
|
|
|
}
|
2008-04-13 16:38:39 +02:00
|
|
|
}
|
|
|
|
break;
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
case '/':
|
2008-12-04 10:29:16 +01:00
|
|
|
if (p < end && *p == '*')
|
2008-04-13 12:03:56 +02:00
|
|
|
{
|
|
|
|
ret = ttBrokenComment;
|
|
|
|
p++;
|
2008-04-09 22:18:47 +02:00
|
|
|
while (p < end) {
|
|
|
|
if (*p++ == '*' && p < end && *p == '/') {
|
|
|
|
p++;
|
|
|
|
ret = ttComment;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = ttOther;
|
|
|
|
}
|
2008-04-13 16:38:39 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
case '-':
|
|
|
|
if (p < end && *p == '-') {
|
|
|
|
while (p < end) {
|
|
|
|
if (*p++ == '\n') {
|
|
|
|
p--;
|
|
|
|
ret = ttComment;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = ttOther;
|
|
|
|
}
|
2008-04-13 16:38:39 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
if (classes(c) & CHR_DIGIT)
|
|
|
|
{
|
|
|
|
while (p < end && classes(*p) & CHR_DIGIT)
|
|
|
|
p++;
|
|
|
|
ret = ttOther;
|
|
|
|
}
|
|
|
|
else if (classes(c) & CHR_IDENT)
|
|
|
|
{
|
|
|
|
while (p < end && classes(*p) & CHR_IDENT)
|
|
|
|
p++;
|
|
|
|
ret = ttIdent;
|
|
|
|
}
|
|
|
|
else if (classes(c) & CHR_WHITE)
|
|
|
|
{
|
|
|
|
while (p < end && (classes(*p) & CHR_WHITE))
|
|
|
|
*p++;
|
|
|
|
ret = ttWhite;
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
else
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
c = *p;
|
2008-12-04 10:29:16 +01:00
|
|
|
while (p < end && !( classes(c) & (CHR_DIGIT | CHR_IDENT | CHR_WHITE) ) &&
|
2008-04-09 22:18:47 +02:00
|
|
|
c != '/' && c != '-' && c != ':' && c != '?' && c != '\'' && c != '"')
|
|
|
|
{
|
|
|
|
c = *p++;
|
|
|
|
}
|
|
|
|
ret = ttOther;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*begin = p;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-04-12 14:19:15 +02:00
|
|
|
void Statement::preprocess(const string& sql, string& ret)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
bool passAsIs = true, execBlock = false;
|
|
|
|
const char *p = sql.begin(), *end = sql.end();
|
2008-04-12 14:19:15 +02:00
|
|
|
const char* start = p;
|
2008-04-10 21:41:30 +02:00
|
|
|
TokenType tok = getToken(&p, end);
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-04-12 14:19:15 +02:00
|
|
|
const char* i = start;
|
2008-12-04 10:29:16 +01:00
|
|
|
while (p < end && (tok == ttComment || tok == ttWhite))
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
i = p;
|
|
|
|
tok = getToken(&p, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p >= end || tok != ttIdent) {
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement preprocess SQL error
|
|
|
|
// Statement expected
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_eds_preprocess) <<
|
|
|
|
Arg::Gds(isc_eds_stmt_expected));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
start = i; // skip leading comments ??
|
|
|
|
string ident(i, p - i);
|
|
|
|
ident.upper();
|
|
|
|
|
|
|
|
if (ident == "EXECUTE")
|
|
|
|
{
|
|
|
|
const char *i2 = p;
|
|
|
|
tok = getToken(&p, end);
|
2008-12-04 10:29:16 +01:00
|
|
|
while (p < end && (tok == ttComment || tok == ttWhite))
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
i2 = p;
|
|
|
|
tok = getToken(&p, end);
|
|
|
|
}
|
|
|
|
if (p >= end || tok != ttIdent) {
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement preprocess SQL error
|
|
|
|
// Statement expected
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_eds_preprocess) <<
|
|
|
|
Arg::Gds(isc_eds_stmt_expected));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
string ident2(i2, p - i2);
|
|
|
|
ident2.upper();
|
|
|
|
|
|
|
|
execBlock = (ident2 == "BLOCK");
|
|
|
|
passAsIs = false;
|
|
|
|
}
|
|
|
|
else {
|
2008-12-04 10:29:16 +01:00
|
|
|
passAsIs = !(ident == "INSERT" || ident == "UPDATE" || ident == "DELETE" ||
|
2008-08-29 04:18:50 +02:00
|
|
|
ident == "MERGE" || ident == "SELECT" || ident == "WITH");
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (passAsIs)
|
|
|
|
{
|
|
|
|
ret = sql;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret += string(start, p - start);
|
|
|
|
|
|
|
|
while (p < end)
|
|
|
|
{
|
|
|
|
start = p;
|
|
|
|
tok = getToken(&p, end);
|
|
|
|
switch (tok)
|
|
|
|
{
|
|
|
|
case ttParamMark:
|
|
|
|
tok = getToken(&p, end);
|
|
|
|
if (tok == ttIdent /*|| tok == ttString*/)
|
|
|
|
{
|
|
|
|
// hvlad: TODO check quoted param names
|
|
|
|
ident.assign(start + 1, p - start - 1);
|
|
|
|
if (tok == ttIdent)
|
|
|
|
ident.upper();
|
|
|
|
|
2008-12-30 11:52:52 +01:00
|
|
|
size_t n = 0;
|
2008-04-09 22:18:47 +02:00
|
|
|
for (; n < m_sqlParamNames.getCount(); n++)
|
2008-04-12 14:19:15 +02:00
|
|
|
{
|
2008-04-09 22:18:47 +02:00
|
|
|
if ((*m_sqlParamNames[n]) == ident)
|
|
|
|
break;
|
2008-04-12 14:19:15 +02:00
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
if (n >= m_sqlParamNames.getCount())
|
|
|
|
{
|
|
|
|
n = m_sqlParamNames.getCount();
|
|
|
|
m_sqlParamNames.add(FB_NEW(getPool()) string(getPool(), ident));
|
|
|
|
}
|
|
|
|
m_sqlParamsMap.add(m_sqlParamNames[n]);
|
|
|
|
}
|
|
|
|
else {
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement preprocess SQL error
|
|
|
|
// Parameter name expected
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_eds_preprocess) <<
|
|
|
|
Arg::Gds(isc_eds_prm_name_expected));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
ret += '?';
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
case ttIdent:
|
|
|
|
if (execBlock)
|
|
|
|
{
|
|
|
|
ident.assign(start, p - start);
|
|
|
|
ident.upper();
|
|
|
|
if (ident == "AS")
|
|
|
|
{
|
|
|
|
ret += string(start, end - start);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// fall thru
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
case ttWhite:
|
|
|
|
case ttComment:
|
|
|
|
case ttString:
|
|
|
|
case ttOther:
|
|
|
|
ret += string(start, p - start);
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-04-13 12:03:56 +02:00
|
|
|
case ttBrokenComment:
|
|
|
|
{
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement preprocess SQL error
|
|
|
|
// Unclosed comment found near ''@1''
|
2008-08-27 14:20:47 +02:00
|
|
|
string s(start, MIN(16, end - start));
|
|
|
|
ERR_post(Arg::Gds(isc_eds_preprocess) <<
|
|
|
|
Arg::Gds(isc_eds_unclosed_comment) << Arg::Str(s));
|
2008-04-13 12:03:56 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
case ttNone:
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement preprocess SQL error
|
2008-08-27 14:20:47 +02:00
|
|
|
ERR_post(Arg::Gds(isc_eds_preprocess));
|
2008-04-10 21:41:30 +02:00
|
|
|
break;
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-04-13 16:38:39 +02:00
|
|
|
void Statement::setInParams(thread_db* tdbb, int count, const string* const* names, jrd_nod** params)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2009-01-22 17:53:10 +01:00
|
|
|
m_error = (names && ((int) m_sqlParamNames.getCount() != count || !count)) ||
|
2008-04-09 22:18:47 +02:00
|
|
|
(!names && m_sqlParamNames.getCount());
|
|
|
|
|
|
|
|
if (m_error) {
|
2008-06-22 09:34:36 +02:00
|
|
|
// Input parameters mismatch
|
2008-07-03 14:02:54 +02:00
|
|
|
status_exception::raise(Arg::Gds(isc_eds_input_prm_mismatch));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_sqlParamNames.getCount())
|
|
|
|
{
|
|
|
|
const int sqlCount = m_sqlParamsMap.getCount();
|
|
|
|
Array<jrd_nod*> sqlParamsArray(getPool(), 16);
|
|
|
|
jrd_nod **sqlParams = sqlParamsArray.getBuffer(sqlCount);
|
|
|
|
|
|
|
|
for (int sqlNum = 0; sqlNum < sqlCount; sqlNum++)
|
|
|
|
{
|
|
|
|
const string* sqlName = m_sqlParamsMap[sqlNum];
|
|
|
|
|
|
|
|
int num = 0;
|
|
|
|
for (; num < count; num++)
|
2008-04-10 21:41:30 +02:00
|
|
|
{
|
2008-04-09 22:18:47 +02:00
|
|
|
if (*names[num] == *sqlName)
|
|
|
|
break;
|
2008-04-10 21:41:30 +02:00
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (num == count)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-06-22 09:34:36 +02:00
|
|
|
// Input parameter ''@1'' have no value set
|
2008-07-03 14:02:54 +02:00
|
|
|
status_exception::raise(Arg::Gds(isc_eds_input_prm_not_set) << Arg::Str(*sqlName));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sqlParams[sqlNum] = params[num];
|
|
|
|
}
|
|
|
|
|
|
|
|
doSetInParams(tdbb, sqlCount, m_sqlParamsMap.begin(), sqlParams);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
doSetInParams(tdbb, count, names, params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-13 16:38:39 +02:00
|
|
|
void Statement::doSetInParams(thread_db* tdbb, int count, const string* const* names, jrd_nod** params)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-12-04 10:29:16 +01:00
|
|
|
if (count != getInputs())
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
m_error = true;
|
2008-06-22 09:34:36 +02:00
|
|
|
// Input parameters mismatch
|
2008-07-03 14:02:54 +02:00
|
|
|
status_exception::raise(Arg::Gds(isc_eds_input_prm_mismatch));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!count)
|
|
|
|
return;
|
|
|
|
|
2008-04-12 14:19:15 +02:00
|
|
|
jrd_nod** jrdVar = params;
|
2008-04-09 22:18:47 +02:00
|
|
|
GenericMap<Pair<NonPooled<jrd_nod*, dsc*> > > paramDescs(getPool());
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
for (int i = 0; i < count; i++, jrdVar++)
|
|
|
|
{
|
2008-04-10 21:41:30 +02:00
|
|
|
dsc *src = NULL;
|
|
|
|
dsc &dst = m_inDescs[i * 2];
|
|
|
|
dsc &null = m_inDescs[i * 2 + 1];
|
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (!paramDescs.get(*jrdVar, src))
|
|
|
|
{
|
|
|
|
src = EVL_expr(tdbb, *jrdVar);
|
|
|
|
paramDescs.put(*jrdVar, src);
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool srcNull = !src || src->isNull();
|
|
|
|
*((SSHORT*) null.dsc_address) = (srcNull ? -1 : 0);
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (srcNull)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
dst.setNull();
|
|
|
|
memset(dst.dsc_address, 0, dst.dsc_length);
|
|
|
|
}
|
2008-12-04 10:29:16 +01:00
|
|
|
else if (dst.isBlob())
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
dsc srcBlob;
|
|
|
|
srcBlob.clear();
|
|
|
|
ISC_QUAD srcBlobID;
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (src->isBlob())
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
srcBlob.makeBlob(src->getBlobSubType(), src->getTextType(), &srcBlobID);
|
|
|
|
memmove(srcBlob.dsc_address, src->dsc_address, src->dsc_length);
|
2008-12-04 10:29:16 +01:00
|
|
|
}
|
|
|
|
else
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
srcBlob.makeBlob(dst.getBlobSubType(), dst.getTextType(), &srcBlobID);
|
|
|
|
MOV_move(tdbb, src, &srcBlob);
|
|
|
|
}
|
|
|
|
|
|
|
|
putExtBlob(tdbb, srcBlob, dst);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
MOV_move(tdbb, src, &dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
// m_outDescs -> jrd_nod
|
2008-04-09 22:18:47 +02:00
|
|
|
void Statement::getOutParams(thread_db *tdbb, int count, jrd_nod **params)
|
|
|
|
{
|
|
|
|
if (count != getOutputs())
|
|
|
|
{
|
|
|
|
m_error = true;
|
2008-06-22 09:34:36 +02:00
|
|
|
// Output parameters mismatch
|
2008-07-03 14:02:54 +02:00
|
|
|
status_exception::raise(Arg::Gds(isc_eds_output_prm_mismatch));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
jrd_nod **jrdVar = params;
|
|
|
|
for (int i = 0; i < count; i++, jrdVar++)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
dsc* d = EVL_assign_to(tdbb, *jrdVar);
|
2008-12-04 10:29:16 +01:00
|
|
|
if (d->dsc_dtype >= FB_NELEM(sqlType) || sqlType[d->dsc_dtype] < 0)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
m_error = true;
|
|
|
|
status_exception::raise(
|
2008-07-03 14:02:54 +02:00
|
|
|
Arg::Gds(isc_exec_sql_invalid_var) << Arg::Num(i + 1) << Arg::Str(m_sql.substr(0, 31)));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// build the src descriptor
|
2008-04-13 10:11:16 +02:00
|
|
|
dsc &src = m_outDescs[i * 2];
|
2008-04-10 21:41:30 +02:00
|
|
|
const dsc &null = m_outDescs[i * 2 + 1];
|
2008-04-13 10:11:16 +02:00
|
|
|
dsc* local = &src;
|
2008-04-09 22:18:47 +02:00
|
|
|
dsc localDsc;
|
|
|
|
bid localBlobID;
|
|
|
|
|
|
|
|
const bool srcNull = (*(SSHORT*) null.dsc_address) == -1;
|
2008-12-04 10:29:16 +01:00
|
|
|
if (src.isBlob() && !srcNull)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
localDsc = src;
|
|
|
|
localDsc.dsc_address = (UCHAR*) &localBlobID;
|
|
|
|
getExtBlob(tdbb, src, localDsc);
|
|
|
|
local = &localDsc;
|
2008-12-04 10:29:16 +01:00
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
|
|
|
|
// and assign to the target
|
2008-04-13 10:11:16 +02:00
|
|
|
EXE_assignment(tdbb, *jrdVar, local, srcNull, NULL, NULL);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
// read external blob (src), store it as temporary local blob and put local blob_id into dst
|
2008-04-09 22:18:47 +02:00
|
|
|
void Statement::getExtBlob(thread_db *tdbb, const dsc &src, dsc &dst)
|
|
|
|
{
|
|
|
|
blb *destBlob = NULL;
|
2008-04-11 03:38:50 +02:00
|
|
|
AutoPtr<Blob> extBlob(m_connection.createBlob());
|
2008-04-09 22:18:47 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
extBlob->open(tdbb, *m_transaction, src, NULL);
|
|
|
|
|
|
|
|
jrd_req* request = tdbb->getRequest();
|
|
|
|
const UCHAR bpb[] = {isc_bpb_version1, isc_bpb_storage, isc_bpb_storage_temp};
|
|
|
|
bid *localBlobID = (bid*) dst.dsc_address;
|
|
|
|
destBlob = BLB_create2(tdbb, request->req_transaction, localBlobID, sizeof(bpb), bpb);
|
|
|
|
|
|
|
|
// hvlad ?
|
|
|
|
destBlob->blb_sub_type = src.getBlobSubType();
|
|
|
|
destBlob->blb_charset = src.getCharSet();
|
|
|
|
|
|
|
|
Firebird::Array<char> buffer;
|
|
|
|
const int bufSize = 32 * 1024 - 2/*input->blb_max_segment*/;
|
|
|
|
char* buff = buffer.getBuffer(bufSize);
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
while (true)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
const USHORT length = extBlob->read(tdbb, buff, bufSize);
|
2008-12-04 10:29:16 +01:00
|
|
|
if (!length)
|
2008-04-09 22:18:47 +02:00
|
|
|
break;
|
|
|
|
|
2008-04-13 10:11:16 +02:00
|
|
|
BLB_put_segment(tdbb, destBlob, reinterpret_cast<const UCHAR*>(buff), length);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
extBlob->close(tdbb);
|
|
|
|
BLB_close(tdbb, destBlob);
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
extBlob->close(tdbb);
|
|
|
|
if (destBlob) {
|
|
|
|
BLB_cancel(tdbb, destBlob);
|
|
|
|
}
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// read local blob, store it as external blob and put external blob_id in dst
|
2008-04-13 10:11:16 +02:00
|
|
|
void Statement::putExtBlob(thread_db *tdbb, dsc &src, dsc &dst)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
blb* srcBlob = NULL;
|
2008-04-11 03:38:50 +02:00
|
|
|
AutoPtr<Blob> extBlob(m_connection.createBlob());
|
2008-04-09 22:18:47 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
extBlob->create(tdbb, *m_transaction, dst, NULL);
|
2008-12-04 10:29:16 +01:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
jrd_req* request = tdbb->getRequest();
|
|
|
|
bid *srcBid = (bid*) src.dsc_address;
|
|
|
|
|
|
|
|
UCharBuffer bpb;
|
|
|
|
BLB_gen_bpb_from_descs(&src, &dst, bpb);
|
|
|
|
srcBlob = BLB_open2(tdbb, request->req_transaction, srcBid, bpb.getCount(), bpb.begin());
|
|
|
|
|
|
|
|
Firebird::HalfStaticArray<char, 2048> buffer;
|
|
|
|
const int bufSize = srcBlob->blb_max_segment;
|
|
|
|
char* buff = buffer.getBuffer(bufSize);
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
while (true)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
2008-04-13 10:11:16 +02:00
|
|
|
USHORT length = BLB_get_segment(tdbb, srcBlob, reinterpret_cast<UCHAR*>(buff), srcBlob->blb_max_segment);
|
2008-04-09 22:18:47 +02:00
|
|
|
if (srcBlob->blb_flags & BLB_eof) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
extBlob->write(tdbb, buff, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
BLB_close(tdbb, srcBlob);
|
|
|
|
extBlob->close(tdbb);
|
|
|
|
}
|
|
|
|
catch (const Exception&)
|
|
|
|
{
|
|
|
|
extBlob->cancel(tdbb);
|
|
|
|
if (srcBlob) {
|
|
|
|
BLB_close(tdbb, srcBlob);
|
|
|
|
}
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::clearNames()
|
|
|
|
{
|
|
|
|
string **s = m_sqlParamNames.begin(), **end = m_sqlParamNames.end();
|
|
|
|
for (; s < end; s++)
|
|
|
|
{
|
|
|
|
delete *s;
|
|
|
|
*s = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sqlParamNames.clear();
|
|
|
|
m_sqlParamsMap.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
void Statement::raise(ISC_STATUS* status, thread_db *tdbb, const char* sWhere,
|
2008-04-09 22:18:47 +02:00
|
|
|
const string* sQuery)
|
|
|
|
{
|
|
|
|
m_error = true;
|
|
|
|
|
2008-06-19 12:45:18 +02:00
|
|
|
string rem_err;
|
2008-04-09 22:18:47 +02:00
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
m_provider.getRemoteError(status, rem_err);
|
|
|
|
|
|
|
|
if (status == tdbb->tdbb_status_vector)
|
|
|
|
{
|
2008-08-27 14:20:47 +02:00
|
|
|
fb_utils::init_status(status);
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-19 12:45:18 +02:00
|
|
|
// Execute statement error at @1 :\n@2Statement : @3\nData source : @4
|
2008-12-04 10:29:16 +01:00
|
|
|
ERR_post(Arg::Gds(isc_eds_statement) << Arg::Str(sWhere) <<
|
|
|
|
Arg::Str(rem_err) <<
|
|
|
|
Arg::Str(sQuery ? sQuery->substr(0, 255) : m_sql.substr(0, 255)) <<
|
2008-08-27 14:20:47 +02:00
|
|
|
Arg::Str(m_connection.getDataSourceName()));
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::bindToRequest(jrd_req* request, Statement** impure)
|
|
|
|
{
|
|
|
|
fb_assert(!m_boundReq);
|
|
|
|
fb_assert(!m_prevInReq);
|
|
|
|
fb_assert(!m_nextInReq);
|
|
|
|
|
2008-12-04 10:29:16 +01:00
|
|
|
if (request->req_ext_stmt)
|
2008-04-09 22:18:47 +02:00
|
|
|
{
|
|
|
|
this->m_nextInReq = request->req_ext_stmt;
|
|
|
|
request->req_ext_stmt->m_prevInReq = this;
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
request->req_ext_stmt = this;
|
|
|
|
m_boundReq = request;
|
|
|
|
m_ReqImpure = impure;
|
|
|
|
*m_ReqImpure = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Statement::unBindFromRequest()
|
|
|
|
{
|
|
|
|
fb_assert(m_boundReq);
|
|
|
|
fb_assert(*m_ReqImpure == this);
|
|
|
|
|
|
|
|
if (m_boundReq->req_ext_stmt == this)
|
|
|
|
m_boundReq->req_ext_stmt = m_nextInReq;
|
|
|
|
|
|
|
|
if (m_nextInReq)
|
|
|
|
m_nextInReq->m_prevInReq = this->m_prevInReq;
|
|
|
|
|
|
|
|
if (m_prevInReq)
|
|
|
|
m_prevInReq->m_nextInReq = this->m_nextInReq;
|
|
|
|
|
|
|
|
*m_ReqImpure = NULL;
|
|
|
|
m_ReqImpure = NULL;
|
|
|
|
m_boundReq = NULL;
|
|
|
|
m_prevInReq = m_nextInReq = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EngineCallbackGuard
|
|
|
|
|
|
|
|
void EngineCallbackGuard::init(thread_db *tdbb, Connection &conn)
|
|
|
|
{
|
|
|
|
m_tdbb = tdbb;
|
|
|
|
m_mutex = conn.isConnected() ? &conn.m_mutex : &conn.m_provider.m_mutex;
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (m_tdbb)
|
|
|
|
{
|
|
|
|
if (m_tdbb->getTransaction()) {
|
|
|
|
m_tdbb->getTransaction()->tra_callback_count++;
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (m_tdbb->getAttachment()) {
|
|
|
|
fb_assert(!m_tdbb->getAttachment()->att_ext_connection);
|
|
|
|
m_tdbb->getAttachment()->att_ext_connection = &conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_tdbb->getDatabase()->dbb_sync->unlock();
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-04-09 22:18:47 +02:00
|
|
|
if (m_mutex) {
|
|
|
|
m_mutex->enter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EngineCallbackGuard::~EngineCallbackGuard()
|
|
|
|
{
|
|
|
|
if (m_mutex) {
|
|
|
|
m_mutex->leave();
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (m_tdbb)
|
|
|
|
{
|
|
|
|
m_tdbb->getDatabase()->dbb_sync->lock();
|
|
|
|
|
|
|
|
if (m_tdbb->getTransaction()) {
|
|
|
|
m_tdbb->getTransaction()->tra_callback_count--;
|
|
|
|
}
|
2008-04-10 21:41:30 +02:00
|
|
|
|
2008-11-28 00:06:48 +01:00
|
|
|
if (m_tdbb->getAttachment()) {
|
|
|
|
m_tdbb->getAttachment()->att_ext_connection = NULL;
|
|
|
|
}
|
2008-04-09 22:18:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-10 21:41:30 +02:00
|
|
|
} // namespace EDS
|