From d6fe41fddd359c65ddc9262cf6283b0d8c0aaa07 Mon Sep 17 00:00:00 2001 From: irodushka Date: Fri, 22 Nov 2024 13:00:35 +0300 Subject: [PATCH 1/5] Issue #233: Fix prepared execute issues --- IscDbc/IscCallableStatement.cpp | 6 +- IscDbc/IscStatement.cpp | 14 ++- IscDbc/Sqlda.cpp | 161 +++++++++++++++++++------------- IscDbc/Sqlda.h | 117 ++++++++++------------- 4 files changed, 159 insertions(+), 139 deletions(-) diff --git a/IscDbc/IscCallableStatement.cpp b/IscDbc/IscCallableStatement.cpp index 9ac74f9..aaf9193 100644 --- a/IscDbc/IscCallableStatement.cpp +++ b/IscDbc/IscCallableStatement.cpp @@ -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 ) diff --git a/IscDbc/IscStatement.cpp b/IscDbc/IscStatement.cpp index 08f16be..5c93962 100644 --- a/IscDbc/IscStatement.cpp +++ b/IscDbc/IscStatement.cpp @@ -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 ); } } @@ -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 ) diff --git a/IscDbc/Sqlda.cpp b/IscDbc/Sqlda.cpp index 020f129..ce83588 100644 --- a/IscDbc/Sqlda.cpp +++ b/IscDbc/Sqlda.cpp @@ -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(); } @@ -325,7 +325,7 @@ Sqlda::~Sqlda() void Sqlda::init() { - meta = nullptr; + execMeta = meta = nullptr; sqlvar.clear(); dataStaticCursor = nullptr; columnsCount = 0; @@ -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 ) @@ -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 { @@ -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(&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 ) diff --git a/IscDbc/Sqlda.h b/IscDbc/Sqlda.h index 512b3b4..9947195 100644 --- a/IscDbc/Sqlda.h +++ b/IscDbc/Sqlda.h @@ -43,7 +43,7 @@ struct SqlProperties short sqlscale; /* scale factor */ short sqlsubtype; /* datatype subtype - BLOBs & Text types only */ unsigned sqlcharset; - short sqllen; /* length of data area */ + unsigned sqllen; /* length of data area */ inline SqlProperties() : offsetData{ 0 }, @@ -78,6 +78,8 @@ class CAttrSqlVar : public SqlProperties sqldata{ nullptr }, sqlind{ nullptr }, + eff_sqldata{ nullptr }, eff_sqlind{ nullptr }, + array{ nullptr }, index{ 0 }, replaceForParamArray{ false } @@ -117,7 +119,8 @@ class CAttrSqlVar : public SqlProperties // ++index; //to make it 1-based) - orgSqlProperties = *static_cast(this); // save original props + orgSqlProperties = *this; // save original props + lastSqlProperties = *this; } public: @@ -128,59 +131,62 @@ class CAttrSqlVar : public SqlProperties } inline void assignBuffer(buffer_t& buffer ) { - sqldata = &buffer.at( offsetData ); - sqlind = (short*)&buffer.at( offsetNull ); + eff_sqldata = sqldata = &buffer.at( offsetData ); + eff_sqlind = sqlind = (short*)&buffer.at( offsetNull ); } inline bool propertiesOverriden() { - return !( *this == orgSqlProperties ); + return !( *this == lastSqlProperties); } char* sqldata; short* sqlind; + char* eff_sqldata; + short* eff_sqlind; CAttrArray *array; unsigned index; // 1-based parameter index bool replaceForParamArray; SqlProperties orgSqlProperties; // original properties after prepare + SqlProperties lastSqlProperties; // last used properties after prev exec }; class Value; class IscConnection; class CDataStaticCursor; -class Sqlda +class Sqlda { public: using buffer_t = std::vector; protected: - buffer_t& initStaticCursor(IscStatement *stmt); + buffer_t& initStaticCursor(IscStatement* stmt); buffer_t& addRowSqldaInBufferStaticCursor(); void restoreOrgAdressFieldsStaticCursor(); public: - const char* getOwnerName (int index); - int findColumn (const char *columnName); - void setBlob (CAttrSqlVar* var, Value * value, IscStatement *stmt); - void setArray (CAttrSqlVar* var, Value *value, IscStatement *stmt); - void setValue (int slot, Value *value, IscStatement *stmt); - const char* getTableName (int index); - int getSqlType (CAttrSqlVar *var, int &realSqlType); - const char* getSqlTypeName (CAttrSqlVar *var); - bool isNullable (int index); - int getScale (int index); - int getPrecision (int index); + const char* getOwnerName(int index); + int findColumn(const char* columnName); + void setBlob(CAttrSqlVar* var, Value* value, IscStatement* stmt); + void setArray(CAttrSqlVar* var, Value* value, IscStatement* stmt); + void setValue(int slot, Value* value, IscStatement* stmt); + const char* getTableName(int index); + int getSqlType(CAttrSqlVar* var, int& realSqlType); + const char* getSqlTypeName(CAttrSqlVar* var); + bool isNullable(int index); + int getScale(int index); + int getPrecision(int index); int getNumPrecRadix(int index); - const char* getColumnLabel (int index); - const char* getColumnName (int index); - int getColumnDisplaySize (int index); + const char* getColumnLabel(int index); + const char* getColumnName(int index); + int getColumnDisplaySize(int index); short getSubType(int index); - int getColumnType (int index, int &realSqlType); - const char * getColumnTypeName (int index); + int getColumnType(int index, int& realSqlType); + const char* getColumnTypeName(int index); void print(); bool setCurrentRowInBufferStaticCursor(int nRow); - void getAdressFieldFromCurrentRowInBufferStaticCursor(int column, char *& sqldata, short *& sqlind); + void getAdressFieldFromCurrentRowInBufferStaticCursor(int column, char*& sqldata, short*& sqlind); void copyNextSqldaInBufferStaticCursor(); void copyNextSqldaFromBufferStaticCursor(); void saveCurrentSqldaToBuffer(); @@ -189,13 +195,13 @@ class Sqlda int getColumnCount(); void init(); void remove(); - void allocBuffer(IscStatement *stmt, Firebird::IMessageMetadata* msgMetadata); - void mapSqlAttributes(IscStatement *stmt); + void allocBuffer(IscStatement* stmt, Firebird::IMessageMetadata* msgMetadata); + void mapSqlAttributes(IscStatement* stmt); void deleteSqlda(); void clearSqlda(); - CAttrSqlVar* Var(int index) { return &sqlvar.at( index - 1 ); } + CAttrSqlVar* Var(int index) { return &sqlvar.at(index - 1); } - Sqlda( IscConnection* conn ); + Sqlda(IscConnection* conn); ~Sqlda(); int isBlobOrArray(int index); @@ -203,25 +209,25 @@ class Sqlda void setNull(int index); void setNotNull(int index); - bool getBoolean (int index); - short getShort (int index); - int getInt (int index); - char * getText (int index, int &len); - char * getVarying (int index, int &len); + bool getBoolean(int index); + short getShort(int index); + int getInt(int index); + char* getText(int index, int& len); + char* getVarying(int index, int& len); - void updateBoolean (int index, bool value); - void updateShort (int index, short value); - void updateInt (int index, int value); - void updateText (int index, const char* value); - void updateVarying (int index, const char* dst); + void updateBoolean(int index, bool value); + void updateShort(int index, short value); + void updateInt(int index, int value); + void updateText(int index, const char* value); + void updateVarying(int index, const char* dst); - CDataStaticCursor * dataStaticCursor; + CDataStaticCursor* dataStaticCursor; int lengthBufferRows; IscConnection* connection; - Firebird::IMessageMetadata* meta; - - buffer_t buffer; + Firebird::IMessageMetadata* meta, * execMeta; + buffer_t buffer, execBuffer; + bool useExecBufferMeta; //flag, if true - use execMeta/execBuffer instead of meta/buffer using orgsqlvar_t = std::vector; orgsqlvar_t sqlvar; @@ -231,34 +237,11 @@ class Sqlda friend class IscResultSet; inline bool isExternalOverriden() { - for( auto & var : sqlvar ) if (var.propertiesOverriden()) return true; + for (auto& var : sqlvar) if (var.propertiesOverriden()) return true; return false; } - /* - * This class is used to build meta & buffer just before the execution, taking into account sqlvar change during bind - * It restores the original sqlvars in dtor - */ - class ExecBuilder - { - public: - ExecBuilder(Sqlda& sqlda); - ~ExecBuilder(); - - inline Firebird::IMessageMetadata* getMeta() { - return *_meta; - } - inline char* getBuffer() { - return _buffer->data(); - } - private: - Sqlda& _sqlda; - Firebird::IMessageMetadata** _meta; - buffer_t* _buffer; - - Firebird::IMessageMetadata* _localMeta; - buffer_t _localBuffer; - }; + bool checkAndRebuild(); }; }; // end namespace IscDbcLibrary From 88cee98cf93dc61e9640eba0f5a7c4a61b4531b3 Mon Sep 17 00:00:00 2001 From: irodushka Date: Wed, 27 Nov 2024 13:30:00 +0300 Subject: [PATCH 2/5] Fix error in field size getter - always get the original data length --- IscDbc/Sqlda.cpp | 4 ++-- IscDbc/Sqlda.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/IscDbc/Sqlda.cpp b/IscDbc/Sqlda.cpp index ce83588..9ae9182 100644 --- a/IscDbc/Sqlda.cpp +++ b/IscDbc/Sqlda.cpp @@ -684,7 +684,7 @@ void Sqlda::print() // int Sqlda::getColumnDisplaySize(int index) { - CAttrSqlVar *var = Var(index); + const SqlProperties *var = orgVarSqlProperties(index); switch (var->sqltype) { @@ -724,7 +724,7 @@ int Sqlda::getColumnDisplaySize(int index) MAX_QUAD_LENGTH + 1); case SQL_ARRAY: - return var->array->arrOctetLength; + return Var(index)->array->arrOctetLength; // return MAX_ARRAY_LENGTH; case SQL_BLOB: diff --git a/IscDbc/Sqlda.h b/IscDbc/Sqlda.h index 9947195..d9eec8d 100644 --- a/IscDbc/Sqlda.h +++ b/IscDbc/Sqlda.h @@ -200,6 +200,7 @@ class Sqlda void deleteSqlda(); void clearSqlda(); CAttrSqlVar* Var(int index) { return &sqlvar.at(index - 1); } + const SqlProperties* orgVarSqlProperties(int index) { return &sqlvar.at(index - 1).orgSqlProperties; } Sqlda(IscConnection* conn); ~Sqlda(); From efd3306825ade20dadb2dd44438d2deba539b87d Mon Sep 17 00:00:00 2001 From: irodushka Date: Mon, 2 Dec 2024 17:03:39 +0300 Subject: [PATCH 3/5] Fix in multibute convs - error related to an incorrect patch in issue#201 --- MbsAndWcs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MbsAndWcs.cpp b/MbsAndWcs.cpp index bf999fb..40a2263 100644 --- a/MbsAndWcs.cpp +++ b/MbsAndWcs.cpp @@ -26,7 +26,7 @@ extern UINT codePage; // from Main.cpp size_t _MbsToWcs( wchar_t *wcstr, const char *mbstr, size_t count ) { - if (!count) return count; + if (wcstr && !count) return count; size_t len = MultiByteToWideChar( codePage, 0, @@ -44,7 +44,7 @@ size_t _MbsToWcs( wchar_t *wcstr, const char *mbstr, size_t count ) size_t _WcsToMbs( char *mbstr, const wchar_t *wcstr, size_t count ) { - if (!count) return count; + if (mbstr && !count) return count; size_t len = WideCharToMultiByte( codePage, 0, From f96bc8ca47ce7cf5ea0f4e71a81bfcac8f772f66 Mon Sep 17 00:00:00 2001 From: irodushka Date: Tue, 3 Dec 2024 18:51:22 +0300 Subject: [PATCH 4/5] Update build num -> 15 --- WriteBuildNo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WriteBuildNo.h b/WriteBuildNo.h index fc50b82..ae99d2e 100644 --- a/WriteBuildNo.h +++ b/WriteBuildNo.h @@ -4,4 +4,4 @@ // Note - there must be two tabs between BUILDNUM_VERSION and // the actual number, otherwise the makefile for linux will not // pick up the value. -#define BUILDNUM_VERSION 14 +#define BUILDNUM_VERSION 15 From f8de755046e903c8d01e086e6f4bbc2f24abb851 Mon Sep 17 00:00:00 2001 From: irodushka Date: Wed, 4 Dec 2024 17:27:09 +0300 Subject: [PATCH 5/5] Make SQLDA logic more solid --- IscDbc/IscStatement.cpp | 4 ++-- IscDbc/Sqlda.cpp | 4 ++-- IscDbc/Sqlda.h | 8 +++++++- IscDbc/TypesResultSet.cpp | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/IscDbc/IscStatement.cpp b/IscDbc/IscStatement.cpp index 5c93962..85dd7cd 100644 --- a/IscDbc/IscStatement.cpp +++ b/IscDbc/IscStatement.cpp @@ -106,8 +106,8 @@ namespace IscDbcLibrary { ////////////////////////////////////////////////////////////////////// IscStatement::IscStatement(IscConnection *connect) : - inputSqlda{connect}, - outputSqlda{connect} + inputSqlda{connect, Sqlda::SQLDA_INPUT}, + outputSqlda{connect, Sqlda::SQLDA_OUTPUT} { connection = connect; useCount = 1; diff --git a/IscDbc/Sqlda.cpp b/IscDbc/Sqlda.cpp index 9ae9182..4754c83 100644 --- a/IscDbc/Sqlda.cpp +++ b/IscDbc/Sqlda.cpp @@ -313,7 +313,7 @@ class CDataStaticCursor // Construction/Destruction Sqlda ////////////////////////////////////////////////////////////////////// -Sqlda::Sqlda(IscConnection* conn) : connection{ conn }, buffer{}, execBuffer{}, useExecBufferMeta{ false } +Sqlda::Sqlda(IscConnection* conn, e_sqlda_dir dir) : connection{ conn }, buffer{}, execBuffer{}, useExecBufferMeta{ false }, SqldaDir{ dir } { init(); } @@ -684,7 +684,7 @@ void Sqlda::print() // int Sqlda::getColumnDisplaySize(int index) { - const SqlProperties *var = orgVarSqlProperties(index); + const SqlProperties *var = (SqldaDir == SQLDA_INPUT) ? orgVarSqlProperties(index) : Var(index); switch (var->sqltype) { diff --git a/IscDbc/Sqlda.h b/IscDbc/Sqlda.h index d9eec8d..9c7b6e9 100644 --- a/IscDbc/Sqlda.h +++ b/IscDbc/Sqlda.h @@ -160,10 +160,16 @@ class Sqlda public: using buffer_t = std::vector; + enum e_sqlda_dir { + SQLDA_INPUT, + SQLDA_OUTPUT, + }; + protected: buffer_t& initStaticCursor(IscStatement* stmt); buffer_t& addRowSqldaInBufferStaticCursor(); void restoreOrgAdressFieldsStaticCursor(); + e_sqlda_dir SqldaDir; public: const char* getOwnerName(int index); @@ -202,7 +208,7 @@ class Sqlda CAttrSqlVar* Var(int index) { return &sqlvar.at(index - 1); } const SqlProperties* orgVarSqlProperties(int index) { return &sqlvar.at(index - 1).orgSqlProperties; } - Sqlda(IscConnection* conn); + Sqlda(IscConnection* conn, e_sqlda_dir dir); ~Sqlda(); int isBlobOrArray(int index); diff --git a/IscDbc/TypesResultSet.cpp b/IscDbc/TypesResultSet.cpp index 907dae0..ef051c1 100644 --- a/IscDbc/TypesResultSet.cpp +++ b/IscDbc/TypesResultSet.cpp @@ -136,7 +136,7 @@ static Types types [] = TypesResultSet::TypesResultSet(int dataType, int appOdbcVersion, int bytesPerCharacter, IscConnection* conn) : IscResultSet{nullptr}, - outputSqlda {conn} + outputSqlda {conn, Sqlda::SQLDA_OUTPUT} { dataTypes = dataType;