diff --git a/src/sql/ObjectIdentifier.cpp b/src/sql/ObjectIdentifier.cpp index cbe26055..4e8a56b3 100644 --- a/src/sql/ObjectIdentifier.cpp +++ b/src/sql/ObjectIdentifier.cpp @@ -1,6 +1,15 @@ #include "ObjectIdentifier.h" -#include +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, '"') + '"'; } } diff --git a/src/sql/sqlitetypes.cpp b/src/sql/sqlitetypes.cpp index 134bc0b2..585efc01 100644 --- a/src/sql/sqlitetypes.cpp +++ b/src/sql/sqlitetypes.cpp @@ -6,9 +6,18 @@ #include #include #include // This include seems to only be necessary for the Windows build -#include #include +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; diff --git a/src/sql/sqlitetypes.h b/src/sql/sqlitetypes.h index 861e9018..20be76fa 100644 --- a/src/sql/sqlitetypes.h +++ b/src/sql/sqlitetypes.h @@ -24,6 +24,12 @@ bool compare_ci(const T& a, const T& b) }); } +template +bool compare_ci(const T& a, const char* b) +{ + return compare_ci(a, std::string(b)); +} + namespace sqlb { using StringVector = std::vector;