-
-
Notifications
You must be signed in to change notification settings - Fork 233
Fix for #8082 by making engine to use user buffers directly #8145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
53e6ae4
f8cb4a6
4a3153c
e6bb98f
9b887ed
7a35dbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ | |
|
||
#include "../dsql/dsql_proto.h" | ||
#include "../dsql/DsqlCursor.h" | ||
#include "../dsql/StmtNodes.h" | ||
|
||
using namespace Firebird; | ||
using namespace Jrd; | ||
|
@@ -36,10 +37,10 @@ static const char* const SCRATCH = "fb_cursor_"; | |
static const ULONG PREFETCH_SIZE = 65536; // 64 KB | ||
|
||
DsqlCursor::DsqlCursor(DsqlDmlRequest* req, ULONG flags) | ||
: m_dsqlRequest(req), m_message(req->getDsqlStatement()->getReceiveMsg()), | ||
m_resultSet(NULL), m_flags(flags), | ||
m_space(req->getPool(), SCRATCH), | ||
m_state(BOS), m_eof(false), m_position(0), m_cachedCount(0) | ||
: m_dsqlRequest(req), | ||
m_message(req->getDsqlStatement()->getReceiveMsg()->msg_number), | ||
m_flags(flags), | ||
m_space(req->getPool(), SCRATCH) | ||
{ | ||
TRA_link_cursor(m_dsqlRequest->req_transaction, this); | ||
} | ||
|
@@ -48,6 +49,8 @@ DsqlCursor::~DsqlCursor() | |
{ | ||
if (m_resultSet) | ||
m_resultSet->resetHandle(); | ||
|
||
delete[] m_keyBuffer; | ||
} | ||
|
||
jrd_tra* DsqlCursor::getTransaction() const | ||
|
@@ -66,6 +69,23 @@ void DsqlCursor::setInterfacePtr(JResultSet* interfacePtr) noexcept | |
m_resultSet = interfacePtr; | ||
} | ||
|
||
bool DsqlCursor::getCurrentRecordKey(USHORT context, RecordKey& key) const | ||
{ | ||
if (m_keyBuffer == nullptr || context * sizeof(RecordKey) >= m_keyBufferLength) | ||
{ | ||
fb_assert(false); | ||
return false; | ||
} | ||
|
||
if (m_state != POSITIONED) | ||
{ | ||
return false; | ||
} | ||
|
||
key = m_keyBuffer[context]; | ||
return key.recordNumber.bid_relation_id != 0; | ||
} | ||
|
||
void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) | ||
{ | ||
if (!cursor) | ||
|
@@ -88,7 +108,7 @@ void DsqlCursor::close(thread_db* tdbb, DsqlCursor* cursor) | |
|
||
if (dsqlRequest->req_traced && TraceManager::need_dsql_free(attachment)) | ||
{ | ||
TraceSQLStatementImpl stmt(dsqlRequest, NULL); | ||
TraceSQLStatementImpl stmt(dsqlRequest, nullptr, nullptr); | ||
TraceManager::event_dsql_free(attachment, &stmt, DSQL_close); | ||
} | ||
|
||
|
@@ -115,6 +135,15 @@ int DsqlCursor::fetchNext(thread_db* tdbb, UCHAR* buffer) | |
return 1; | ||
} | ||
|
||
if (m_keyBufferLength == 0) | ||
{ | ||
Request* req = m_dsqlRequest->getRequest(); | ||
m_keyBufferLength = req->req_rpb.getCount() * sizeof(RecordKey); | ||
fb_assert(m_keyBufferLength > 0); | ||
m_keyBuffer = new RecordKey[req->req_rpb.getCount()]; | ||
} | ||
|
||
m_dsqlRequest->gatherRecordKey(m_keyBuffer); | ||
m_state = POSITIONED; | ||
return 0; | ||
} | ||
|
@@ -163,7 +192,7 @@ int DsqlCursor::fetchAbsolute(thread_db* tdbb, UCHAR* buffer, SLONG position) | |
{ | ||
if (!m_eof) | ||
{ | ||
cacheInput(tdbb); | ||
cacheInput(tdbb, buffer); | ||
fb_assert(m_eof); | ||
} | ||
|
||
|
@@ -248,7 +277,7 @@ void DsqlCursor::getInfo(thread_db* tdbb, | |
case IResultSet::INF_RECORD_COUNT: | ||
if (isScrollable && !m_eof) | ||
{ | ||
cacheInput(tdbb); | ||
cacheInput(tdbb, nullptr); | ||
fb_assert(m_eof); | ||
} | ||
response.insertInt(tag, isScrollable ? m_cachedCount : -1); | ||
|
@@ -291,48 +320,71 @@ int DsqlCursor::fetchFromCache(thread_db* tdbb, UCHAR* buffer, FB_UINT64 positio | |
{ | ||
if (position >= m_cachedCount) | ||
{ | ||
if (m_eof || !cacheInput(tdbb, position)) | ||
if (m_eof || !cacheInput(tdbb, buffer, position)) | ||
{ | ||
m_state = EOS; | ||
return 1; | ||
} | ||
} | ||
|
||
fb_assert(position < m_cachedCount); | ||
fb_assert(m_messageLength > 0); // At this point m_messageLength must be set by cacheInput | ||
|
||
UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; | ||
|
||
const FB_UINT64 offset = position * m_message->msg_length; | ||
const FB_UINT64 readBytes = m_space.read(offset, msgBuffer, m_message->msg_length); | ||
fb_assert(readBytes == m_message->msg_length); | ||
|
||
m_dsqlRequest->mapInOut(tdbb, true, m_message, NULL, buffer); | ||
FB_UINT64 offset = position * (m_messageLength + m_keyBufferLength); | ||
FB_UINT64 readBytes = m_space.read(offset, buffer, m_messageLength); | ||
offset += m_messageLength; | ||
readBytes += m_space.read(offset, m_keyBuffer, m_keyBufferLength); | ||
fb_assert(readBytes == m_messageLength + m_keyBufferLength); | ||
|
||
m_position = position; | ||
m_state = POSITIONED; | ||
return 0; | ||
} | ||
|
||
bool DsqlCursor::cacheInput(thread_db* tdbb, FB_UINT64 position) | ||
bool DsqlCursor::cacheInput(thread_db* tdbb, UCHAR* buffer, FB_UINT64 position) | ||
{ | ||
fb_assert(!m_eof); | ||
|
||
const ULONG prefetchCount = MAX(PREFETCH_SIZE / m_message->msg_length, 1); | ||
const UCHAR* const msgBuffer = m_dsqlRequest->req_msg_buffers[m_message->msg_buffer_number]; | ||
// It could not be done before: user buffer length may be unknown until call setDelayedOutputMetadata() | ||
if (m_messageLength == 0) | ||
{ | ||
Request* req = m_dsqlRequest->getRequest(); | ||
const MessageNode* msg = req->getStatement()->getMessage(m_message); | ||
m_messageLength = msg->getFormat(req)->fmt_length; | ||
// Save record key unconditionally because setCursorName() can be called after openCursor() | ||
m_keyBufferLength = req->req_rpb.getCount() * sizeof(RecordKey); | ||
m_keyBuffer = new RecordKey[req->req_rpb.getCount()]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and below (for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
std::unique_ptr<UCHAR[]> ownBuffer; | ||
if (buffer == nullptr) | ||
{ | ||
// We are called from getInfo() and there is no user-provided buffer for data. | ||
// Create a temporary one. | ||
// This code cannot be moved into getInfo() itself because it is most likely called before fetch() | ||
// so m_messageLength is still unknown there. | ||
ownBuffer.reset(buffer = new UCHAR[m_messageLength]); | ||
} | ||
|
||
const ULONG prefetchCount = MAX(PREFETCH_SIZE / (m_messageLength + m_keyBufferLength), 1); | ||
|
||
while (position >= m_cachedCount) | ||
{ | ||
for (ULONG count = 0; count < prefetchCount; count++) | ||
{ | ||
if (!m_dsqlRequest->fetch(tdbb, NULL)) | ||
if (!m_dsqlRequest->fetch(tdbb, buffer)) | ||
{ | ||
m_eof = true; | ||
break; | ||
} | ||
|
||
const FB_UINT64 offset = m_cachedCount * m_message->msg_length; | ||
const FB_UINT64 writtenBytes = m_space.write(offset, msgBuffer, m_message->msg_length); | ||
fb_assert(writtenBytes == m_message->msg_length); | ||
m_dsqlRequest->gatherRecordKey(m_keyBuffer); | ||
|
||
FB_UINT64 offset = m_cachedCount * (m_messageLength + m_keyBufferLength); | ||
FB_UINT64 writtenBytes = m_space.write(offset, buffer, m_messageLength); | ||
offset += m_messageLength; | ||
writtenBytes += m_space.write(offset, m_keyBuffer, m_keyBufferLength); | ||
fb_assert(writtenBytes == m_messageLength + m_keyBufferLength); | ||
m_cachedCount++; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that we copy the whole
m_keyBufferLength
chunk of bytes to the temp space and back even if they're useless for us (non-relations or no cursor name is assigned).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment below describes it: "
setCursorName()
can be called afteropenCursor()
" so there is no way to predict if key values are useless or not.