diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index a08f9fcce81..cfac854db3a 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -49,6 +49,7 @@ class YRequest; class YResultSet; class YService; class YStatement; +class IscStatement; class YTransaction; class YObject @@ -98,10 +99,13 @@ class HandleArray void destroy(unsigned dstrFlags) { Firebird::MutexLockGuard guard(mtx, FB_FUNCTION); - FB_SIZE_T i; - while ((i = array.getCount()) > 0) - array[i - 1]->destroy(dstrFlags); + // Call destroy() only once even if handle is not removed from array + // by this call for any reason + for (int i = array.getCount() - 1; i >= 0; i--) + array[i]->destroy(dstrFlags); + + clear(); } void assign(HandleArray& from) @@ -473,6 +477,7 @@ class YAttachment FB_FINAL : HandleArray childEvents; HandleArray childRequests; HandleArray childStatements; + HandleArray childIscStatements; HandleArray childTransactions; Firebird::Array cleanupHandlers; Firebird::StatusHolder savedStatus; // Do not use raise() method of this class in yValve. diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 8a703e35c84..5dfb98238e7 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -634,55 +634,6 @@ int SQLDAMetadata::detach() } -class IscStatement : public RefCounted, public GlobalStorage, public YObject -{ -public: - static const ISC_STATUS ERROR_CODE = isc_bad_stmt_handle; - - explicit IscStatement(YAttachment* aAttachment) - : cursorName(getPool()), - attachment(aAttachment), - statement(NULL), - userHandle(NULL), - pseudoOpened(false), - delayedFormat(false) - { } - - FB_API_HANDLE& getHandle(); - void openCursor(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, - IMessageMetadata* inMetadata, UCHAR* buffer, IMessageMetadata* outMetadata); - void closeCursor(CheckStatusWrapper* status, bool raise); - void closeStatement(CheckStatusWrapper* status); - - void execute(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, - IMessageMetadata* inMetadata, UCHAR* inBuffer, IMessageMetadata* outMetadata, UCHAR* outBuffer); - FB_BOOLEAN fetch(CheckStatusWrapper* status, IMessageMetadata* outMetadata, UCHAR* outBuffer); - - void checkPrepared(ISC_STATUS code = isc_unprepared_stmt) const - { - if (!statement) - Arg::Gds(code).raise(); - } - - void checkCursorOpened() const - { - if (!statement || !statement->cursor) - Arg::Gds(isc_dsql_cursor_not_open).raise(); - } - - void checkCursorClosed() const - { - if (statement && statement->cursor) - Arg::Gds(isc_dsql_cursor_open_err).raise(); - } - - string cursorName; - YAttachment* attachment; - YStatement* statement; - FB_API_HANDLE* userHandle; - bool pseudoOpened, delayedFormat; -}; - GlobalPtr handleMappingLock; GlobalPtr > > > services; GlobalPtr > > > attachments; @@ -766,13 +717,6 @@ RefPtr translateHandle(GlobalPtr return RefPtr(*obj); } -FB_API_HANDLE& IscStatement::getHandle() -{ - if (!handle) - makeHandle(&statements, this, handle); - return handle; -} - //------------------------------------- const int SHUTDOWN_TIMEOUT = 5000; // 5 sec @@ -1241,6 +1185,58 @@ namespace Why RefPtr nextRef; }; + class IscStatement : public RefCounted, public GlobalStorage, public YObject + { + public: + static const ISC_STATUS ERROR_CODE = isc_bad_stmt_handle; + + explicit IscStatement(YAttachment* aAttachment) + : cursorName(getPool()), + attachment(aAttachment), + statement(NULL), + userHandle(NULL), + pseudoOpened(false), + delayedFormat(false) + { } + + ~IscStatement() override; + + FB_API_HANDLE& getHandle(); + void destroy(unsigned); + void openCursor(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, + IMessageMetadata* inMetadata, UCHAR* buffer, IMessageMetadata* outMetadata); + void closeCursor(CheckStatusWrapper* status, bool raise); + void closeStatement(CheckStatusWrapper* status); + + void execute(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, + IMessageMetadata* inMetadata, UCHAR* inBuffer, IMessageMetadata* outMetadata, UCHAR* outBuffer); + FB_BOOLEAN fetch(CheckStatusWrapper* status, IMessageMetadata* outMetadata, UCHAR* outBuffer); + + void checkPrepared(ISC_STATUS code = isc_unprepared_stmt) const + { + if (!statement) + Arg::Gds(code).raise(); + } + + void checkCursorOpened() const + { + if (!statement || !statement->cursor) + Arg::Gds(isc_dsql_cursor_not_open).raise(); + } + + void checkCursorClosed() const + { + if (statement && statement->cursor) + Arg::Gds(isc_dsql_cursor_open_err).raise(); + } + + string cursorName; + YAttachment* attachment; + YStatement* statement; + FB_API_HANDLE* userHandle; + bool pseudoOpened, delayedFormat; + }; + template <> YEntry::YEntry(CheckStatusWrapper* aStatus, YAttachment* aAttachment, int checkAttachment) : ref(aAttachment), nextRef(NULL) @@ -1311,103 +1307,6 @@ namespace Why } // namespace Why -namespace { - void IscStatement::openCursor(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, - IMessageMetadata* inMetadata, UCHAR* buffer, IMessageMetadata* outMetadata) - { - checkCursorClosed(); - - // Transaction is not optional for statement returning result set - RefPtr transaction = translateHandle(transactions, traHandle);; - - statement->openCursor(status, transaction, inMetadata, buffer, outMetadata, 0); - - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - return; - - fb_assert(statement->cursor); - - delayedFormat = (outMetadata == DELAYED_OUT_FORMAT); - } - - void IscStatement::closeCursor(CheckStatusWrapper* status, bool raise) - { - if (statement && statement->cursor) - { - fb_assert(!pseudoOpened); - - statement->cursor->close(status); - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - status_exception::raise(status); - - statement->cursor = NULL; - } - else if (pseudoOpened) - pseudoOpened = false; - else if (raise) - Arg::Gds(isc_dsql_cursor_close_err).raise(); - } - - void IscStatement::closeStatement(CheckStatusWrapper* status) - { - if (statement) - { - statement->free(status); - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - status_exception::raise(status); - - statement = NULL; - } - } - - void IscStatement::execute(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, - IMessageMetadata* inMetadata, UCHAR* inBuffer, IMessageMetadata* outMetadata, - UCHAR* outBuffer) - { - checkCursorClosed(); - - RefPtr transaction; - if (traHandle && *traHandle) - transaction = translateHandle(transactions, traHandle); - - ITransaction* newTrans = statement->execute(status, transaction, - inMetadata, inBuffer, outMetadata, outBuffer); - - if (!(status->getState() & Firebird::IStatus::STATE_ERRORS)) - { - if (transaction && !newTrans) - { - transaction->destroy(0); - *traHandle = 0; - } - else if (!transaction && newTrans) - { - // in this case we know for sure that newTrans points to YTransaction - if (traHandle) - *traHandle = static_cast(newTrans)->getHandle(); - } - } - } - - FB_BOOLEAN IscStatement::fetch(CheckStatusWrapper* status, IMessageMetadata* outMetadata, - UCHAR* outBuffer) - { - checkCursorOpened(); - - if (delayedFormat) - { - statement->cursor->setDelayedOutputFormat(status, outMetadata); - - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - return FB_FALSE; - - delayedFormat = false; - } - - return statement->cursor->fetchNext(status, outBuffer) == IStatus::RESULT_OK; - } -} - struct TEB { FB_API_HANDLE* teb_database; @@ -2163,6 +2062,7 @@ ISC_STATUS API_ROUTINE isc_dsql_allocate_statement(ISC_STATUS* userStatus, FB_AP statement = FB_NEW IscStatement(attachment); statement->addRef(); + attachment->childIscStatements.add(statement); *stmtHandle = statement->getHandle(); } catch (const Exception& e) @@ -2586,8 +2486,10 @@ ISC_STATUS API_ROUTINE isc_dsql_free_statement(ISC_STATUS* userStatus, FB_API_HA // Release everything statement->closeCursor(&statusWrapper, false); statement->closeStatement(&statusWrapper); - statement->release(); - removeHandle(&statements, *stmtHandle); + // statement->userHandle is not erased here because this routine can be called + // against a copy of original variable. + // This call must release statement and clean handles + statement->destroy(0); *stmtHandle = 0; } else if (option & DSQL_unprepare) @@ -4201,8 +4103,6 @@ void YStatement::destroy(unsigned dstrFlags) attachment->childStatements.remove(this); attachment = NULL; - removeHandle(&statements, handle); - destroy2(dstrFlags); } @@ -4443,6 +4343,126 @@ void YStatement::free(CheckStatusWrapper* status) } } +//------------------------------------- + +IscStatement::~IscStatement() +{ + if (userHandle) + { + *userHandle = 0; + userHandle = nullptr; + } + removeHandle(&statements, handle); +} + +void IscStatement::destroy(unsigned) +{ + attachment->childIscStatements.remove(this); + attachment = NULL; + release(); +} + +FB_API_HANDLE& IscStatement::getHandle() +{ + if (!handle) + makeHandle(&statements, this, handle); + return handle; +} + +void IscStatement::openCursor(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, + IMessageMetadata* inMetadata, UCHAR* buffer, IMessageMetadata* outMetadata) +{ + checkCursorClosed(); + + // Transaction is not optional for statement returning result set + RefPtr transaction = translateHandle(transactions, traHandle);; + + statement->openCursor(status, transaction, inMetadata, buffer, outMetadata, 0); + + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + return; + + fb_assert(statement->cursor); + + delayedFormat = (outMetadata == DELAYED_OUT_FORMAT); +} + +void IscStatement::closeCursor(CheckStatusWrapper* status, bool raise) +{ + if (statement && statement->cursor) + { + fb_assert(!pseudoOpened); + + statement->cursor->close(status); + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + status_exception::raise(status); + + statement->cursor = NULL; + } + else if (pseudoOpened) + pseudoOpened = false; + else if (raise) + Arg::Gds(isc_dsql_cursor_close_err).raise(); +} + +void IscStatement::closeStatement(CheckStatusWrapper* status) +{ + if (statement) + { + statement->free(status); + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + status_exception::raise(status); + + statement = NULL; + } +} + +void IscStatement::execute(CheckStatusWrapper* status, FB_API_HANDLE* traHandle, + IMessageMetadata* inMetadata, UCHAR* inBuffer, IMessageMetadata* outMetadata, + UCHAR* outBuffer) +{ + checkCursorClosed(); + + RefPtr transaction; + if (traHandle && *traHandle) + transaction = translateHandle(transactions, traHandle); + + ITransaction* newTrans = statement->execute(status, transaction, + inMetadata, inBuffer, outMetadata, outBuffer); + + if (!(status->getState() & Firebird::IStatus::STATE_ERRORS)) + { + if (transaction && !newTrans) + { + transaction->destroy(0); + *traHandle = 0; + } + else if (!transaction && newTrans) + { + // in this case we know for sure that newTrans points to YTransaction + if (traHandle) + *traHandle = static_cast(newTrans)->getHandle(); + } + } +} + +FB_BOOLEAN IscStatement::fetch(CheckStatusWrapper* status, IMessageMetadata* outMetadata, + UCHAR* outBuffer) +{ + checkCursorOpened(); + + if (delayedFormat) + { + statement->cursor->setDelayedOutputFormat(status, outMetadata); + + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + return FB_FALSE; + + delayedFormat = false; + } + + return statement->cursor->fetchNext(status, outBuffer) == IStatus::RESULT_OK; +} //------------------------------------- @@ -4955,6 +4975,7 @@ YAttachment::YAttachment(IProvider* aProvider, IAttachment* aNext, const PathNam childEvents(getPool()), childRequests(getPool()), childStatements(getPool()), + childIscStatements(getPool()), childTransactions(getPool()), cleanupHandlers(getPool()) { @@ -4987,6 +5008,7 @@ void YAttachment::destroy(unsigned dstrFlags) childRequests.destroy(dstrFlags & ~DF_RELEASE); childStatements.destroy(dstrFlags & ~DF_RELEASE); + childIscStatements.destroy(dstrFlags & ~DF_RELEASE); childBlobs.destroy(dstrFlags & ~DF_RELEASE); childEvents.destroy(dstrFlags & ~DF_RELEASE); childTransactions.destroy(dstrFlags & ~DF_RELEASE);