From 0d72b8097c292dabb5c9a257a157f20d9362ab26 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 | 170 ++++++++++++++++---------------- src/jrd/SysFunction.h | 1 + src/jrd/optimizer/Optimizer.cpp | 10 +- 7 files changed, 154 insertions(+), 88 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 676c98d833b..8320ab355be 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -396,6 +396,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; @@ -12390,6 +12404,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; @@ -13306,6 +13325,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 e59c3878031..fb79beccf57 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -799,6 +799,11 @@ class FieldNode final : public TypedNode dsqlDesc = desc; } + virtual bool deterministic() const override + { + return true; + } + virtual bool possiblyUnknown() const { return false; @@ -875,6 +880,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; @@ -1634,6 +1644,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 65ee4ed7e0a..ce4f7c0d51d 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -472,6 +472,11 @@ class ProcedureSourceNode final : public TypedNodecsb_rpt[rseStream].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 = getConjuncts(); iter.hasData(); ++iter) { if (!(iter & CONJUNCT_USED) && + iter->deterministic() && iter->computable(csb, INVALID_STREAM, false)) { compose(getPool(), &invariantBoolean, iter);