diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-10-02 17:07:20 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-10-02 17:07:20 +0200 |
commit | 7d3b47fc855c183a0269ee4fb8c66d5ef9545f4d (patch) | |
tree | 4af3a222a1a48893e280366ce840d38c386e96b0 | |
parent | 219d6ffefb5f71fc2a262f8b5c93f2f88e9aecd9 (diff) |
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.
-rw-r--r-- | odb/pgsql/query.cxx | 139 | ||||
-rw-r--r-- | odb/pgsql/query.hxx | 62 |
2 files changed, 142 insertions, 59 deletions
diff --git a/odb/pgsql/query.cxx b/odb/pgsql/query.cxx index 945db7a..9fcaa04 100644 --- a/odb/pgsql/query.cxx +++ b/odb/pgsql/query.cxx @@ -24,6 +24,10 @@ namespace odb { } + // query + // + const query query::true_expr (true); + query:: query (const query& q) : clause_ (q.clause_), @@ -259,6 +263,56 @@ namespace odb return native_binding_; } + 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 { @@ -266,33 +320,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 "; } @@ -307,7 +336,9 @@ namespace odb size_t param (1); 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] : ' '); @@ -347,10 +378,62 @@ namespace odb r += p; break; } + case clause_part::boolean: + { + if (last != ' ' && last != '(') + r += ' '; + + r += i->bool_part ? "TRUE" : "FALSE"; + 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/pgsql/query.hxx b/odb/pgsql/query.hxx index 438f2f5..0c3f837 100644 --- a/odb/pgsql/query.hxx +++ b/odb/pgsql/query.hxx @@ -95,14 +95,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 () @@ -116,8 +119,7 @@ namespace odb query (bool v) : binding_ (0, 0), native_binding_ (0, 0, 0, 0) { - clause_.push_back ( - clause_part (clause_part::native, v ? "TRUE" : "FALSE")); + clause_.push_back (clause_part (v)); } explicit @@ -187,6 +189,26 @@ namespace odb } 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) @@ -364,36 +386,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 // |