Skip to content

Commit 53e6ae4

Browse files
committed
Fix #8082 by removing mapInOut() routine and one intermediate message buffer
1 parent 3c388fb commit 53e6ae4

33 files changed

+870
-929
lines changed

Diff for: src/dsql/DsqlBatch.cpp

+16-21
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,6 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadat
203203

204204
const auto statement = req->getDsqlStatement();
205205

206-
if (statement->getFlags() & DsqlStatement::FLAG_ORPHAN)
207-
{
208-
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
209-
Arg::Gds(isc_bad_req_handle));
210-
}
211-
212206
switch (statement->getType())
213207
{
214208
case DsqlStatement::TYPE_INSERT:
@@ -224,7 +218,7 @@ DsqlBatch* DsqlBatch::open(thread_db* tdbb, DsqlDmlRequest* req, IMessageMetadat
224218
}
225219

226220
const dsql_msg* message = statement->getSendMsg();
227-
if (! (inMetadata && message && req->parseMetadata(inMetadata, message->msg_parameters)))
221+
if (! (inMetadata && message && message->msg_parameter > 0))
228222
{
229223
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
230224
Arg::Gds(isc_batch_param));
@@ -654,18 +648,23 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb)
654648
// execute request
655649
m_dsqlRequest->req_transaction = transaction;
656650
Request* req = m_dsqlRequest->getRequest();
651+
DsqlStatement* dStmt = m_dsqlRequest->getDsqlStatement();
657652
fb_assert(req);
658653

659654
// prepare completion interface
660655
AutoPtr<BatchCompletionState, SimpleDispose> completionState
661656
(FB_NEW BatchCompletionState(m_flags & (1 << IBatch::TAG_RECORD_COUNTS), m_detailed));
662657
AutoSetRestore<bool> batchFlag(&req->req_batch_mode, true);
663-
const dsql_msg* message = m_dsqlRequest->getDsqlStatement()->getSendMsg();
658+
const dsql_msg* sendMessage = dStmt->getSendMsg();
659+
// map message to internal engine format
660+
// Do it one time only to avoid parsing its metadata for every message
661+
m_dsqlRequest->metadataToFormat(m_meta, sendMessage);
662+
// Using of positional DML in batch is strange but not forbidden
663+
m_dsqlRequest->mapCursorKey(tdbb);
664664
bool startRequest = true;
665665

666-
bool isExecBlock = m_dsqlRequest->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_BLOCK;
667-
const auto receiveMessage = isExecBlock ? m_dsqlRequest->getDsqlStatement()->getReceiveMsg() : nullptr;
668-
auto receiveMsgBuffer = isExecBlock ? m_dsqlRequest->req_msg_buffers[receiveMessage->msg_buffer_number] : nullptr;
666+
bool isExecBlock = dStmt->getType() == DsqlStatement::TYPE_EXEC_BLOCK;
667+
const dsql_msg* receiveMessage = isExecBlock ? dStmt->getReceiveMsg() : nullptr;
669668

670669
// process messages
671670
ULONG remains;
@@ -721,25 +720,18 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb)
721720
*id = newId;
722721
}
723722

724-
// map message to internal engine format
725-
// pass m_meta one time only to avoid parsing its metadata for every message
726-
m_dsqlRequest->mapInOut(tdbb, false, message, start ? m_meta : nullptr, nullptr, data);
727-
data += m_messageSize;
728-
remains -= m_messageSize;
729-
730-
UCHAR* msgBuffer = m_dsqlRequest->req_msg_buffers[message->msg_buffer_number];
731723
try
732724
{
733725
// runsend data to request and collect stats
734726
ULONG before = req->req_records_inserted + req->req_records_updated +
735727
req->req_records_deleted;
736-
EXE_send(tdbb, req, message->msg_number, message->msg_length, msgBuffer);
728+
EXE_send(tdbb, req, sendMessage->msg_number, m_messageSize, data);
737729
ULONG after = req->req_records_inserted + req->req_records_updated +
738730
req->req_records_deleted;
739731
completionState->regUpdate(after - before);
740732

741-
if (isExecBlock)
742-
EXE_receive(tdbb, req, receiveMessage->msg_number, receiveMessage->msg_length, receiveMsgBuffer);
733+
if (receiveMessage)
734+
EXE_receive(tdbb, req, receiveMessage->msg_number, receiveMessage->msg_length, nullptr); // We don't care about returned record
743735
}
744736
catch (const Exception& ex)
745737
{
@@ -759,6 +751,9 @@ Firebird::IBatchCompletionState* DsqlBatch::execute(thread_db* tdbb)
759751

760752
startRequest = true;
761753
}
754+
755+
data += m_messageSize;
756+
remains -= m_messageSize;
762757
}
763758

764759
UCHAR* alignedData = FB_ALIGN(data, m_alignment);

Diff for: src/dsql/DsqlCompilerScratch.cpp

+1-10
Original file line numberDiff line numberDiff line change
@@ -421,10 +421,7 @@ dsql_var* DsqlCompilerScratch::resolveVariable(const MetaName& varName)
421421
// Generate BLR for a return.
422422
void DsqlCompilerScratch::genReturn(bool eosFlag)
423423
{
424-
const bool hasEos = !(flags & (FLAG_TRIGGER | FLAG_FUNCTION));
425-
426-
if (hasEos && !eosFlag)
427-
appendUChar(blr_begin);
424+
const bool hasEos = !(flags & (FLAG_TRIGGER | FLAG_FUNCTION | FLAG_EXEC_BLOCK));
428425

429426
appendUChar(blr_send);
430427
appendUChar(1);
@@ -455,12 +452,6 @@ void DsqlCompilerScratch::genReturn(bool eosFlag)
455452
}
456453

457454
appendUChar(blr_end);
458-
459-
if (hasEos && !eosFlag)
460-
{
461-
appendUChar(blr_stall);
462-
appendUChar(blr_end);
463-
}
464455
}
465456

466457
void DsqlCompilerScratch::genParameters(Array<NestConst<ParameterClause> >& parameters,

Diff for: src/dsql/DsqlCompilerScratch.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ typedef Firebird::Pair<
5151
Firebird::NonPooled<NestConst<ValueListNode>, NestConst<ValueListNode>>> ReturningClause;
5252

5353

54-
// DSQL Compiler scratch block - may be discarded after compilation in the future.
54+
// DSQL Compiler scratch block.
55+
// Contains any kind of objects used during DsqlStatement compilation
56+
// Is deleted with its pool as soon as DsqlStatement is fully formed in prepareStatement()
57+
// or with the statement itself (if the statement reqested it returning true from shouldPreserveScratch())
5558
class DsqlCompilerScratch : public BlrDebugWriter
5659
{
5760
public:
@@ -70,6 +73,7 @@ class DsqlCompilerScratch : public BlrDebugWriter
7073
static const unsigned FLAG_DDL = 0x2000;
7174
static const unsigned FLAG_FETCH = 0x4000;
7275
static const unsigned FLAG_VIEW_WITH_CHECK = 0x8000;
76+
static const unsigned FLAG_EXEC_BLOCK = 0x100000;
7377

7478
static const unsigned MAX_NESTING = 512;
7579

@@ -105,7 +109,7 @@ class DsqlCompilerScratch : public BlrDebugWriter
105109

106110
protected:
107111
// DsqlCompilerScratch should never be destroyed using delete.
108-
// It dies together with it's pool in release_request().
112+
// It dies together with it's pool.
109113
~DsqlCompilerScratch()
110114
{
111115
}
@@ -317,6 +321,7 @@ class DsqlCompilerScratch : public BlrDebugWriter
317321
DsqlCompilerScratch* mainScratch = nullptr;
318322
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <outer, inner>
319323
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <outer, inner>
324+
dsql_msg* recordKeyMessage = nullptr; // Side message for positioned DML
320325

321326
private:
322327
Firebird::HalfStaticArray<SelectExprNode*, 4> ctes; // common table expressions

Diff for: src/dsql/DsqlCursor.cpp

+72-19
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "../dsql/dsql_proto.h"
3030
#include "../dsql/DsqlCursor.h"
31+
#include "../dsql/StmtNodes.h"
3132

3233
using namespace Firebird;
3334
using namespace Jrd;
@@ -36,7 +37,11 @@ static const char* const SCRATCH = "fb_cursor_";
3637
static const ULONG PREFETCH_SIZE = 65536; // 64 KB
3738

3839
DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags)
39-
: m_dsqlRequest(req), m_message(req->getDsqlStatement()->getReceiveMsg()),
40+
: m_dsqlRequest(req),
41+
m_keyBuffer(nullptr),
42+
m_keyBufferLength(0),
43+
m_message(req->getDsqlStatement()->getReceiveMsg()->msg_number),
44+
m_messageLength(0),
4045
m_resultSet(NULL), m_flags(flags),
4146
m_space(req->getPool(), SCRATCH),
4247
m_state(BOS), m_eof(false), m_position(0), m_cachedCount(0)
@@ -48,6 +53,11 @@ DsqlCursor::~DsqlCursor()
4853
{
4954
if (m_resultSet)
5055
m_resultSet->resetHandle();
56+
if (m_keyBuffer)
57+
{
58+
delete[] m_keyBuffer;
59+
m_keyBuffer = nullptr;
60+
}
5161
}
5262

5363
jrd_tra* DsqlCursor::getTransaction() const
@@ -66,6 +76,17 @@ void DsqlCursor::setInterfacePtr(JResultSet* interfacePtr) noexcept
6676
m_resultSet = interfacePtr;
6777
}
6878

79+
bool DsqlCursor::getCurrentRecordKey(USHORT context, RecordKey& key) const
80+
{
81+
if (context * sizeof(RecordKey) >= m_keyBufferLength)
82+
{
83+
return false;
84+
}
85+
86+
key = m_keyBuffer[context];
87+
return key.recordNumber.bid_relation_id != 0;
88+
}
89+
6990
void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor)
7091
{
7192
if (!cursor)
@@ -88,7 +109,7 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor)
88109

89110
if (dsqlRequest->req_traced && TraceManager::need_dsql_free(attachment))
90111
{
91-
TraceSQLStatementImpl stmt(dsqlRequest, NULL);
112+
TraceSQLStatementImpl stmt(dsqlRequest, nullptr, nullptr);
92113
TraceManager::event_dsql_free(attachment, &stmt, DSQL_close);
93114
}
94115

@@ -115,6 +136,15 @@ int DsqlCursor::fetchNext(thread_db* tdbb, UCHAR* buffer)
115136
return 1;
116137
}
117138

139+
if (m_keyBufferLength == 0)
140+
{
141+
Request* req = m_dsqlRequest->getRequest();
142+
m_keyBufferLength = req->req_rpb.getCount() * sizeof(RecordKey);
143+
fb_assert(m_keyBufferLength > 0);
144+
m_keyBuffer = new RecordKey[req->req_rpb.getCount()];
145+
}
146+
147+
m_dsqlRequest->gatherRecordKey(m_keyBuffer);
118148
m_state = POSITIONED;
119149
return 0;
120150
}
@@ -163,7 +193,7 @@ int DsqlCursor::fetchAbsolute(thread_db* tdbb, UCHAR* buffer, SLONG position)
163193
{
164194
if (!m_eof)
165195
{
166-
cacheInput(tdbb);
196+
cacheInput(tdbb, buffer);
167197
fb_assert(m_eof);
168198
}
169199

@@ -248,7 +278,7 @@ void DsqlCursor::getInfo(thread_db* tdbb,
248278
case IResultSet::INF_RECORD_COUNT:
249279
if (isScrollable && !m_eof)
250280
{
251-
cacheInput(tdbb);
281+
cacheInput(tdbb, nullptr);
252282
fb_assert(m_eof);
253283
}
254284
response.insertInt(tag, isScrollable ? m_cachedCount : -1);
@@ -291,48 +321,71 @@ int DsqlCursor::fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 positio
291321
{
292322
if (position >= m_cachedCount)
293323
{
294-
if (m_eof || !cacheInput(tdbb, position))
324+
if (m_eof || !cacheInput(tdbb, buffer, position))
295325
{
296326
m_state = EOS;
297327
return 1;
298328
}
299329
}
300330

301331
fb_assert(position < m_cachedCount);
332+
fb_assert(m_messageLength > 0); // At this point m_messageLength must be set by cacheInput
302333

303-
UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number];
304-
305-
const FB_UINT64 offset = position * m_message->msg_length;
306-
const FB_UINT64 readBytes = m_space.read(offset, msgBuffer, m_message->msg_length);
307-
fb_assert(readBytes == m_message->msg_length);
308-
309-
m_dsqlRequest->mapInOut(tdbb, true, m_message, NULL, buffer);
334+
FB_UINT64 offset = position * (m_messageLength + m_keyBufferLength);
335+
FB_UINT64 readBytes = m_space.read(offset, buffer, m_messageLength);
336+
offset += m_messageLength;
337+
readBytes += m_space.read(offset, m_keyBuffer, m_keyBufferLength);
338+
fb_assert(readBytes == m_messageLength + m_keyBufferLength);
310339

311340
m_position = position;
312341
m_state = POSITIONED;
313342
return 0;
314343
}
315344

316-
bool DsqlCursor::cacheInput(thread_db* tdbb, FB_UINT64 position)
345+
bool DsqlCursor::cacheInput(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position)
317346
{
318347
fb_assert(!m_eof);
319348

320-
const ULONG prefetchCount = MAX(PREFETCH_SIZE / m_message->msg_length, 1);
321-
const UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number];
349+
// It could not be done before: user buffer length may be unknown until call setDelayedOutputMetadata()
350+
if (m_messageLength == 0)
351+
{
352+
Request* req = m_dsqlRequest->getRequest();
353+
const MessageNode* msg = req->getStatement()->getMessage(m_message);
354+
m_messageLength = msg->getFormat(req)->fmt_length;
355+
// Save record key unconditionally because setCursorName() can be called after openCursor()
356+
m_keyBufferLength = req->req_rpb.getCount() * sizeof(RecordKey);
357+
m_keyBuffer = new RecordKey[req->req_rpb.getCount()];
358+
}
359+
360+
std::unique_ptr<UCHAR[]> ownBuffer;
361+
if (buffer == nullptr)
362+
{
363+
// We are called from getInfo() and there is no user-provided buffer for data.
364+
// Create a temporary one.
365+
// This code cannot be moved into getInfo() itself because it is most likely called before fetch()
366+
// so m_messageLength is still unknown there.
367+
ownBuffer.reset(buffer = new UCHAR[m_messageLength]);
368+
}
369+
370+
const ULONG prefetchCount = MAX(PREFETCH_SIZE / (m_messageLength + m_keyBufferLength), 1);
322371

323372
while (position >= m_cachedCount)
324373
{
325374
for (ULONG count = 0; count < prefetchCount; count++)
326375
{
327-
if (!m_dsqlRequest->fetch(tdbb, NULL))
376+
if (!m_dsqlRequest->fetch(tdbb, buffer))
328377
{
329378
m_eof = true;
330379
break;
331380
}
332381

333-
const FB_UINT64 offset = m_cachedCount * m_message->msg_length;
334-
const FB_UINT64 writtenBytes = m_space.write(offset, msgBuffer, m_message->msg_length);
335-
fb_assert(writtenBytes == m_message->msg_length);
382+
m_dsqlRequest->gatherRecordKey(m_keyBuffer);
383+
384+
FB_UINT64 offset = m_cachedCount * (m_messageLength + m_keyBufferLength);
385+
FB_UINT64 writtenBytes = m_space.write(offset, buffer, m_messageLength);
386+
offset += m_messageLength;
387+
writtenBytes += m_space.write(offset, m_keyBuffer, m_keyBufferLength);
388+
fb_assert(writtenBytes == m_messageLength + m_keyBufferLength);
336389
m_cachedCount++;
337390
}
338391

Diff for: src/dsql/DsqlCursor.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace Jrd {
2929

3030
class DsqlDmlRequest;
3131
class JResultSet;
32+
struct RecordKey;
3233

3334
class DsqlCursor
3435
{
@@ -41,6 +42,7 @@ class DsqlCursor
4142
jrd_tra* getTransaction() const;
4243
Attachment* getAttachment() const;
4344
void setInterfacePtr(JResultSet* interfacePtr) noexcept;
45+
bool getCurrentRecordKey(USHORT context, RecordKey& key) const;
4446

4547
static void close(thread_db* tdbb, DsqlCursor* cursor);
4648

@@ -67,10 +69,13 @@ class DsqlCursor
6769

6870
private:
6971
int fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position);
70-
bool cacheInput(thread_db* tdbb, FB_UINT64 position = MAX_UINT64);
72+
bool cacheInput(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position = MAX_UINT64);
7173

7274
DsqlDmlRequest* const m_dsqlRequest;
73-
const dsql_msg* const m_message;
75+
RecordKey* m_keyBuffer;
76+
ULONG m_keyBufferLength;
77+
const USHORT m_message;
78+
ULONG m_messageLength;
7479
JResultSet* m_resultSet;
7580
const ULONG m_flags;
7681
TempSpace m_space;

0 commit comments

Comments
 (0)