Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postgis parameter quoting and interpolation #31

Merged
8 changes: 8 additions & 0 deletions CHANGELOG.carto.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ Changes:
- Use metrics without guards
- Add docs/metrics.md
- Avoid unnecessary copy of features with "cache-features"
- PostGIS: Variables in postgis SQL queries must now additionally be wrapped in `!` (refs [#29](https://github.com/CartoDB/mapnik/issues/29), [mapnik/#3618](https://github.com/mapnik/mapnik/pull/3618)):
```sql
-- Before
SELECT ... WHERE trait = @variable

-- Now
SELECT ... WHERE trait = !@variable!
```

## 3.0.15.3

Expand Down
134 changes: 132 additions & 2 deletions include/mapnik/sql_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,161 @@
#pragma GCC diagnostic pop

// stl
#include <sstream>
#include <vector>
#include <iosfwd>
#include <regex>
#include <string>

namespace mapnik { namespace sql_utils {

struct quoted_string
{
std::string const* operator-> () const { return &str; }
std::string const& str;
char const quot;
};

inline quoted_string identifier(std::string const& str)
{
return { str, '"' };
}

inline quoted_string literal(std::string const& str)
{
return { str, '\'' };
}

inline std::ostream& operator << (std::ostream& os, quoted_string qs)
{
std::size_t pos = 0, next;

os.put(qs.quot);
while ((next = qs->find(qs.quot, pos)) != std::string::npos)
{
os.write(qs->data() + pos, next - pos + 1);
os.put(qs.quot);
pos = next + 1;
}
if ((next = qs->size()) > pos)
{
os.write(qs->data() + pos, next - pos);
}
return os.put(qs.quot);
}

// Does nothing if `str` doesn't start with `quot`.
// Otherwise erases the opening quote, collapses inner quote pairs,
// and erases everything from the closing quote to the end of the
// string. The closing quote is the first non-paired quote after the
// opening one. For a well-formed quoted string, it is also the last
// character, so nothing gets lost.
inline void unquote(char quot, std::string & str)
{
if (!str.empty() && str.front() == quot)
{
std::size_t di = 0;
for (std::size_t si = 1; si < str.size(); ++si)
{
char c = str[si];
if (c == quot && (++si >= str.size() || str[si] != quot))
break;
str[di++] = c;
}
str.erase(di);
}
}

inline std::string unquote_copy(char quot, std::string const& str)
{
std::string tmp(str);
sql_utils::unquote(quot, tmp);
return tmp;
}

[[deprecated("flawed")]]
inline std::string unquote_double(std::string const& sql)
{
std::string table_name = sql;
util::unquote_double(table_name);
return table_name;
}

[[deprecated("flawed")]]
inline std::string unquote(std::string const& sql)
{
std::string table_name = sql;
util::unquote(table_name);
return table_name;
}

[[deprecated("flawed")]]
inline void quote_attr(std::ostringstream & s, std::string const& field)
{
s << ",\"" << field << "\"";
}

const std::regex re_from{
"\\bFROM\\b"
, std::regex::icase
};

const std::regex re_table_name{
"\\s*(\\w+|(\"[^\"]*\")+)" // $1 = schema
"(\\.(\\w+|(\"[^\"]*\")+))?" // $4 = table
"\\s*"
};

inline bool table_from_sql(std::string const& sql,
std::string & schema,
std::string & table)
{
std::smatch m;
auto start = sql.begin();
auto end = sql.end();
auto flags = std::regex_constants::match_default;
auto found = std::regex_match(start, end, m, re_table_name);
auto extract_matched_parts = [&]()
{
if (m[4].matched)
{
table.assign(m[4].first, m[4].second);
schema.assign(m[1].first, m[1].second);
}
else
{
table.assign(m[1].first, m[1].second);
schema.clear();
}
};

if (found)
{
// input is not subquery, just "[schema.]table"
extract_matched_parts();
}
else
{
// search "FROM [schema.]table" in subquery
while (std::regex_search(start, end, m, re_from, flags))
{
start = m[0].second;
if (std::regex_search(start, end, m, re_table_name,
std::regex_constants::match_continuous))
{
extract_matched_parts();
found = true;
start = m[0].second;
}
flags = std::regex_constants::match_prev_avail;
}
}
if (found)
{
sql_utils::unquote('"', schema);
sql_utils::unquote('"', table);
}
return found;
}

inline std::string table_from_sql(std::string const& sql)
{
std::string table_name = sql;
Expand Down
Loading