Avoid using std::regex for now

It turned out that on some platforms we still aim to support the stdlib
had not yet implemented std::regex. For this reason we want to avoid
using it for now.

In the particular use cases here it was also not the best solution. It
was faster to write using a regular expression instead of a hand coded
loop but clearly a simple loop should be more efficient here.

See issue #1873.
This commit is contained in:
Martin Kleusberg
2019-05-05 15:35:50 +02:00
parent d87f253940
commit 37aaa4c7f2
3 changed files with 76 additions and 16 deletions

View File

@@ -1,6 +1,15 @@
#include "ObjectIdentifier.h"
#include <regex>
static std::string duplicate_char(std::string str, char to_replace)
{
size_t pos = 0;
while((pos = str.find(to_replace, pos)) != std::string::npos)
{
str.insert(pos, 1, to_replace);
pos += 2; // Advance by two characters in order to avoid escaping a character multiple times
}
return str;
}
namespace sqlb {
@@ -15,7 +24,7 @@ std::string escapeIdentifier(const std::string& id)
{
switch(customQuoting) {
case GraveAccents:
return '`' + std::regex_replace(id, std::regex("\\`"), std::string("``")) + '`';
return '`' + duplicate_char(id, '`') + '`';
case SquareBrackets:
// There aren't any escaping possibilities for square brackets inside the identifier,
// so we rely on the user to not enter these characters when this kind of quoting is
@@ -26,7 +35,7 @@ std::string escapeIdentifier(const std::string& id)
// This may produce a 'control reaches end of non-void function' warning if the
// default branch is removed, even though we have covered all possibilities in the
// switch statement.
return '"' + std::regex_replace(id, std::regex("\\\""), std::string("\"\"")) + '"';
return '"' + duplicate_char(id, '"') + '"';
}
}

View File

@@ -6,9 +6,18 @@
#include <sstream>
#include <iostream>
#include <clocale> // This include seems to only be necessary for the Windows build
#include <regex>
#include <numeric>
namespace {
bool starts_with_ci(const std::string& str, const std::string& with)
{
if(str.size() < with.size())
return false;
else
return compare_ci(str.substr(0, with.size()), with);
}
}
namespace sqlb {
StringVector escapeIdentifier(StringVector ids)
@@ -235,32 +244,55 @@ std::string Field::toString(const std::string& indent, const std::string& sep) c
bool Field::isText() const
{
std::regex is_text("(^(character|varchar|varying character|nchar|native character|nvarchar))|(^(text|clob)$)", std::regex::icase);
return std::regex_match(m_type, is_text);
if(starts_with_ci(m_type, "character")) return true;
if(starts_with_ci(m_type, "varchar")) return true;
if(starts_with_ci(m_type, "varying character")) return true;
if(starts_with_ci(m_type, "nchar")) return true;
if(starts_with_ci(m_type, "native character")) return true;
if(starts_with_ci(m_type, "nvarchar")) return true;
if(compare_ci(m_type, "text")) return true;
if(compare_ci(m_type, "clob")) return true;
return false;
}
bool Field::isInteger() const
{
std::regex is_integer("^(int|integer|tinyint|smallint|mediumint|bigint|unsigned big int|int2|int8)$", std::regex::icase);
return std::regex_match(m_type, is_integer);
if(compare_ci(m_type, "int")) return true;
if(compare_ci(m_type, "integer")) return true;
if(compare_ci(m_type, "tinyint")) return true;
if(compare_ci(m_type, "smallint")) return true;
if(compare_ci(m_type, "mediumint")) return true;
if(compare_ci(m_type, "bigint")) return true;
if(compare_ci(m_type, "unsigned big int")) return true;
if(compare_ci(m_type, "int2")) return true;
if(compare_ci(m_type, "int8")) return true;
return false;
}
bool Field::isReal() const
{
std::regex is_real("^(real|double|double precision|float)$", std::regex::icase);
return std::regex_match(m_type, is_real);
if(compare_ci(m_type, "real")) return true;
if(compare_ci(m_type, "double")) return true;
if(compare_ci(m_type, "double precision")) return true;
if(compare_ci(m_type, "float")) return true;
return false;
}
bool Field::isNumeric() const
{
std::regex is_numeric("(^(decimal))|(^(numeric|boolean|date|datetime)$)", std::regex::icase);
return std::regex_match(m_type, is_numeric);
if(starts_with_ci(m_type, "decimal")) return true;
if(compare_ci(m_type, "numeric")) return true;
if(compare_ci(m_type, "boolean")) return true;
if(compare_ci(m_type, "date")) return true;
if(compare_ci(m_type, "datetime")) return true;
return false;
}
bool Field::isBlob() const
{
std::regex is_blob("(^$)|(^blob$)", std::regex::icase);
return std::regex_match(m_type, is_blob);
if(m_type.empty()) return true;
if(compare_ci(m_type, "blob")) return true;
return false;
}
std::string Field::affinity() const
@@ -591,6 +623,19 @@ void Table::renameKeyInAllConstraints(const std::string& key, const std::string&
namespace
{
std::string unescape_identifier(std::string str, char quote_char)
{
std::string quote(2, quote_char);
size_t pos = 0;
while((pos = str.find(quote, pos)) != std::string::npos)
{
str.erase(pos, 1);
pos += 1; // Don't remove the other quote char too
}
return str;
}
std::string identifier(antlr::RefAST ident)
{
std::string sident = ident->getText();
@@ -599,7 +644,7 @@ std::string identifier(antlr::RefAST ident)
ident->getType() == sqlite3TokenTypes::STRINGLITERAL)
{
// Remember the way the identifier is quoted
std::string quoteChar(1, sident.at(0));
char quoteChar = sident.at(0);
// Remove first and final character, i.e. the quotes
sident = sident.substr(1, sident.size() - 2);
@@ -608,7 +653,7 @@ std::string identifier(antlr::RefAST ident)
// by a single instance. This is done because two quotes can be used as a means of escaping
// the quote character, thus only the visual representation has its two quotes, the actual
// name contains only one.
sident = std::regex_replace(sident, std::regex("\\" + quoteChar + "\\" + quoteChar), quoteChar);
sident = unescape_identifier(sident, quoteChar);
}
return sident;

View File

@@ -24,6 +24,12 @@ bool compare_ci(const T& a, const T& b)
});
}
template<typename T>
bool compare_ci(const T& a, const char* b)
{
return compare_ci(a, std::string(b));
}
namespace sqlb {
using StringVector = std::vector<std::string>;