Skip to content

Commit

Permalink
Issue #233: Fix prepared execute issues
Browse files Browse the repository at this point in the history
  • Loading branch information
irodushka committed Nov 22, 2024
1 parent fbdfe6e commit d6fe41f
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 139 deletions.
6 changes: 4 additions & 2 deletions IscDbc/IscCallableStatement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ bool IscCallableStatement::execute()
ThrowStatusWrapper status( connection->GDS->_status );
try
{
Sqlda::ExecBuilder execBuilder(inputSqlda);
inputSqlda.checkAndRebuild();
auto* _imeta = inputSqlda.useExecBufferMeta ? inputSqlda.execMeta : inputSqlda.meta;
auto& _ibuf = inputSqlda.useExecBufferMeta ? inputSqlda.execBuffer : inputSqlda.buffer;

statementHandle->execute( &status, transHandle, execBuilder.getMeta(), execBuilder.getBuffer(),
statementHandle->execute( &status, transHandle, _imeta, _ibuf.data(),
outputSqlda.meta, outputSqlda.buffer.data() );
}
catch( const FbException& error )
Expand Down
14 changes: 9 additions & 5 deletions IscDbc/IscStatement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,16 +656,18 @@ bool IscStatement::execute()
// Make sure there is a transaction
ITransaction* transHandle = startTransaction();

Sqlda::ExecBuilder execBuilder(inputSqlda);
inputSqlda.checkAndRebuild();
auto* _imeta = inputSqlda.useExecBufferMeta ? inputSqlda.execMeta : inputSqlda.meta;
auto& _ibuf = inputSqlda.useExecBufferMeta ? inputSqlda.execBuffer : inputSqlda.buffer;

if( openCursor == false )
{
statementHandle->execute( &status, transHandle, execBuilder.getMeta(), execBuilder.getBuffer(), NULL, NULL);
statementHandle->execute( &status, transHandle, _imeta, _ibuf.data(), NULL, NULL);
}
else
{
fbResultSet = statementHandle->openCursor( &status, transHandle,
execBuilder.getMeta(), execBuilder.getBuffer(),
_imeta, _ibuf.data(),
outputSqlda.meta, 0 );
}
}
Expand Down Expand Up @@ -737,10 +739,12 @@ bool IscStatement::executeProcedure()
// Make sure there is a transaction
ITransaction* transHandle = startTransaction();

Sqlda::ExecBuilder execBuilder(inputSqlda);
inputSqlda.checkAndRebuild();
auto* _imeta = inputSqlda.useExecBufferMeta ? inputSqlda.execMeta : inputSqlda.meta;
auto& _ibuf = inputSqlda.useExecBufferMeta ? inputSqlda.execBuffer : inputSqlda.buffer;

statementHandle->execute( &status, transHandle,
execBuilder.getMeta(), execBuilder.getBuffer(),
_imeta, _ibuf.data(),
outputSqlda.meta, outputSqlda.buffer.data() );
}
catch( const FbException& error )
Expand Down
161 changes: 96 additions & 65 deletions IscDbc/Sqlda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ class CDataStaticCursor
// Construction/Destruction Sqlda
//////////////////////////////////////////////////////////////////////

Sqlda::Sqlda( IscConnection* conn ) : connection{conn}, buffer{}
Sqlda::Sqlda(IscConnection* conn) : connection{ conn }, buffer{}, execBuffer{}, useExecBufferMeta{ false }
{
init();
}
Expand All @@ -325,7 +325,7 @@ Sqlda::~Sqlda()

void Sqlda::init()
{
meta = nullptr;
execMeta = meta = nullptr;
sqlvar.clear();
dataStaticCursor = nullptr;
columnsCount = 0;
Expand All @@ -347,11 +347,17 @@ void Sqlda::deleteSqlda()
{
sqlvar.clear();
buffer.clear();
execBuffer.clear();

if( meta ) {
meta->release();
meta = nullptr;
}
if ( execMeta ) {
execMeta->release();
execMeta = nullptr;
}
useExecBufferMeta = false;
}

void Sqlda::allocBuffer ( IscStatement *stmt, IMessageMetadata* msgMetadata )
Expand All @@ -362,6 +368,14 @@ void Sqlda::allocBuffer ( IscStatement *stmt, IMessageMetadata* msgMetadata )
if( meta ) meta->release();
meta = msgMetadata;

if (execMeta)
{
execMeta->release();
execMeta = nullptr;
}
execBuffer.clear();
useExecBufferMeta = false;

ThrowStatusWrapper status( connection->GDS->_status );
try
{
Expand All @@ -377,92 +391,109 @@ void Sqlda::allocBuffer ( IscStatement *stmt, IMessageMetadata* msgMetadata )
mapSqlAttributes( stmt );
}

Sqlda::ExecBuilder::ExecBuilder(Sqlda& sqlda) : _sqlda{sqlda}, _localMeta { nullptr }, _localBuffer{}
/*
* This method is used to build meta & buffer just before the execution, taking into account sqlvar change during bind
*/
bool Sqlda::checkAndRebuild()
{
bool overrideFlag = sqlda.isExternalOverriden();
const bool overrideFlag = isExternalOverriden();

_meta = overrideFlag ? &_localMeta : &_sqlda.meta;
_buffer = overrideFlag ? &_localBuffer : &_sqlda.buffer;

IMetadataBuilder* metaBuilder = nullptr;
ThrowStatusWrapper status(_sqlda.connection->GDS->_status);

auto build_new_meta = [&]()->IMessageMetadata*
if (overrideFlag)
{
if (!_sqlda.meta)
throw SQLEXCEPTION(RUNTIME_ERROR, "Sqlda::ExecBuilder(): sqlda.meta==null before execute!");

metaBuilder = _sqlda.meta->getBuilder(&status);
IMetadataBuilder* metaBuilder = nullptr;
ThrowStatusWrapper status(connection->GDS->_status);

for (auto& var : _sqlda.sqlvar)
auto build_new_meta = [&]()->IMessageMetadata*
{
const auto i = var.index - 1;
metaBuilder->setType(&status, i, var.sqltype | (short)(var.isNullable ? 1 : 0));
if (!meta)
throw SQLEXCEPTION(RUNTIME_ERROR, "Sqlda::ExecBuilder(): sqlda.meta==null before execute!");

//ooapi provides charset in separated field, so we should set subtype=0 for text types
const auto subtype = (var.sqltype == SQL_TEXT || var.sqltype == SQL_VARYING) ? 0 : var.sqlsubtype;
metaBuilder->setSubType(&status, i, subtype);
metaBuilder = meta->getBuilder(&status);

metaBuilder->setCharSet(&status, i, var.sqlcharset);
metaBuilder->setScale(&status, i, var.sqlscale);
metaBuilder->setLength(&status, i, var.sqllen);
}
for (const auto& var : sqlvar)
{
const auto i = var.index - 1;
metaBuilder->setType(&status, i, var.sqltype | (short)(var.isNullable ? 1 : 0));

auto * res = metaBuilder->getMetadata(&status);
metaBuilder->release();
metaBuilder = nullptr;
return res;
};
//ooapi provides charset in separated field, so we should set subtype=0 for text types
const auto subtype = (var.sqltype == SQL_TEXT || var.sqltype == SQL_VARYING) ? 0 : var.sqlsubtype;
metaBuilder->setSubType(&status, i, subtype);

try
{
if (overrideFlag)
metaBuilder->setCharSet(&status, i, var.sqlcharset);
metaBuilder->setScale(&status, i, var.sqlscale);
metaBuilder->setLength(&status, i, var.sqllen);
}

auto* res = metaBuilder->getMetadata(&status);
metaBuilder->release();
metaBuilder = nullptr;
return res;
};

try
{
//build the new metadata
_localMeta = build_new_meta();
//printf("Rebuilding metadata due to sqlvar changes...\n");

if ((*_meta)->getCount(&status) != _sqlda.columnsCount)
throw SQLEXCEPTION(RUNTIME_ERROR, "Sqlda::ExecBuilder(): incorrect columns count");
if (execMeta)
{
execMeta->release();
}

//resize the new buffer to required length
_buffer->resize((*_meta)->getMessageLength(&status));
}
execMeta = build_new_meta();

//fill exec buffer with data - regardless of whether it was rebuilt or not
for (auto& var : _sqlda.sqlvar)
{
const auto i = var.index - 1;
const auto offs = (*_meta)->getOffset(&status, i);
const auto offsNull = (*_meta)->getNullOffset(&status, i);
if (execMeta->getCount(&status) != columnsCount)
{
throw SQLEXCEPTION(RUNTIME_ERROR, "Sqlda::checkAndRebuild(): incorrect columns count");
}

if ( (*(short*)(_buffer->data() + offsNull) = *var.sqlind) != sqlNull )
lengthBufferRows = execMeta->getMessageLength(&status);
execBuffer.clear();
execBuffer.resize(lengthBufferRows);

for (auto& var : sqlvar)
{
memcpy(_buffer->data() + offs, var.sqldata, (*_meta)->getLength(&status, i));
const auto i = var.index - 1;
//save effective sqldata&sqlind, pointing to the new execBuffer
var.eff_sqldata = &execBuffer.at(execMeta->getOffset(&status, i));
var.eff_sqlind = (short*)&execBuffer.at(execMeta->getNullOffset(&status, i));
//update last sqlvars to avoid buffers rebuilt next time
var.lastSqlProperties = var;
}

useExecBufferMeta = true;
}
catch (const FbException& error)
{
if (metaBuilder) metaBuilder->release();
THROW_ISC_EXCEPTION(connection, error.getStatus());
}
catch (...)
{
if (metaBuilder) metaBuilder->release();
throw;
}
}
catch (const FbException& error)
{
if (metaBuilder) metaBuilder->release();
THROW_ISC_EXCEPTION(_sqlda.connection, error.getStatus());
}
catch (...)
else
{
if (metaBuilder) metaBuilder->release();
throw;
//printf("Metadata is actual\n");
}
}

Sqlda::ExecBuilder::~ExecBuilder()
{
//save new meta
if (_localMeta)
{
for (auto& var : _sqlda.sqlvar) var.orgSqlProperties = *static_cast<SqlProperties*>(&var);
if(_sqlda.meta) _sqlda.meta->release();
_sqlda.meta = _localMeta;
_sqlda.buffer = _localBuffer;
for (const auto& var : sqlvar)
{
if (var.eff_sqlind != var.sqlind)
{
memcpy(var.eff_sqlind, var.sqlind, sizeof(short));
}

if (var.eff_sqldata != var.sqldata && *var.eff_sqlind != sqlNull)
{
memcpy(var.eff_sqldata, var.sqldata, var.sqllen);
}
}
}

return overrideFlag;
}

void Sqlda::mapSqlAttributes( IscStatement *stmt )
Expand Down
Loading

0 comments on commit d6fe41f

Please sign in to comment.