From 894c72c73dc07ce417e5411bd3ac50f4c0461a60 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sun, 2 Oct 2011 17:07:20 +0200 Subject: If query substituting placeholder is empty, pass true expression instead This allows uniform usage of views both with and without any extra conditions. Also optimize some common cases so that we don't have useless WHERE TRUE clauses or (...) AND (TRUE) expressions. --- odb/sqlite/query.cxx | 139 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 28 deletions(-) (limited to 'odb/sqlite/query.cxx') 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; + } } } -- cgit v1.1