diff --git a/CHANGELOG.md b/CHANGELOG.md index a5741f1..e389725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ the remaining digits are the major version. For instance, the code `000000003` is equivalent to `0.0.3` and the code `123456001` is `123.456.1.` +## `0.0.5` +- Added "bouncing" to avoid stack overflows in sqlite +- Added bounce test in unit testing + ## `0.0.4` - Added `merge_rows` for results objects, allowing easier aggregation diff --git a/src/gql.hpp b/src/gql.hpp index e26044f..539b959 100644 --- a/src/gql.hpp +++ b/src/gql.hpp @@ -56,7 +56,7 @@ OTHER DEALINGS IN THE SOFTWARE. // The GQL version. This only ever increases with versions. // This can (and should) be used in static assertions to ensure // validity. -#define GQL_VERSION 000000004ULL +#define GQL_VERSION 000000005ULL // Assert compiler version static_assert(__cplusplus > 201100L, "Invalid C++ std."); @@ -153,6 +153,15 @@ std::string format_str(const std::string &_format, #endif +// After this many characters, the query "bounces". This +// ensures that SQLite will not experience parser overflows. +// "Bouncing" replaces the existing query with its ids, leading +// to a less complicated (although not necessarily shorter) +// query. +#ifndef GQL_BOUNCE_THRESH +#define GQL_BOUNCE_THRESH 128 +#endif + /* GQL (Graph SQLite3) manager object @@ -282,10 +291,8 @@ class GQL std::string cmd; public: - Vertices(GQL *const _owner, const std::string &_cmd) - : owner(_owner), cmd(_cmd) - { - } + // Bounces may happen herein + Vertices(GQL *const _owner, const std::string &_cmd); Vertices &operator=(const Vertices &_other) { @@ -423,10 +430,8 @@ class GQL std::string cmd; public: - Edges(GQL *const _owner, const std::string &_cmd) - : owner(_owner), cmd(_cmd) - { - } + // Bounces may occur herein + Edges(GQL *const _owner, const std::string &_cmd); Edges &operator=(const Edges &_other) { @@ -644,6 +649,31 @@ inline void GQL::rollback() //////////////////////////////////////////////////////////////// +inline GQL::Vertices::Vertices(GQL *const _owner, + const std::string &_cmd) + : owner(_owner), cmd(_cmd) +{ + if (_cmd.size() > GQL_BOUNCE_THRESH) + { + // Perform query simplification "bounce" + auto ids = this->id(); + + std::string bounced_cmd = + "SELECT * FROM nodes WHERE id IN ("; + if (ids.empty()) + { + bounced_cmd += ')'; + } + for (const auto &id : ids) + { + bounced_cmd += id[0] + ","; + } + bounced_cmd.back() = ')'; + + cmd = bounced_cmd; + } +} + inline GQL::Vertices GQL::Vertices::where( const std::string &_sql_statement) { @@ -960,6 +990,31 @@ inline GQL::Edges GQL::Vertices::add_edge( //////////////////////////////////////////////////////////////// +inline GQL::Edges::Edges(GQL *const _owner, + const std::string &_cmd) + : owner(_owner), cmd(_cmd) +{ + if (_cmd.size() > GQL_BOUNCE_THRESH) + { + // Perform query simplification "bounce" + auto ids = this->id(); + + std::string bounced_cmd = + "SELECT * FROM edges WHERE id IN ("; + if (ids.empty()) + { + bounced_cmd += ')'; + } + for (const auto &id : ids) + { + bounced_cmd += id[0] + ","; + } + bounced_cmd.back() = ')'; + + cmd = bounced_cmd; + } +} + inline GQL::Edges GQL::Edges::where( const std::string &_sql_statement) { diff --git a/test/cpg.cpp b/test/cpg.cpp index 5da25ea..dc7715d 100644 --- a/test/cpg.cpp +++ b/test/cpg.cpp @@ -6,7 +6,6 @@ From another project I'm working on that could use a better GDBM #include "../src/gql.hpp" #include -#include #include #include diff --git a/test/test.cpp b/test/test.cpp index e3f51f8..2c9491e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -534,6 +534,29 @@ void test_traversals() {"4", "5"}); } +void test_bounce() +{ + GQL g("foo.db", true); + uint64_t max = 10000; + + g.add_vertex(1); + for (uint64_t i = 1; i < max; ++i) + { + g.v() + .with_id(i) + .add_edge(g.add_vertex(i + 1)) + .label("next"); + } + + // Create and evaluate a copiously long query + GQL::Vertices query = g.v().with_id(1); + for (uint64_t i = 1; i < max; ++i) + { + query = query.out().with_label("next").target(); + } + assert(std::stoull(query.id()["id"][0]) == max); +} + //////////////////////////////////////////////////////////////// int main() @@ -552,6 +575,7 @@ int main() test_set_operations(); test_lemma(); test_multiple_tag_getter(); + test_bounce(); // Clean up system("rm -f ./foo.*");