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:
parent
ad5368a73c
commit
05c3ccc6cf
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user