diff options
-rw-r--r-- | odb/sqlite/query.cxx | 139 | ||||
-rw-r--r-- | odb/sqlite/query.hxx | 61 |
2 files changed, 142 insertions, 58 deletions
diff --git a/odb/sqlite/query.cxx b/odb/sqlite/query.cxx index 16d8cc2..c3fd31e 100644 --- a/odb/sqlite/query.cxx +++ b/odb/sqlite/query.cxx @@ -128,6 +128,10 @@ namespace odb // query // + // query + // + const query query::true_expr (true); + query:: query (const query& q) : clause_ (q.clause_), @@ -197,6 +201,56 @@ namespace odb parameters_->add (p); } + static bool + check_prefix (const string& s) + { + string::size_type n; + + // It is easier to compare to upper and lower-case versions + // rather than getting involved with the portable case- + // insensitive string comparison mess. + // + if (s.compare (0, (n = 5), "WHERE") == 0 || + s.compare (0, (n = 5), "where") == 0 || + s.compare (0, (n = 6), "SELECT") == 0 || + s.compare (0, (n = 6), "select") == 0 || + s.compare (0, (n = 8), "ORDER BY") == 0 || + s.compare (0, (n = 8), "order by") == 0 || + s.compare (0, (n = 8), "GROUP BY") == 0 || + s.compare (0, (n = 8), "group by") == 0 || + s.compare (0, (n = 6), "HAVING") == 0 || + s.compare (0, (n = 6), "having") == 0) + { + // It either has to be an exact match, or there should be + // a whitespace following the keyword. + // + if (s.size () == n || s[n] == ' ' || s[n] =='\t') + return true; + } + + return false; + } + + void query:: + optimize () + { + // Remove a single TRUE literal or one that is followe by one of + // the other clauses. This avoids usless WHERE clauses like + // + // WHERE TRUE GROUP BY foo + // + clause_type::iterator i (clause_.begin ()), e (clause_.end ()); + + if (i != e && i->kind == clause_part::boolean && i->bool_part) + { + clause_type::iterator j (i + 1); + + if (j == e || + (j->kind == clause_part::native && check_prefix (j->part))) + clause_.erase (i); + } + } + const char* query:: clause_prefix () const { @@ -204,33 +258,8 @@ namespace odb { const clause_part& p (clause_.front ()); - if (p.kind == clause_part::native) - { - const string& s (p.part); - string::size_type n; - - // It is easier to compare to upper and lower-case versions - // rather than getting involved with the portable case- - // insensitive string comparison mess. - // - if (s.compare (0, (n = 5), "WHERE") == 0 || - s.compare (0, (n = 5), "where") == 0 || - s.compare (0, (n = 6), "SELECT") == 0 || - s.compare (0, (n = 6), "select") == 0 || - s.compare (0, (n = 8), "ORDER BY") == 0 || - s.compare (0, (n = 8), "order by") == 0 || - s.compare (0, (n = 8), "GROUP BY") == 0 || - s.compare (0, (n = 8), "group by") == 0 || - s.compare (0, (n = 6), "HAVING") == 0 || - s.compare (0, (n = 6), "having") == 0) - { - // It either has to be an exact match, or there should be - // a whitespace following the keyword. - // - if (s.size () == n || s[n] == ' ' || s[n] =='\t') - return ""; - } - } + if (p.kind == clause_part::native && check_prefix (p.part)) + return ""; return "WHERE "; } @@ -244,7 +273,9 @@ namespace odb string r; for (clause_type::const_iterator i (clause_.begin ()), - end (clause_.end ()); i != end; ++i) + end (clause_.end ()); + i != end; + ++i) { char last (!r.empty () ? r[r.size () - 1] : ' '); @@ -281,10 +312,62 @@ namespace odb r += p; break; } + case clause_part::boolean: + { + if (last != ' ' && last != '(') + r += ' '; + + r += i->bool_part ? "1" : "0"; + break; + } } } return clause_prefix () + r; } + + query + operator&& (const query& x, const query& y) + { + // Optimize cases where one or both sides are constant truth. + // + bool xt (x.const_true ()), yt (y.const_true ()); + + if (xt && yt) + return x; + + if (xt) + return y; + + if (yt) + return x; + + query r ("("); + r += x; + r += ") AND ("; + r += y; + r += ")"; + return r; + } + + query + operator|| (const query& x, const query& y) + { + query r ("("); + r += x; + r += ") OR ("; + r += y; + r += ")"; + return r; + } + + query + operator! (const query& x) + { + query r ("NOT ("); + r += x; + r += ")"; + return r; + } } } diff --git a/odb/sqlite/query.hxx b/odb/sqlite/query.hxx index c712b58..a3c63ba 100644 --- a/odb/sqlite/query.hxx +++ b/odb/sqlite/query.hxx @@ -121,14 +121,17 @@ namespace odb { column, param, - native + native, + boolean }; clause_part (kind_type k): kind (k) {} clause_part (kind_type k, const std::string& p): kind (k), part (p) {} + clause_part (bool p): kind (boolean), bool_part (p) {} kind_type kind; std::string part; + bool bool_part; }; query () @@ -142,7 +145,7 @@ namespace odb query (bool v) : parameters_ (new (details::shared) query_params) { - clause_.push_back (clause_part (clause_part::native, v ? "1" : "0")); + clause_.push_back (clause_part (v)); } explicit @@ -203,6 +206,26 @@ namespace odb parameters () const; public: + bool + empty () const + { + return clause_.empty (); + } + + static const query true_expr; + + bool + const_true () const + { + return clause_.size () == 1 && + clause_.front ().kind == clause_part::boolean && + clause_.front ().bool_part; + } + + void + optimize (); + + public: template <typename T> static val_bind<T> _val (const T& x) @@ -370,36 +393,14 @@ namespace odb return r; } - inline query - operator&& (const query& x, const query& y) - { - query r ("("); - r += x; - r += ") AND ("; - r += y; - r += ")"; - return r; - } + query + operator&& (const query&, const query&); - inline query - operator|| (const query& x, const query& y) - { - query r ("("); - r += x; - r += ") OR ("; - r += y; - r += ")"; - return r; - } + query + operator|| (const query&, const query&); - inline query - operator! (const query& x) - { - query r ("NOT ("); - r += x; - r += ")"; - return r; - } + query + operator! (const query&); // query_column // |