8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 10:00:38 +01:00

Attempt to fix CORE-5393: Bad optimization of some operations with views containing subqueries.

This commit is contained in:
Dmitry Yemanov 2016-11-08 10:59:36 +03:00
parent ad5368a73c
commit 05c3ccc6cf
3 changed files with 116 additions and 93 deletions

View File

@ -65,6 +65,103 @@
using namespace Firebird;
using namespace Jrd;
namespace
{
// Expand DBKEY for view
void expandViewNodes(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
ValueExprNodeStack& stack, UCHAR blrOp)
{
const CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[stream];
// If the stream's dbkey should be ignored, do so
if (csb_tail->csb_flags & csb_no_dbkey)
return;
// If the stream references a view, follow map
const StreamType* map = csb_tail->csb_map;
if (map)
{
++map;
while (*map)
expandViewNodes(tdbb, csb, *map++, stack, blrOp);
return;
}
// Relation is primitive - make DBKEY node
if (csb_tail->csb_relation)
{
RecordKeyNode* node = FB_NEW_POOL(csb->csb_pool) RecordKeyNode(csb->csb_pool, blrOp);
node->recStream = stream;
stack.push(node);
}
}
// Try to expand the given stream. If it's a view reference, collect its base streams
// (the ones directly residing in the FROM clause) and recurse.
void expandViewStreams(CompilerScratch* csb, StreamType stream, SortedStreamList& streams)
{
const CompilerScratch::csb_repeat* const csb_tail = &csb->csb_rpt[stream];
const RseNode* const rse =
csb_tail->csb_relation ? csb_tail->csb_relation->rel_view_rse : NULL;
// If we have a view, collect its base streams and remap/expand them.
if (rse)
{
const StreamType* const map = csb_tail->csb_map;
fb_assert(map);
StreamList viewStreams;
rse->computeRseStreams(viewStreams);
for (StreamType* iter = viewStreams.begin(); iter != viewStreams.end(); ++iter)
{
// Remap stream and expand it recursively
expandViewStreams(csb, map[*iter], streams);
}
return;
}
// Otherwise, just add the current stream to the list.
if (!streams.exist(stream))
streams.add(stream);
}
// Look at all RSEs which are lower in scope than the RSE which this field is
// referencing, and mark them as variant - the rule is that if a field from one RSE is
// referenced within the scope of another RSE, the inner RSE can't be invariant.
// This won't optimize all cases, but it is the simplest operating assumption for now.
void markVariant(CompilerScratch* csb, StreamType stream)
{
if (csb->csb_current_nodes.isEmpty())
return;
for (ExprNode** node = csb->csb_current_nodes.end() - 1;
node >= csb->csb_current_nodes.begin(); --node)
{
RseNode* const rseNode = (*node)->as<RseNode>();
if (rseNode)
{
if (rseNode->containsStream(stream))
break;
rseNode->flags |= RseNode::FLAG_VARIANT;
}
else if (*node)
(*node)->nodFlags &= ~ExprNode::FLAG_INVARIANT;
}
}
}
namespace Jrd {
@ -4119,7 +4216,7 @@ ValueExprNode* DerivedExprNode::copy(thread_db* tdbb, NodeCopier& copier) const
{
#ifdef CMP_DEBUG
csb->dump("remap nod_derived_expr:\n");
for (const auto i : node->streamList)
for (const auto i : node->internalStreamList)
csb->dump("\t%d: %d -> %d\n", i, i, copier.remap[i]);
#endif
@ -4134,33 +4231,32 @@ ValueExprNode* DerivedExprNode::copy(thread_db* tdbb, NodeCopier& copier) const
ValueExprNode* DerivedExprNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
ValueExprNodeStack stack;
ValueExprNode::pass1(tdbb, csb);
#ifdef CMP_DEBUG
csb->dump("expand nod_derived_expr:");
for (const auto i : streamList)
for (const auto i : internalStreamList)
csb->dump(" %d", i);
csb->dump("\n");
#endif
SortedStreamList newStreams;
for (const auto i : internalStreamList)
{
CMP_mark_variant(csb, i);
CMP_expand_view_nodes(tdbb, csb, i, stack, blr_dbkey, true);
markVariant(csb, i);
expandViewStreams(csb, i, newStreams);
}
internalStreamList.clear();
#ifdef CMP_DEBUG
for (ExprValueNodeStack::iterator i(stack); i.hasData(); ++i)
csb->dump(" %d", i.object()->as<RecordKeyNode>()->recStream);
for (const auto i : newStreams)
csb->dump(" %d", i);
csb->dump("\n");
#endif
for (ValueExprNodeStack::iterator i(stack); i.hasData(); ++i)
internalStreamList.add(i.object()->as<RecordKeyNode>()->recStream);
internalStreamList.assign(newStreams.begin(), newStreams.getCount());
return ValueExprNode::pass1(tdbb, csb);
return this;
}
ValueExprNode* DerivedExprNode::pass2(thread_db* tdbb, CompilerScratch* csb)
@ -5479,7 +5575,7 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
StreamType stream = fieldStream;
CMP_mark_variant(csb, stream);
markVariant(csb, stream);
CompilerScratch::csb_repeat* tail = &csb->csb_rpt[stream];
jrd_rel* relation = tail->csb_relation;
@ -5673,21 +5769,12 @@ ValueExprNode* FieldNode::pass1(thread_db* tdbb, CompilerScratch* csb)
if (!view_refs)
{
ValueExprNodeStack stack;
CMP_expand_view_nodes(tdbb, csb, stream, stack, blr_dbkey, true);
const size_t streamCount = stack.getCount();
DerivedExprNode* derivedNode =
FB_NEW_POOL(*tdbb->getDefaultPool()) DerivedExprNode(*tdbb->getDefaultPool());
derivedNode->arg = sub;
derivedNode->internalStreamList.add(stream);
if (streamCount)
{
DerivedExprNode* derivedNode =
FB_NEW_POOL(*tdbb->getDefaultPool()) DerivedExprNode(*tdbb->getDefaultPool());
derivedNode->arg = sub;
for (ValueExprNodeStack::iterator i(stack); i.hasData(); ++i)
derivedNode->internalStreamList.add(i.object()->as<RecordKeyNode>()->recStream);
sub = derivedNode;
}
sub = derivedNode;
}
doPass1(tdbb, csb, &sub); // note: scope of AutoSetRestore
@ -8576,13 +8663,13 @@ ValueExprNode* RecordKeyNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
ValueExprNode::pass1(tdbb, csb);
CMP_mark_variant(csb, recStream);
markVariant(csb, recStream);
if (!csb->csb_rpt[recStream].csb_map)
return this;
ValueExprNodeStack stack;
CMP_expand_view_nodes(tdbb, csb, recStream, stack, blrOp, false);
expandViewNodes(tdbb, csb, recStream, stack, blrOp);
#ifdef CMP_DEBUG
csb->dump("expand RecordKeyNode: %d\n", recStream);

View File

@ -480,67 +480,6 @@ USHORT NodeCopier::getFieldId(const FieldNode* field)
}
// Expand dbkey for view.
void CMP_expand_view_nodes(thread_db* tdbb, CompilerScratch* csb, StreamType stream,
ValueExprNodeStack& stack, UCHAR blrOp, bool allStreams)
{
SET_TDBB(tdbb);
DEV_BLKCHK(csb, type_csb);
// if the stream's dbkey should be ignored, do so
if (!allStreams && (csb->csb_rpt[stream].csb_flags & csb_no_dbkey))
return;
// if the stream references a view, follow map
const StreamType* map = csb->csb_rpt[stream].csb_map;
if (map)
{
++map;
while (*map)
CMP_expand_view_nodes(tdbb, csb, *map++, stack, blrOp, allStreams);
return;
}
// relation is primitive - make dbkey node
if (allStreams || csb->csb_rpt[stream].csb_relation)
{
RecordKeyNode* node = FB_NEW_POOL(csb->csb_pool) RecordKeyNode(csb->csb_pool, blrOp);
node->recStream = stream;
stack.push(node);
}
}
// Look at all RseNode's which are lower in scope than the RseNode which this field is
// referencing, and mark them as variant - the rule is that if a field from one RseNode is
// referenced within the scope of another RseNode, the inner RseNode can't be invariant.
// This won't optimize all cases, but it is the simplest operating assumption for now.
void CMP_mark_variant(CompilerScratch* csb, StreamType stream)
{
if (csb->csb_current_nodes.isEmpty())
return;
for (ExprNode** node = csb->csb_current_nodes.end() - 1;
node >= csb->csb_current_nodes.begin(); --node)
{
RseNode* rseNode = (*node)->as<RseNode>();
if (rseNode)
{
if (rseNode->containsStream(stream))
break;
rseNode->flags |= RseNode::FLAG_VARIANT;
}
else if (*node)
(*node)->nodFlags &= ~ExprNode::FLAG_INVARIANT;
}
}
// Copy items' information into appropriate node.
ItemInfo* CMP_pass2_validation(thread_db* tdbb, CompilerScratch* csb, const Item& item)
{

View File

@ -40,13 +40,10 @@ Jrd::ValueExprNode* CMP_clone_node(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::
Jrd::jrd_req* CMP_compile2(Jrd::thread_db*, const UCHAR* blr, ULONG blr_length, bool internal_flag,
ULONG = 0, const UCHAR* = NULL);
Jrd::CompilerScratch::csb_repeat* CMP_csb_element(Jrd::CompilerScratch*, StreamType element);
void CMP_expand_view_nodes(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, StreamType stream,
Jrd::ValueExprNodeStack& stack, UCHAR blrOp, bool allStreams);
const Jrd::Format* CMP_format(Jrd::thread_db*, Jrd::CompilerScratch*, StreamType);
Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT);
ULONG CMP_impure(Jrd::CompilerScratch*, ULONG);
Jrd::jrd_req* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool);
void CMP_mark_variant(Jrd::CompilerScratch*, StreamType stream);
Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&);
void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Firebird::MetaName&, SLONG ssRelationId,