8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-29 06:43:03 +01:00
firebird-mirror/src/jrd/extds/InternalDS.cpp
Roman Simakov 238fff3a2d Fixed CORE-1815: Ability to grant role to another role (#23)
* Initial patch for cumulative roles

* Fixed multiple records in USER_PRIVILEGES and reworked logic on additional grant default role and admin option
2016-05-12 17:03:54 +03:00

788 lines
18 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) 2008 Vlad Khorsun <hvlad@users.sourceforge.net>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "fb_types.h"
#include "../../include/fb_blk.h"
#include "../align.h"
#include "../exe.h"
#include "../jrd.h"
#include "../tra.h"
#include "../common/dsc.h"
#include "../../dsql/dsql.h"
#include "../../dsql/sqlda_pub.h"
#include "../blb_proto.h"
#include "../evl_proto.h"
#include "../exe_proto.h"
#include "../mov_proto.h"
#include "../mov_proto.h"
#include "../PreparedStatement.h"
#include "../Function.h"
#include "InternalDS.h"
#include "ValidatePassword.h"
using namespace Jrd;
using namespace Firebird;
namespace EDS {
const char* INTERNAL_PROVIDER_NAME = "Internal";
class RegisterInternalProvider
{
public:
RegisterInternalProvider(MemoryPool&)
{
InternalProvider* provider = FB_NEW InternalProvider(INTERNAL_PROVIDER_NAME);
Manager::addProvider(provider);
}
};
static GlobalPtr<RegisterInternalProvider> reg;
// InternalProvider
void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Jrd::Attachment* att)
{
/***
hvlad: this inactive code could be useful in the future, for example when EDS
connection pool will be implemented - it allows to remove closed connections
from the pool.
if (m_connections.getCount() == 0)
return;
Connection** ptr = m_connections.end();
Connection** begin = m_connections.begin();
for (ptr--; ptr >= begin; ptr--)
{
InternalConnection* conn = (InternalConnection*) *ptr;
if (conn->getJrdAtt() == att->getInterface())
releaseConnection(tdbb, *conn, false);
}
***/
}
void InternalProvider::getRemoteError(const FbStatusVector* status, string& err) const
{
err = "";
char buff[1024];
const ISC_STATUS* p = status->getErrors();
for (;;)
{
const ISC_STATUS* code = p + 1;
if (!fb_interpret(buff, sizeof(buff), &p))
break;
string rem_err;
rem_err.printf("%lu : %s\n", *code, buff);
err += rem_err;
}
}
Connection* InternalProvider::doCreateConnection()
{
return FB_NEW InternalConnection(*this);
}
// InternalConnection
InternalConnection::~InternalConnection()
{
}
// Status helper
class IntStatus : public Jrd::FbLocalStatus
{
public:
explicit IntStatus(FbStatusVector *p)
: FbLocalStatus(), v(p)
{}
~IntStatus()
{
if (v)
fb_utils::copyStatus(v, &(*this));
}
private:
FbStatusVector *v;
};
void InternalConnection::attach(thread_db* tdbb, const PathName& dbName,
const MetaName& user, const string& pwd,
const MetaName& role)
{
fb_assert(!m_attachment);
Database* dbb = tdbb->getDatabase();
fb_assert(dbName.isEmpty() || dbName == dbb->dbb_database_name.c_str());
// Don't wrap raised errors. This is needed for backward compatibility.
setWrapErrors(false);
Jrd::Attachment* attachment = tdbb->getAttachment();
if ((user.isEmpty() || user == attachment->att_user->usr_user_name) &&
pwd.isEmpty() &&
(role.isEmpty() || role == attachment->att_user->usr_sql_role_name))
{
m_isCurrent = true;
m_attachment = attachment->getInterface();
}
else
{
m_isCurrent = false;
m_dbName = dbb->dbb_database_name.c_str();
generateDPB(tdbb, m_dpb, user, pwd, role);
// Avoid change of m_dpb by validatePassword() below
ClumpletWriter newDpb(m_dpb);
validatePassword(tdbb, m_dbName, newDpb);
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
RefPtr<JProvider> jInstance(JProvider::getInstance());
jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback);
m_attachment.assignRefNoIncr(jInstance->attachDatabase(&status, m_dbName.c_str(),
newDpb.getBufferLength(), newDpb.getBuffer()));
}
if (status->getState() & IStatus::STATE_ERRORS)
raise(&status, tdbb, "JProvider::attach");
}
m_sqlDialect = (m_attachment->getHandle()->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
SQL_DIALECT_V6 : SQL_DIALECT_V5;
}
void InternalConnection::doDetach(thread_db* tdbb)
{
fb_assert(m_attachment);
if (!m_attachment->getHandle())
return;
if (m_isCurrent)
{
m_attachment = NULL;
}
else
{
FbLocalStatus status;
RefPtr<JAttachment> att = m_attachment;
m_attachment = NULL;
{ // scope
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
att->detach(&status);
}
if (status->getErrors()[1] == isc_att_shutdown)
{
status->init();
}
if (status->getState() & IStatus::STATE_ERRORS)
{
m_attachment = att;
raise(&status, tdbb, "JAttachment::detach");
}
}
fb_assert(!m_attachment);
}
bool InternalConnection::cancelExecution()
{
if (!m_attachment->getHandle())
return false;
if (m_isCurrent)
return true;
FbLocalStatus status;
m_attachment->cancelOperation(&status, fb_cancel_raise);
return !(status->getState() & IStatus::STATE_ERRORS);
}
// this internal connection instance is available for the current execution context if it
// a) is current connection and current thread's attachment is equal to
// this attachment, or
// b) is not current connection
bool InternalConnection::isAvailable(thread_db* tdbb, TraScope /*traScope*/) const
{
return !m_isCurrent ||
(m_isCurrent && (tdbb->getAttachment() == m_attachment->getHandle()));
}
bool InternalConnection::isSameDatabase(thread_db* tdbb, const PathName& dbName,
const MetaName& user, const string& pwd,
const MetaName& role) const
{
if (m_isCurrent)
{
const UserId* attUser = m_attachment->getHandle()->att_user;
return ((user.isEmpty() || user == attUser->usr_user_name) &&
pwd.isEmpty() &&
(role.isEmpty() || role == attUser->usr_sql_role_name));
}
return Connection::isSameDatabase(tdbb, dbName, user, pwd, role);
}
Transaction* InternalConnection::doCreateTransaction()
{
return FB_NEW InternalTransaction(*this);
}
Statement* InternalConnection::doCreateStatement()
{
return FB_NEW InternalStatement(*this);
}
Blob* InternalConnection::createBlob()
{
return FB_NEW InternalBlob(*this);
}
// InternalTransaction()
void InternalTransaction::doStart(FbStatusVector* status, thread_db* tdbb, ClumpletWriter& tpb)
{
fb_assert(!m_transaction);
jrd_tra* localTran = tdbb->getTransaction();
fb_assert(localTran);
if (m_scope == traCommon && m_IntConnection.isCurrent())
m_transaction = localTran->getInterface();
else
{
JAttachment* att = m_IntConnection.getJrdAtt();
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
IntStatus s(status);
m_transaction.assignRefNoIncr(
att->startTransaction(&s, tpb.getBufferLength(), tpb.getBuffer()));
if (m_transaction)
m_transaction->getHandle()->tra_callback_count = localTran->tra_callback_count;
}
}
void InternalTransaction::doPrepare(FbStatusVector* /*status*/, thread_db* /*tdbb*/,
int /*info_len*/, const char* /*info*/)
{
fb_assert(m_transaction);
fb_assert(false);
}
void InternalTransaction::doCommit(FbStatusVector* status, thread_db* tdbb, bool retain)
{
fb_assert(m_transaction);
if (m_scope == traCommon && m_IntConnection.isCurrent())
{
if (!retain) {
m_transaction = NULL;
}
}
else
{
IntStatus s(status);
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
if (retain)
m_transaction->commitRetaining(&s);
else
m_transaction->commit(&s);
}
}
void InternalTransaction::doRollback(FbStatusVector* status, thread_db* tdbb, bool retain)
{
fb_assert(m_transaction);
if (m_scope == traCommon && m_IntConnection.isCurrent())
{
if (!retain) {
m_transaction = NULL;
}
}
else
{
IntStatus s(status);
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
if (retain)
m_transaction->rollbackRetaining(&s);
else
m_transaction->rollback(&s);
}
if (status->getErrors()[1] == isc_att_shutdown && !retain)
{
m_transaction = NULL;
status->init();
}
}
// InternalStatement
InternalStatement::InternalStatement(InternalConnection& conn) :
Statement(conn),
m_intConnection(conn),
m_intTransaction(0),
m_request(0),
m_cursor(0),
m_inMetadata(FB_NEW MsgMetadata),
m_outMetadata(FB_NEW MsgMetadata)
{
}
InternalStatement::~InternalStatement()
{
}
void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
{
m_inMetadata->reset();
m_outMetadata->reset();
JAttachment* att = m_intConnection.getJrdAtt();
JTransaction* tran = getIntTransaction()->getJrdTran();
FbLocalStatus status;
if (m_request)
{
doClose(tdbb, true);
fb_assert(!m_allocated);
}
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
CallerName save_caller_name(tran->getHandle()->tra_caller_name);
if (m_callerPrivileges)
{
jrd_req* request = tdbb->getRequest();
JrdStatement* statement = request ? request->getStatement() : NULL;
CallerName callerName;
const Routine* routine;
if (statement && statement->parentStatement)
statement = statement->parentStatement;
if (statement && statement->triggerName.hasData())
tran->getHandle()->tra_caller_name = CallerName(obj_trigger, statement->triggerName);
else if (statement && (routine = statement->getRoutine()) &&
routine->getName().identifier.hasData())
{
if (routine->getName().package.isEmpty())
{
tran->getHandle()->tra_caller_name = CallerName(routine->getObjectType(),
routine->getName().identifier);
}
else
{
tran->getHandle()->tra_caller_name = CallerName(obj_package_header,
routine->getName().package);
}
}
else
tran->getHandle()->tra_caller_name = CallerName();
}
m_request.assignRefNoIncr(att->prepare(&status, tran, sql.length(), sql.c_str(),
m_connection.getSqlDialect(), 0));
m_allocated = (m_request != NULL);
tran->getHandle()->tra_caller_name = save_caller_name;
}
if (status->getState() & IStatus::STATE_ERRORS)
raise(&status, tdbb, "JAttachment::prepare", &sql);
const DsqlCompiledStatement* statement = m_request->getHandle()->getStatement();
if (statement->getSendMsg())
{
try
{
PreparedStatement::parseDsqlMessage(statement->getSendMsg(), m_inDescs,
m_inMetadata, m_in_buffer);
m_inputs = m_inMetadata->getCount();
}
catch (const Exception&)
{
raise(tdbb->tdbb_status_vector, tdbb, "parse input message", &sql);
}
}
else
m_inputs = 0;
if (statement->getReceiveMsg())
{
try
{
PreparedStatement::parseDsqlMessage(statement->getReceiveMsg(), m_outDescs,
m_outMetadata, m_out_buffer);
m_outputs = m_outMetadata->getCount();
}
catch (const Exception&)
{
raise(tdbb->tdbb_status_vector, tdbb, "parse output message", &sql);
}
}
else
m_outputs = 0;
m_stmt_selectable = false;
switch (statement->getType())
{
case DsqlCompiledStatement::TYPE_SELECT:
case DsqlCompiledStatement::TYPE_SELECT_UPD:
case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
m_stmt_selectable = true;
break;
case DsqlCompiledStatement::TYPE_START_TRANS:
case DsqlCompiledStatement::TYPE_COMMIT:
case DsqlCompiledStatement::TYPE_ROLLBACK:
case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
case DsqlCompiledStatement::TYPE_CREATE_DB:
Arg::Gds(isc_eds_expl_tran_ctrl).copyTo(&status);
raise(&status, tdbb, "JAttachment::prepare", &sql);
break;
case DsqlCompiledStatement::TYPE_INSERT:
case DsqlCompiledStatement::TYPE_DELETE:
case DsqlCompiledStatement::TYPE_UPDATE:
case DsqlCompiledStatement::TYPE_UPDATE_CURSOR:
case DsqlCompiledStatement::TYPE_DELETE_CURSOR:
case DsqlCompiledStatement::TYPE_DDL:
case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE:
case DsqlCompiledStatement::TYPE_SET_GENERATOR:
case DsqlCompiledStatement::TYPE_SAVEPOINT:
case DsqlCompiledStatement::TYPE_EXEC_BLOCK:
break;
}
}
void InternalStatement::doExecute(thread_db* tdbb)
{
JTransaction* transaction = getIntTransaction()->getJrdTran();
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
fb_assert(m_inMetadata->getMessageLength() == m_in_buffer.getCount());
fb_assert(m_outMetadata->getMessageLength() == m_out_buffer.getCount());
m_request->execute(&status, transaction,
m_inMetadata, m_in_buffer.begin(), m_outMetadata, m_out_buffer.begin());
}
if (status->getState() & IStatus::STATE_ERRORS)
raise(&status, tdbb, "JStatement::execute");
}
void InternalStatement::doOpen(thread_db* tdbb)
{
JTransaction* transaction = getIntTransaction()->getJrdTran();
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
if (m_cursor)
{
m_cursor->close(&status);
m_cursor = NULL;
}
fb_assert(m_inMetadata->getMessageLength() == m_in_buffer.getCount());
m_cursor.assignRefNoIncr(m_request->openCursor(&status, transaction,
m_inMetadata, m_in_buffer.begin(), m_outMetadata, 0));
}
if (status->getState() & IStatus::STATE_ERRORS)
raise(&status, tdbb, "JStatement::open");
}
bool InternalStatement::doFetch(thread_db* tdbb)
{
FbLocalStatus status;
bool res = true;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
fb_assert(m_outMetadata->getMessageLength() == m_out_buffer.getCount());
fb_assert(m_cursor);
res = m_cursor->fetchNext(&status, m_out_buffer.begin()) == IStatus::RESULT_OK;
}
if (status->getState() & IStatus::STATE_ERRORS)
raise(&status, tdbb, "JResultSet::fetchNext");
return res;
}
void InternalStatement::doClose(thread_db* tdbb, bool drop)
{
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
if (m_cursor)
m_cursor->close(&status);
m_cursor = NULL;
if (status->getState() & IStatus::STATE_ERRORS)
{
raise(&status, tdbb, "JResultSet::close");
}
if (drop)
{
if (m_request)
m_request->free(&status);
m_allocated = false;
m_request = NULL;
if (status->getState() & IStatus::STATE_ERRORS)
{
raise(&status, tdbb, "JStatement::free");
}
}
}
}
// We need to copy external blob from\to dynamic statement.
// If external blob is permanent one - we could use its blob_id and avoid
// copying blob contents into new temporary blob.
// If external blob is temporary - we could access it by blob_id only if
// dynamic statement is executed in the same transaction as local (caller)
// statement.
static bool isPermanentBlob(const dsc& src)
{
if (src.isBlob())
{
const bid* srcBlobID = reinterpret_cast<bid*>(src.dsc_address);
return (srcBlobID->bid_internal.bid_relation_id != 0);
}
return false;
}
void InternalStatement::putExtBlob(thread_db* tdbb, dsc& src, dsc& dst)
{
if (isPermanentBlob(src) || (m_transaction->getScope() == traCommon && m_intConnection.isCurrent()))
MOV_move(tdbb, &src, &dst);
else
Statement::putExtBlob(tdbb, src, dst);
}
void InternalStatement::getExtBlob(thread_db* tdbb, const dsc& src, dsc& dst)
{
fb_assert(dst.dsc_length == src.dsc_length);
fb_assert(dst.dsc_length == sizeof(bid));
if (isPermanentBlob(src) || (m_transaction->getScope() == traCommon && m_intConnection.isCurrent()))
memcpy(dst.dsc_address, src.dsc_address, sizeof(bid));
else
Statement::getExtBlob(tdbb, src, dst);
}
// InternalBlob
InternalBlob::InternalBlob(InternalConnection& conn) :
Blob(conn),
m_connection(conn),
m_blob(NULL)
{
memset(&m_blob_id, 0, sizeof(m_blob_id));
}
InternalBlob::~InternalBlob()
{
fb_assert(!m_blob);
}
void InternalBlob::open(thread_db* tdbb, Transaction& tran, const dsc& desc, const UCharBuffer* bpb)
{
fb_assert(!m_blob);
fb_assert(sizeof(m_blob_id) == desc.dsc_length);
JAttachment* att = m_connection.getJrdAtt();
JTransaction* transaction = static_cast<InternalTransaction&>(tran).getJrdTran();
memcpy(&m_blob_id, desc.dsc_address, sizeof(m_blob_id));
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
USHORT bpb_len = bpb ? bpb->getCount() : 0;
const UCHAR* bpb_buff = bpb ? bpb->begin() : NULL;
m_blob.assignRefNoIncr(
att->openBlob(&status, transaction, &m_blob_id, bpb_len, bpb_buff));
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JAttachment::openBlob");
fb_assert(m_blob);
}
void InternalBlob::create(thread_db* tdbb, Transaction& tran, dsc& desc, const UCharBuffer* bpb)
{
fb_assert(!m_blob);
fb_assert(sizeof(m_blob_id) == desc.dsc_length);
JAttachment* att = m_connection.getJrdAtt();
JTransaction* transaction = ((InternalTransaction&) tran).getJrdTran();
memset(&m_blob_id, 0, sizeof(m_blob_id));
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
const USHORT bpb_len = bpb ? bpb->getCount() : 0;
const UCHAR* bpb_buff = bpb ? bpb->begin() : NULL;
m_blob.assignRefNoIncr(
att->createBlob(&status, transaction, &m_blob_id, bpb_len, bpb_buff));
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JAttachment::createBlob");
fb_assert(m_blob);
memcpy(desc.dsc_address, &m_blob_id, sizeof(m_blob_id));
}
USHORT InternalBlob::read(thread_db* tdbb, UCHAR* buff, USHORT len)
{
fb_assert(m_blob);
unsigned result = 0;
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
m_blob->getSegment(&status, len, buff, &result);
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JBlob::getSegment");
return result;
}
void InternalBlob::write(thread_db* tdbb, const UCHAR* buff, USHORT len)
{
fb_assert(m_blob);
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
m_blob->putSegment(&status, len, buff);
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JBlob::putSegment");
}
void InternalBlob::close(thread_db* tdbb)
{
fb_assert(m_blob);
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
m_blob->close(&status);
m_blob = NULL;
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JBlob::close");
fb_assert(!m_blob);
}
void InternalBlob::cancel(thread_db* tdbb)
{
if (!m_blob) {
return;
}
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
m_blob->cancel(&status);
m_blob = NULL;
}
if (status->getState() & IStatus::STATE_ERRORS)
m_connection.raise(&status, tdbb, "JBlob::cancel");
fb_assert(!m_blob);
}
} // namespace EDS