From 483643c470d34e0cbd0192ff60a653c8786a8198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Cser=C3=A9p?= Date: Fri, 25 Jul 2025 10:36:58 +0200 Subject: [PATCH] Don't construct queries with too many OR statements for SQLite. --- .../parser/src/cppmetricsparser.cpp | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 188762cda..1379bea4a 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -315,6 +315,60 @@ void CppMetricsParser::lackOfCohesion() // Record these fields for later use. fieldHashes.insert(field.entityHash); } + std::size_t fieldCount = fieldHashes.size(); + +#ifdef DATABASE_SQLITE + /* SQLite has a fixed-size recursive descent parser, and each logical operator (like OR) adds a + * level to the parser's stack. Constructing queries with many ORs can lead to a stack overflow. + */ + + // Counter variables. + std::size_t methodCount = 0; + std::size_t totalCohesion = 0; + + for (const model::CohesionCppMethodView& method + : _ctx.db->query(QMethodTypeHash == type.entityHash)) + { + // Do not consider methods with no explicit bodies. + const model::Position start(method.startLine, method.startColumn); + const model::Position end(method.endLine, method.endColumn); + + if (!(start < end)) + { + continue; + } + + std::unordered_set usedFields; + + // Query AST nodes that use a variable for reading or writing... + for (const model::CohesionCppAstNodeView& node + : _ctx.db->query( + // ... in the same file as the current method + (QNodeFilePath == method.filePath && + // ... within the textual scope of the current method's body. + (QNodeRange.start.line > start.line + || (QNodeRange.start.line == start.line + && QNodeRange.start.column >= start.column)) && + (QNodeRange.end.line < end.line + || (QNodeRange.end.line == end.line + && QNodeRange.end.column <= end.column))) + )) + { + // If this AST node is a reference to a field of the type... + if (fieldHashes.find(node.entityHash) != fieldHashes.end()) + { + // ... then mark it as used by this method. + usedFields.insert(node.entityHash); + } + } + + ++methodCount; + totalCohesion += usedFields.size(); + } +#else + /* With more advanced database engines like PostgreSQL or MySQL we can choose an optimized query strategy. + * These engines use much more robust parsing engine and can handle deeply nested expressions. + */ // Query all methods of the current type. std::vector methods; @@ -354,7 +408,6 @@ void CppMetricsParser::lackOfCohesion() } // Counter variables. - std::size_t fieldCount = fieldHashes.size(); std::size_t methodCount = methods.size(); std::size_t totalCohesion = 0; @@ -384,6 +437,7 @@ void CppMetricsParser::lackOfCohesion() totalCohesion += usedFields.size(); } +#endif // Calculate and record metrics. const double dF = fieldCount;