Skip to content

Commit

Permalink
Fix blob fetching semantics in Oracle
Browse files Browse the repository at this point in the history
Previously, the into-type would take the blob object's LOB locator
object and bind that in the query. That works as long as there is only a
single Blob object being fetched from the database. However, if multiple
values are fetched consecutively, there would only be a single locator
object involved. In the best case, this leads to every new fetch
overriding the previous result and in the worst case, that previous
result has been turned into an independent blob object that has gone out
of scope, which leads to freeing of this shared locator.

With this commit, the into-type object will create its own locator
object and use that. When assigning the fetched result to a blob object,
the locator is copied such that the into-type and the blob keep
independent locator objects, i.e. the locator is no longer shared.
  • Loading branch information
Krzmbrzl committed Sep 1, 2024
1 parent 6c35d77 commit f110c69
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 7 deletions.
2 changes: 1 addition & 1 deletion include/soci/oracle/soci-oracle.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ struct oracle_blob_backend : details::blob_backend

locator_t get_lob_locator() const;

void set_lob_locator(locator_t locator, bool initialized = true);
void set_lob_locator(const locator_t locator, bool initialized = true);

void reset();

Expand Down
8 changes: 6 additions & 2 deletions src/backends/oracle/blob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ oracle_blob_backend::locator_t oracle_blob_backend::get_lob_locator() const
return lobp_;
}

void oracle_blob_backend::set_lob_locator(oracle_blob_backend::locator_t locator, bool initialized)
void oracle_blob_backend::set_lob_locator(const oracle_blob_backend::locator_t locator, bool initialized)
{
// If we select a BLOB value into a BLOB object, then the post_fetch code in
// the standard_into_type_backend will set this object's locator to the one it is
Expand All @@ -157,7 +157,11 @@ void oracle_blob_backend::set_lob_locator(oracle_blob_backend::locator_t locator
{
reset();

lobp_ = locator;
sword res = OCILobLocatorAssign(session_.svchp_, session_.errhp_, locator, &lobp_);
if (res != OCI_SUCCESS)
{
throw_oracle_soci_error(res, session_.errhp_);
}
}

initialized_ = initialized;
Expand Down
24 changes: 20 additions & 4 deletions src/backends/oracle/standard-into-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <ctime>
#include <sstream>

#include <oci.h>

#ifdef _MSC_VER
#pragma warning(disable:4355)
#endif
Expand Down Expand Up @@ -157,11 +159,24 @@ void oracle_standard_into_type_backend::define_by_pos(
oracle_blob_backend *bbe
= static_cast<oracle_blob_backend *>(b->get_backend());

// Reset the blob to ensure that a potentially open temporary BLOB gets
// freed before the locator is changed to point to a different BLOB by the
// to-be-executed statement (which would leave us with a dangling temporary BLOB)
// Reset the blob to ensure that even when the query fails,
// the blob does not contain leftover data from whatever it
// contained before.
bbe->reset();
ociData_ = bbe->get_lob_locator();

// Allocate our very own LOB locator. We have to do this instead of
// reusing the locator from the existing Blob object in case the
// statement for which we are binding is going to be executed multiple
// times. If we borrowed the locator from the Blob object, consecutive
// queries would override a previous locator (which might belong
// to an independent Blob object by now).
sword res = OCIDescriptorAlloc(statement_.session_.envhp_,
reinterpret_cast<dvoid**>(&ociData_), OCI_DTYPE_LOB, 0, 0);
if (res != OCI_SUCCESS)
{
throw soci_error("Cannot allocate the LOB locator");
}


size = sizeof(ociData_);
data = &ociData_;
Expand Down Expand Up @@ -384,6 +399,7 @@ void oracle_standard_into_type_backend::clean_up()

if (type_ == x_blob)
{
OCIDescriptorFree(ociData_, OCI_DTYPE_LOB);
ociData_ = NULL;
}

Expand Down

0 comments on commit f110c69

Please sign in to comment.