Skip to content

Commit f5a9a02

Browse files
committed
Handle outdated format error in a case of selectable procedure. Added ability to force-update version of an object in the cache.
1 parent 5a9c0b5 commit f5a9a02

8 files changed

Lines changed: 144 additions & 26 deletions

File tree

src/dsql/DsqlRequests.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "../dsql/errd_proto.h"
3636
#include "../dsql/movd_proto.h"
3737
#include "../jrd/exe_proto.h"
38+
#include "../common/utils_proto.h"
3839

3940
using namespace Firebird;
4041
using namespace Jrd;
@@ -553,17 +554,47 @@ void DsqlDmlRequest::doExecute(thread_db* tdbb, jrd_tra** traHandle,
553554
firstRowFetched = false;
554555
const dsql_msg* message = dsqlStatement->getSendMsg();
555556

556-
if (!message)
557-
{
558-
JRD_start(tdbb, request, req_transaction);
559-
}
560-
else
557+
for (int i = 0; i < EXEC_RESTARTS; ++i)
561558
{
562-
fb_assert(inMsg != nullptr);
559+
try
560+
{
561+
if (!message)
562+
{
563+
JRD_start(tdbb, request, req_transaction);
564+
}
565+
else
566+
{
567+
fb_assert(inMsg != nullptr);
568+
569+
const ULONG inMsgLength = dsqlStatement->getStatement()->getMessage(message->msg_number)->
570+
getFormat(request)->fmt_length;
571+
JRD_start_and_send(tdbb, request, req_transaction, message->msg_number,
572+
inMsgLength, inMsg);
573+
}
563574

564-
const ULONG inMsgLength = dsqlStatement->getStatement()->getMessage(message->msg_number)->getFormat(request)->fmt_length;
565-
JRD_start_and_send(tdbb, request, req_transaction, message->msg_number,
566-
inMsgLength, inMsg);
575+
break;
576+
}
577+
catch (const Exception& ex)
578+
{
579+
FbLocalStatus st;
580+
ex.stuffException(&st);
581+
582+
if (fb_utils::containsErrorCode(st->getErrors(), isc_old_format))
583+
{
584+
// destroy existing request
585+
auto* statement = request->getStatement();
586+
EXE_release(tdbb, request);
587+
588+
// create fresh one (with changed metadata in cache)
589+
request = statement->findRequest(tdbb);
590+
tdbb->getAttachment()->att_requests.add(request);
591+
592+
tdbb->tdbb_status_vector->init();
593+
continue;
594+
}
595+
596+
throw;
597+
}
567598
}
568599

569600
// Selectable execute block should get the "proc fetch" flag assigned,

src/include/firebird/impl/msg/jrd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,3 +1021,4 @@ FB_IMPL_MSG(JRD, 1018, dsql_agg_param_not_accum, -204, "42", "000", "Aggregate f
10211021
FB_IMPL_MSG(JRD, 1019, dsql_agg_exit_group, -204, "42", "000", "EXIT is not allowed in ON GROUP DO section of aggregate function")
10221022
FB_IMPL_MSG(JRD, 1020, dsql_agg_return, -204, "42", "000", "RETURN is not allowed in ON START DO, ON ACCUMULATE DO or ON FINISH DO sections of aggregate function; use EXIT instead")
10231023
FB_IMPL_MSG(JRD, 1021, hypfun_args_non_equal_sort_item, -833, "42", "000", "Number of arguments of hypothetical-set aggregate function @1 must match number of sort items in WITHIN GROUP clause")
1024+
FB_IMPL_MSG(JRD, 1022, old_format, -804, "07", "000", "Statement format outdated, need to be reprepared")

src/include/gen/Firebird.pas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6063,6 +6063,7 @@ IPerformanceStatsImpl = class(IPerformanceStats)
60636063
isc_dsql_agg_exit_group = 335545339;
60646064
isc_dsql_agg_return = 335545340;
60656065
isc_hypfun_args_non_equal_sort_item = 335545341;
6066+
isc_old_format = 335545342;
60666067
isc_gfix_db_name = 335740929;
60676068
isc_gfix_invalid_sw = 335740930;
60686069
isc_gfix_incmp_sw = 335740932;

src/jrd/CacheVector.h

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,8 @@ namespace CacheFlag
115115
static constexpr ObjectBase::Flag NOCOMMIT = 0x010; // do not commit created version
116116
static constexpr ObjectBase::Flag NOERASED = 0x020; // never return erased version, skip till older object
117117
static constexpr ObjectBase::Flag RETIRED = 0x040; // object is in a process of GC
118-
static constexpr ObjectBase::Flag UPGRADE = 0x080; // create new versions for already existing in a cache objects
119-
static constexpr ObjectBase::Flag MINISCAN = 0x100; // perform minimum scan and set cache entry to reload state
120-
static constexpr ObjectBase::Flag DB_VERSION = 0x200; // execute version upgrade in database
118+
static constexpr ObjectBase::Flag MINISCAN = 0x080; // perform minimum scan and set cache entry to reload state
119+
static constexpr ObjectBase::Flag DB_VERSION = 0x100; // execute version upgrade in database
121120

122121
// Useful combinations
123122
static constexpr ObjectBase::Flag TAG_FOR_UPDATE = NOCOMMIT | MINISCAN | DB_VERSION;
@@ -163,11 +162,13 @@ class ListEntry : public HazardObject
163162
public:
164163
enum State { INITIAL, RELOAD, MISSING, SCANNING, READY };
165164

166-
ListEntry(Versioned* object, TraNumber traNumber, ObjectBase::Flag fl)
165+
ListEntry(Versioned* object, TraNumber traNumber, ObjectBase::Flag fl, ListEntry* link = nullptr)
167166
: object(object), traNumber(traNumber), cacheFlags(fl), state(INITIAL)
168167
{
169168
if (fl & CacheFlag::ERASED)
170169
fb_assert(!object);
170+
if (link)
171+
next.store(link);
171172
}
172173

173174
~ListEntry()
@@ -548,6 +549,21 @@ class ListEntry : public HazardObject
548549
return state == READY ? false : (thd == Thread::getCurrentThreadId()) && (state == SCANNING);
549550
}
550551

552+
static bool upgradable(HazardPtr<ListEntry>& listEntry, const Versioned* from)
553+
{
554+
for (; listEntry; listEntry.set(listEntry->next))
555+
{
556+
if (listEntry->object == from)
557+
return false; // not found upgrade version
558+
559+
if (listEntry->getFlags() & CacheFlag::COMMITTED)
560+
return true; // already upgraded by someone else
561+
}
562+
563+
fb_assert(false);
564+
return false; // miss from what to upgrade
565+
}
566+
551567
private:
552568

553569
// object (nill/not nill) & ERASED bit in cacheFlags together control state of cache element
@@ -951,6 +967,36 @@ class CacheElement : public ElementBase, public P
951967
}
952968
}
953969

970+
bool upgrade(thread_db* tdbb, const Versioned* from)
971+
{
972+
HazardPtr<ListEntry<Versioned>> l(list);
973+
974+
// list of versions should be present
975+
fb_assert(l);
976+
if (!l)
977+
return false;
978+
979+
// if there is another version at the top nothing to be added
980+
if (l->getVersioned() != from)
981+
return ListEntry<Versioned>::upgradable(l, from);
982+
983+
// we have candidate for upgrade - make sure it's not half-done
984+
fb_assert(l->getFlags() & CacheFlag::COMMITTED);
985+
if (!(l->getFlags() & CacheFlag::COMMITTED))
986+
return false;
987+
988+
// Try to upgrade
989+
ListEntry<Versioned>* newEntry = FB_NEW ListEntry<Versioned>(nullptr, TransactionNumber::current(tdbb),
990+
CacheFlag::COMMITTED | CacheFlag::MINISCAN | CacheFlag::DB_VERSION, l.getPointer());
991+
if (l.replace(list, newEntry))
992+
return true;
993+
delete newEntry;
994+
995+
// Someone already added entry - see is it OK for us
996+
l.set(list);
997+
return ListEntry<Versioned>::upgradable(l, from);
998+
}
999+
9541000
private:
9551001
void setNewResetAt(TraNumber oldVal, TraNumber newVal)
9561002
{
@@ -1063,7 +1109,6 @@ class CacheVector : public Firebird::PermanentStorage
10631109

10641110
Versioned* getVersioned(thread_db* tdbb, MetaId id, ObjectBase::Flag fl)
10651111
{
1066-
10671112
// In theory that should be endless cycle - object may arrive/disappear again and again.
10681113
// But in order to faster find devel problems we run it very limited number of times.
10691114
#ifdef DEV_BUILD
@@ -1077,17 +1122,7 @@ class CacheVector : public Firebird::PermanentStorage
10771122
{
10781123
StoredElement* data = ptr->load(atomics::memory_order_acquire);
10791124
if (data)
1080-
{
1081-
if (fl & CacheFlag::UPGRADE)
1082-
{
1083-
auto val = makeObject(tdbb, id, fl);
1084-
if (val)
1085-
return val;
1086-
continue;
1087-
}
1088-
10891125
return data->getVersioned(tdbb, fl);
1090-
}
10911126
}
10921127

10931128
if (!(fl & CacheFlag::AUTOCREATE))
@@ -1170,6 +1205,28 @@ class CacheVector : public Firebird::PermanentStorage
11701205
return data;
11711206
}
11721207

1208+
bool upgrade(thread_db* tdbb, MetaId id, const Versioned* from)
1209+
{
1210+
fb_assert(id < getCount());
1211+
1212+
if (id < getCount())
1213+
{
1214+
auto ptr = getDataPointer(id);
1215+
fb_assert(ptr);
1216+
1217+
if (ptr)
1218+
{
1219+
StoredElement* data = ptr->load(atomics::memory_order_acquire);
1220+
fb_assert(data);
1221+
1222+
if (data)
1223+
return data->upgrade(tdbb, from);
1224+
}
1225+
}
1226+
1227+
return false;
1228+
}
1229+
11731230
template <typename F>
11741231
StoredElement* lookup(thread_db* tdbb, F&& cmp, ObjectBase::Flag fl) const
11751232
{
@@ -1188,6 +1245,7 @@ class CacheVector : public Firebird::PermanentStorage
11881245
auto listEntry = ptr->getEntry(tdbb, TransactionNumber::current(tdbb), fl | CacheFlag::MINISCAN);
11891246
if (listEntry && cmp(ptr))
11901247
{
1248+
// if (!(fl & (CacheFlag::ERASED | CacheFlag::MINISCAN)))
11911249
if (!(fl & CacheFlag::ERASED))
11921250
ptr->reload(tdbb, fl); // found object to be reloaded w/o MINISCAN flag
11931251
return ptr;

src/jrd/Resources.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal)
5555

5656
[[noreturn]] void Resources::outdated()
5757
{
58-
ERR_post(Arg::Gds(isc_random) << "Statement format outdated, need to be reprepared");
58+
ERR_post(Arg::Gds(isc_old_format));
5959
}
6060

6161
Resources::~Resources()

src/jrd/constants.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,4 +520,7 @@ inline constexpr USHORT MAX_ERROR_MSG_LENGTH = 1024 * METADATA_BYTES_PER_CHAR; /
520520
// Prefix of index that's getting dropped
521521
inline constexpr const char* TEMP_DEPEND = "RDB$TEMP_DEPEND";
522522

523+
// How many times request can be restarted in attempts to sync formats in it
524+
inline constexpr int EXEC_RESTARTS = 8;
525+
523526
#endif // JRD_CONSTANTS_H

src/jrd/met.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,15 @@ class MetadataCache : public Firebird::PermanentStorage
391391
return vector.newVersion(tdbb, id);
392392
}
393393

394+
// Upgrade object in cache from requested version
395+
396+
template <typename C, typename V>
397+
static bool upgrade(thread_db* tdbb, MetaId id, V* from)
398+
{
399+
auto& vector = Vector<C>::get(getCache(tdbb));
400+
return vector.upgrade(tdbb, id, from);
401+
}
402+
394403
// Mark object in cache as dropped
395404

396405
template <typename C>

src/jrd/recsrc/ProcedureScan.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "../jrd/trace/TraceManager.h"
3232
#include "../jrd/trace/TraceJrdHelpers.h"
3333
#include "../jrd/optimizer/Optimizer.h"
34+
#include "../common/utils_proto.h"
3435

3536
#include "RecordSource.h"
3637

@@ -116,7 +117,21 @@ void ProcedureScan::internalOpen(thread_db* tdbb) const
116117
im = NULL;
117118
}
118119

119-
Request* const proc_request = proc->getStatement()->findRequest(tdbb);
120+
Request* proc_request = nullptr;
121+
try
122+
{
123+
proc_request = proc->getStatement()->findRequest(tdbb);
124+
}
125+
catch (const Exception& ex)
126+
{
127+
FbLocalStatus st;
128+
ex.stuffException(&st);
129+
130+
if (fb_utils::containsErrorCode(st->getErrors(), isc_old_format))
131+
MetadataCache::upgrade<Cached::Procedure>(tdbb, m_procedure()->getId(), proc);
132+
133+
throw;
134+
}
120135
impure->irsb_req_handle = proc_request;
121136

122137
// req_proc_fetch flag used only when fetching rows, so

0 commit comments

Comments
 (0)