Skip to content

Commit

Permalink
Add esc_like for "LIKE"-escaping.
Browse files Browse the repository at this point in the history
This couldn't be done properly until we had encoding support, which we
now have.

Fixes #110.
  • Loading branch information
jtv committed Jan 27, 2019
1 parent 1efe59d commit 53fe65a
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 0 deletions.
26 changes: 26 additions & 0 deletions include/pqxx/connection_base.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,32 @@ public:
}

std::string quote(const binarystring &);

/// Escape string for literal LIKE match.
/** Use this when part of an SQL "LIKE" pattern should match only as a
* literal string, not as a pattern, even if it contains "%" or "_"
* characters that would normally act as wildcards.
*
* The string does not get string-escaped or quoted. You do that later.
*
* For instance, let's say you have a string @c name entered by the user,
* and you're searching a @c file column for items that match @c name
* followed by a dot and three letters. Even if @c name contains wildcard
* characters "%" or "_", you only want those to match literally, so "_"
* only matches "_" and "%" only matches a single "%".
*
* You do that by "like-escaping" @c name, appending the wildcard pattern
* @c ".___", and finally, escaping and quoting the result for inclusion in
* your query:
*
* tx.exec(
* "SELECT file FROM item WHERE file LIKE " +
* tx.quote(tx.esc_like(name) + ".___"));
*
* The SQL "LIKE" operator also lets you choose your own escape character.
* This is supported, but must be a single-byte character.
*/
std::string esc_like(const std::string &str, char escape_char='\\') const;
//@}

/// Attempt to cancel the ongoing query, if any.
Expand Down
4 changes: 4 additions & 0 deletions include/pqxx/transaction_base.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ public:
/// Escape an SQL identifier for use in a query.
std::string quote_name(const std::string &identifier) const
{ return conn().quote_name(identifier); }

/// Escape string for literal LIKE match.
std::string esc_like(const std::string &str, char escape_char='\\') const
{ return conn().esc_like(str, escape_char); }
//@}

/// Execute query
Expand Down
21 changes: 21 additions & 0 deletions src/connection_base.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,27 @@ std::string pqxx::connection_base::quote_name(const std::string &identifier)
}


std::string pqxx::connection_base::esc_like(
const std::string &str,
char escape_char) const
{
std::string out;
out.reserve(str.size());
internal::for_glyphs(
internal::enc_group(encoding_id()),
[&out, escape_char](const char *gbegin, const char *gend)
{
if ((gend - gbegin == 1) and (*gbegin == '_' or *gbegin == '%'))
out.push_back(escape_char);

for (; gbegin != gend; ++gbegin) out.push_back(*gbegin);
},
str.c_str(),
str.size());
return out;
}


pqxx::internal::reactivation_avoidance_exemption::
reactivation_avoidance_exemption(
connection_base &C) :
Expand Down
18 changes: 18 additions & 0 deletions test/unit/test_escape.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,23 @@ void test_esc_raw_unesc_raw(transaction_base &t)
}


void test_esc_like(transaction_base &tx)
{
PQXX_CHECK_EQUAL(tx.esc_like(""), "", "esc_like breaks on empty string.");
PQXX_CHECK_EQUAL(tx.esc_like("abc"), "abc", "esc_like is broken.");
PQXX_CHECK_EQUAL(tx.esc_like("_"), "\\_", "esc_like fails on underscore.");
PQXX_CHECK_EQUAL(tx.esc_like("%"), "\\%", "esc_like fails on %.");
PQXX_CHECK_EQUAL(
tx.esc_like("a%b_c"),
"a\\%b\\_c",
"esc_like breaks on mix.");
PQXX_CHECK_EQUAL(
tx.esc_like("_", '+'),
"+_",
"esc_like ignores escape character.");
}


void test_escaping()
{
connection conn;
Expand All @@ -127,6 +144,7 @@ void test_escaping()
test_quote(conn, tx);
test_quote_name(tx);
test_esc_raw_unesc_raw(tx);
test_esc_like(tx);
}


Expand Down

0 comments on commit 53fe65a

Please sign in to comment.