8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 15:23:03 +01:00
firebird-mirror/src/jrd/Routine.cpp

341 lines
8.3 KiB
C++

/*
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, 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 Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* Adriano dos Santos Fernandes
*/
#include "firebird.h"
#include "../jrd/Routine.h"
#include "../jrd/JrdStatement.h"
#include "../jrd/jrd.h"
#include "../jrd/exe.h"
#include "../common/StatusHolder.h"
#include "../jrd/lck_proto.h"
#include "../jrd/par_proto.h"
using namespace Firebird;
namespace Jrd {
// Create a MsgMetadata from a parameters array.
MsgMetadata* Routine::createMetadata(const Array<NestConst<Parameter> >& parameters, bool isExtern)
{
RefPtr<MsgMetadata> metadata(FB_NEW MsgMetadata);
for (Array<NestConst<Parameter> >::const_iterator i = parameters.begin();
i != parameters.end();
++i)
{
dsc d((*i)->prm_desc);
if (isExtern && d.dsc_dtype == dtype_dec_fixed)
d.dsc_dtype = dtype_dec128;
metadata->addItem((*i)->prm_name, (*i)->prm_nullable, d);
}
metadata->makeOffsets();
metadata->addRef();
return metadata;
}
// Create a Format based on an IMessageMetadata.
Format* Routine::createFormat(MemoryPool& pool, IMessageMetadata* params, bool addEof)
{
FbLocalStatus status;
unsigned count = params->getCount(&status);
status.check();
Format* format = Format::newFormat(pool, count * 2 + (addEof ? 1 : 0));
unsigned runOffset = 0;
dsc* desc = format->fmt_desc.begin();
for (unsigned i = 0; i < count; ++i)
{
unsigned descOffset, nullOffset, descDtype, descLength;
unsigned type = params->getType(&status, i);
status.check();
unsigned len = params->getLength(&status, i);
status.check();
runOffset = fb_utils::sqlTypeToDsc(runOffset, type, len, &descDtype, &descLength,
&descOffset, &nullOffset);
desc->clear();
desc->dsc_dtype = descDtype;
desc->dsc_length = descLength;
desc->dsc_scale = params->getScale(&status, i);
status.check();
desc->dsc_sub_type = params->getSubType(&status, i);
status.check();
desc->setTextType(params->getCharSet(&status, i));
status.check();
desc->dsc_address = (UCHAR*)(IPTR) descOffset;
desc->dsc_flags = (params->isNullable(&status, i) ? DSC_nullable : 0);
status.check();
++desc;
desc->makeShort(0, (SSHORT*)(IPTR) nullOffset);
++desc;
}
if (addEof)
{
// Next item is aligned on USHORT, so as the previous one.
desc->makeShort(0, (SSHORT*)(IPTR) runOffset);
runOffset += sizeof(USHORT);
}
format->fmt_length = runOffset;
return format;
}
// Parse routine BLR.
void Routine::parseBlr(thread_db* tdbb, CompilerScratch* csb, bid* blob_id)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
UCharBuffer tmp;
if (blob_id)
{
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), blob_id);
ULONG length = blob->blb_length + 10;
UCHAR* temp = tmp.getBuffer(length);
length = blob->BLB_get_data(tdbb, temp, length);
tmp.resize(length);
}
parseMessages(tdbb, csb, BlrReader(tmp.begin(), (unsigned) tmp.getCount()));
JrdStatement* statement = getStatement();
PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb, &statement, false, 0);
setStatement(statement);
if (!blob_id)
setImplemented(false);
}
// Parse the messages of a blr request. For specified message, allocate a format (Format) block.
void Routine::parseMessages(thread_db* tdbb, CompilerScratch* csb, BlrReader blrReader)
{
if (blrReader.getLength() < 2)
status_exception::raise(Arg::Gds(isc_metadata_corrupt));
csb->csb_blr_reader = blrReader;
const SSHORT version = csb->csb_blr_reader.getByte();
switch (version)
{
case blr_version4:
case blr_version5:
//case blr_version6:
break;
default:
status_exception::raise(
Arg::Gds(isc_metadata_corrupt) <<
Arg::Gds(isc_wroblrver2) << Arg::Num(blr_version4) << Arg::Num(blr_version5/*6*/) <<
Arg::Num(version));
}
if (csb->csb_blr_reader.getByte() != blr_begin)
status_exception::raise(Arg::Gds(isc_metadata_corrupt));
while (csb->csb_blr_reader.getByte() == blr_message)
{
const USHORT msgNumber = csb->csb_blr_reader.getByte();
USHORT count = csb->csb_blr_reader.getWord();
Format* format = Format::newFormat(*tdbb->getDefaultPool(), count);
USHORT padField;
const bool shouldPad = csb->csb_message_pad.get(msgNumber, padField);
USHORT maxAlignment = 0;
ULONG offset = 0;
USHORT i = 0;
for (Format::fmt_desc_iterator desc = format->fmt_desc.begin(); i < count; ++i, ++desc)
{
const USHORT align = PAR_desc(tdbb, csb, &*desc);
if (align)
offset = FB_ALIGN(offset, align);
desc->dsc_address = (UCHAR*)(IPTR) offset;
offset += desc->dsc_length;
maxAlignment = MAX(maxAlignment, align);
if (maxAlignment && shouldPad && i + 1 == padField)
offset = FB_ALIGN(offset, maxAlignment);
}
format->fmt_length = offset;
switch (msgNumber)
{
case 0:
setInputFormat(format);
break;
case 1:
setOutputFormat(format);
break;
default:
delete format;
}
}
}
// Decrement the routine's use count.
void Routine::release(thread_db* tdbb)
{
// Actually, it's possible for routines to have intermixed dependencies, so
// this routine can be called for the routine which is being freed itself.
// Hence we should just silently ignore such a situation.
if (!useCount)
return;
if (intUseCount > 0)
intUseCount--;
--useCount;
#ifdef DEBUG_PROCS
{
string buffer;
buffer.printf(
"Called from CMP_decrement():\n\t Decrementing use count of %s\n",
getName().toString().c_str());
JRD_print_procedure_info(tdbb, buffer.c_str());
}
#endif
// Call recursively if and only if the use count is zero AND the routine
// in the cache is different than this routine.
// The routine will be different than in the cache only if it is a
// floating copy, i.e. an old copy or a deleted routine.
if (useCount == 0 && !checkCache(tdbb))
{
if (getStatement())
releaseStatement(tdbb);
flags &= ~Routine::FLAG_BEING_ALTERED;
remove(tdbb);
}
}
void Routine::releaseStatement(thread_db* tdbb)
{
if (getStatement())
{
getStatement()->release(tdbb);
setStatement(NULL);
}
setInputFormat(NULL);
setOutputFormat(NULL);
flags &= ~FLAG_SCANNED;
}
// Remove a routine from cache.
void Routine::remove(thread_db* tdbb)
{
SET_TDBB(tdbb);
// MET_procedure locks it. Lets unlock it now to avoid troubles later
if (existenceLock)
LCK_release(tdbb, existenceLock);
// Routine that is being altered may have references
// to it by other routines via pointer to current meta
// data structure, so don't lose the structure or the pointer.
if (checkCache(tdbb) && !(flags & Routine::FLAG_BEING_ALTERED))
clearCache(tdbb);
// deallocate all structure which were allocated. The routine
// blr is originally read into a new pool from which all request
// are allocated. That will not be freed up.
if (existenceLock)
{
delete existenceLock;
existenceLock = NULL;
}
// deallocate input param structures
for (Array<NestConst<Parameter> >::iterator i = getInputFields().begin();
i != getInputFields().end(); ++i)
{
if (*i)
delete i->getObject();
}
getInputFields().clear();
// deallocate output param structures
for (Array<NestConst<Parameter> >::iterator i = getOutputFields().begin();
i != getOutputFields().end(); ++i)
{
if (*i)
delete i->getObject();
}
getOutputFields().clear();
if (!useCount)
releaseFormat();
if (!(flags & Routine::FLAG_BEING_ALTERED) && useCount == 0)
delete this;
else
{
// Fully clear routine block. Some pieces of code check for empty
// routine name and ID, this is why we do it.
setName(QualifiedName());
setSecurityName("");
setId(0);
setDefaultCount(0);
}
}
bool jrd_prc::checkCache(thread_db* tdbb) const
{
return tdbb->getAttachment()->att_procedures[getId()] == this;
}
void jrd_prc::clearCache(thread_db* tdbb)
{
tdbb->getAttachment()->att_procedures[getId()] = NULL;
}
} // namespace Jrd