From f0b7429a1873ed9470838da61bdca0bce748652d Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Thu, 14 Dec 2023 20:05:52 +0300 Subject: [PATCH] Do not consider non-deterministic expressions as invariants in pre-filters (#7853) * Do not consider non-deterministic expressions as invariants in pre-filters * Follow Adriano's suggestion * Allow deterministic uncorrelated subqueries to be considered as invariants --- src/dsql/ExprNodes.cpp | 24 +++++ src/dsql/ExprNodes.h | 29 ++++++ src/dsql/Nodes.h | 3 + src/jrd/RecordSourceNodes.h | 5 + src/jrd/SysFunction.cpp | 172 ++++++++++++++++---------------- src/jrd/SysFunction.h | 1 + src/jrd/optimizer/Optimizer.cpp | 10 +- 7 files changed, 155 insertions(+), 89 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 1ccfec3c8dd..7b479213f5a 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -369,6 +369,20 @@ bool ExprNode::sameAs(const ExprNode* other, bool ignoreStreams) const return true; } +bool ExprNode::deterministic() const +{ + NodeRefsHolder holder; + getChildren(holder, false); + + for (auto i : holder.refs) + { + if (*i && !(*i)->deterministic()) + return false; + } + + return true; +} + bool ExprNode::possiblyUnknown() const { NodeRefsHolder holder; @@ -12333,6 +12347,11 @@ void SysFuncCallNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) function->makeFunc(&dataTypeUtil, function, desc, argsArray.getCount(), argsArray.begin()); } +bool SysFuncCallNode::deterministic() const +{ + return ExprNode::deterministic() && function->deterministic; +} + void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { Array argsArray; @@ -12980,6 +12999,11 @@ void UdfCallNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) desc->setTextType(dsqlFunction->udf_character_set_id); } +bool UdfCallNode::deterministic() const +{ + return ExprNode::deterministic() && function->fun_deterministic; +} + void UdfCallNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { // Null value for the function indicates that the function was not diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 57d173bb5fb..b5a03189acc 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -786,6 +786,11 @@ class FieldNode final : public TypedNode dsqlDesc = desc; } + virtual bool deterministic() const override + { + return true; + } + virtual bool possiblyUnknown() const { return false; @@ -862,6 +867,11 @@ class GenIdNode final : public TypedNode virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); + virtual bool deterministic() const override + { + return false; + } + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; @@ -1618,6 +1628,11 @@ class ParameterNode final : public TypedNodedsqlFieldRemapper(visitor) : NULL; } + // Check if expression returns deterministic result + virtual bool deterministic() const; + // Check if expression could return NULL or expression can turn NULL into a true/false. virtual bool possiblyUnknown() const; diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index 9594d63f1ef..83c599c2a56 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -479,6 +479,11 @@ class ProcedureSourceNode final : public TypedNodecsb_rpt[*i].deactivate(); - // Find and collect booleans that are invariant in this context - // (i.e. independent from streams in the RseNode). We can do that - // easily because these streams are inactive at this point and - // any node that references them will be not computable. + // Find and collect booleans that are both deterministic and invariant + // in this context (i.e. independent from streams in the current RseNode). + // We can check that easily because these streams are inactive at this point + // and any node that references them will be not computable. // Note that we cannot do that for outer joins, as in this case boolean // represents a join condition which does not filter out the rows. BoolExprNode* invariantBoolean = nullptr; + if (isInnerJoin()) { for (auto iter = getBaseConjuncts(); iter.hasData(); ++iter) { if (!(iter & CONJUNCT_USED) && + iter->deterministic() && iter->computable(csb, INVALID_STREAM, false)) { compose(getPool(), &invariantBoolean, iter);