postgis: revamp substitution of special tokens in query

- replace boost::regex with std::regex
- use regex to match both  !mapnikvar!  and  !@uservar!
- no longer support @uservar (without surrounding !s)
This commit is contained in:
Mickey Rose 2017-02-02 22:37:01 +01:00
parent 195c3e14ce
commit d331b9d66a
3 changed files with 69 additions and 105 deletions

View file

@ -52,7 +52,6 @@ libraries = copy(plugin_env['LIBS'])
if env['PLUGIN_LINKING'] == 'shared': if env['PLUGIN_LINKING'] == 'shared':
libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_system%s' % env['BOOST_APPEND'])
libraries.append('boost_regex%s' % env['BOOST_APPEND'])
libraries.insert(0,env['MAPNIK_NAME']) libraries.insert(0,env['MAPNIK_NAME'])
libraries.append(env['ICU_LIB_NAME']) libraries.append(env['ICU_LIB_NAME'])

View file

@ -38,11 +38,10 @@
#pragma GCC diagnostic push #pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp> #include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
// stl // stl
#include <cfloat> // FLT_MAX
#include <memory> #include <memory>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
@ -52,12 +51,13 @@
DATASOURCE_PLUGIN(postgis_datasource) DATASOURCE_PLUGIN(postgis_datasource)
const double postgis_datasource::FMAX = std::numeric_limits<float>::max();
const std::string postgis_datasource::GEOMETRY_COLUMNS = "geometry_columns"; const std::string postgis_datasource::GEOMETRY_COLUMNS = "geometry_columns";
const std::string postgis_datasource::SPATIAL_REF_SYS = "spatial_ref_system"; const std::string postgis_datasource::SPATIAL_REF_SYS = "spatial_ref_system";
using std::shared_ptr; using std::shared_ptr;
using mapnik::attribute_descriptor; using mapnik::attribute_descriptor;
using mapnik::sql_utils::identifier;
using mapnik::sql_utils::literal;
postgis_datasource::postgis_datasource(parameters const& params) postgis_datasource::postgis_datasource(parameters const& params)
: datasource(params), : datasource(params),
@ -79,10 +79,6 @@ postgis_datasource::postgis_datasource(parameters const& params)
params.get<std::string>("user"), params.get<std::string>("user"),
params.get<std::string>("password"), params.get<std::string>("password"),
params.get<std::string>("connect_timeout", "4")), params.get<std::string>("connect_timeout", "4")),
bbox_token_("!bbox!"),
scale_denom_token_("!scale_denominator!"),
pixel_width_token_("!pixel_width!"),
pixel_height_token_("!pixel_height!"),
pool_max_size_(*params_.get<mapnik::value_integer>("max_size", 10)), pool_max_size_(*params_.get<mapnik::value_integer>("max_size", 10)),
persist_connection_(*params.get<mapnik::boolean_type>("persist_connection", true)), persist_connection_(*params.get<mapnik::boolean_type>("persist_connection", true)),
extent_from_subquery_(*params.get<mapnik::boolean_type>("extent_from_subquery", false)), extent_from_subquery_(*params.get<mapnik::boolean_type>("extent_from_subquery", false)),
@ -99,8 +95,7 @@ postgis_datasource::postgis_datasource(parameters const& params)
simplify_prefilter_(*params_.get<mapnik::value_double>("simplify_prefilter", 0.0)), simplify_prefilter_(*params_.get<mapnik::value_double>("simplify_prefilter", 0.0)),
simplify_dp_preserve_(false), simplify_dp_preserve_(false),
simplify_clip_resolution_(*params_.get<mapnik::value_double>("simplify_clip_resolution", 0.0)), simplify_clip_resolution_(*params_.get<mapnik::value_double>("simplify_clip_resolution", 0.0)),
// TODO - use for known tokens too: "(@\\w+|!\\w+!)" re_tokens_("!(@?\\w+)!"), // matches !mapnik_var! or !@user_var!
pattern_(boost::regex("(@\\w+)",boost::regex::normal | boost::regbase::icase)),
// params below are for testing purposes only and may be removed at any time // params below are for testing purposes only and may be removed at any time
intersect_min_scale_(*params.get<mapnik::value_integer>("intersect_min_scale", 0)), intersect_min_scale_(*params.get<mapnik::value_integer>("intersect_min_scale", 0)),
intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0)), intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0)),
@ -551,43 +546,9 @@ std::string postgis_datasource::sql_bbox(box2d<double> const& env) const
std::string postgis_datasource::populate_tokens(std::string const& sql) const std::string postgis_datasource::populate_tokens(std::string const& sql) const
{ {
std::string populated_sql = sql; return populate_tokens(sql, FLT_MAX,
box2d<double>(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX),
if (boost::algorithm::icontains(sql, bbox_token_)) 0, 0, mapnik::attributes{}, false);
{
box2d<double> max_env(-1.0 * FMAX, -1.0 * FMAX, FMAX, FMAX);
const std::string max_box = sql_bbox(max_env);
boost::algorithm::replace_all(populated_sql, bbox_token_, max_box);
}
if (boost::algorithm::icontains(sql, scale_denom_token_))
{
std::ostringstream ss;
ss << FMAX;
boost::algorithm::replace_all(populated_sql, scale_denom_token_, ss.str());
}
if (boost::algorithm::icontains(sql, pixel_width_token_))
{
boost::algorithm::replace_all(populated_sql, pixel_width_token_, "0");
}
if (boost::algorithm::icontains(sql, pixel_height_token_))
{
boost::algorithm::replace_all(populated_sql, pixel_height_token_, "0");
}
std::string copy2 = populated_sql;
std::list<std::string> l;
boost::regex_split(std::back_inserter(l), copy2, pattern_);
if (!l.empty())
{
for (auto const & token: l)
{
boost::algorithm::replace_all(populated_sql, token, "null");
}
}
return populated_sql;
} }
std::string postgis_datasource::populate_tokens( std::string postgis_datasource::populate_tokens(
@ -596,43 +557,66 @@ std::string postgis_datasource::populate_tokens(
box2d<double> const& env, box2d<double> const& env,
double pixel_width, double pixel_width,
double pixel_height, double pixel_height,
mapnik::attributes const& vars) const mapnik::attributes const& vars,
bool intersect) const
{ {
std::string populated_sql = sql; std::ostringstream populated_sql;
std::string box = sql_bbox(env); std::cmatch m;
char const* start = sql.data();
char const* end = start + sql.size();
if (boost::algorithm::icontains(populated_sql, scale_denom_token_)) while (std::regex_search(start, end, m, re_tokens_))
{ {
std::ostringstream ss; populated_sql.write(start, m[0].first - start);
ss << scale_denom; start = m[0].second;
boost::algorithm::replace_all(populated_sql, scale_denom_token_, ss.str());
}
if (boost::algorithm::icontains(sql, pixel_width_token_)) auto m1 = boost::make_iterator_range(m[1].first, m[1].second);
if (m1.front() == '@')
{ {
std::ostringstream ss; std::string var_name(m1.begin() + 1, m1.end());
ss << pixel_width; auto itr = vars.find(var_name);
boost::algorithm::replace_all(populated_sql, pixel_width_token_, ss.str()); if (itr != vars.end())
}
if (boost::algorithm::icontains(sql, pixel_height_token_))
{ {
std::ostringstream ss; auto var_value = itr->second.to_string();
ss << pixel_height; populated_sql << literal(var_value);
boost::algorithm::replace_all(populated_sql, pixel_height_token_, ss.str());
}
if (boost::algorithm::icontains(populated_sql, bbox_token_))
{
boost::algorithm::replace_all(populated_sql, bbox_token_, box);
} }
else else
{ {
std::ostringstream s; populated_sql << "NULL"; // undefined @variable
}
}
else if (boost::algorithm::equals(m1, "bbox"))
{
populated_sql << sql_bbox(env);
intersect = false;
}
else if (boost::algorithm::equals(m1, "pixel_height"))
{
populated_sql << pixel_height;
}
else if (boost::algorithm::equals(m1, "pixel_width"))
{
populated_sql << pixel_width;
}
else if (boost::algorithm::equals(m1, "scale_denominator"))
{
populated_sql << scale_denom;
}
else
{
populated_sql << "NULL"; // unrecognized !token!
}
}
populated_sql.write(start, end - start);
if (intersect)
{
if (intersect_min_scale_ > 0 && (scale_denom <= intersect_min_scale_)) if (intersect_min_scale_ > 0 && (scale_denom <= intersect_min_scale_))
{ {
s << " WHERE ST_Intersects(\"" << geometryColumn_ << "\"," << box << ")"; populated_sql << " WHERE ST_Intersects("
<< identifier(geometryColumn_) << ", "
<< sql_bbox(env) << ")";
} }
else if (intersect_max_scale_ > 0 && (scale_denom >= intersect_max_scale_)) else if (intersect_max_scale_ > 0 && (scale_denom >= intersect_max_scale_))
{ {
@ -640,29 +624,13 @@ std::string postgis_datasource::populate_tokens(
} }
else else
{ {
s << " WHERE \"" << geometryColumn_ << "\" && " << box; populated_sql << " WHERE "
} << identifier(geometryColumn_) << " && "
populated_sql += s.str(); << sql_bbox(env);
}
std::string copy2 = populated_sql;
std::list<std::string> l;
boost::regex_split(std::back_inserter(l), copy2, pattern_);
if (!l.empty())
{
for (auto const & token: l)
{
auto itr = vars.find(token.substr(1,std::string::npos));
if (itr != vars.end())
{
boost::algorithm::replace_all(populated_sql, token, itr->second.to_string());
}
else
{
boost::algorithm::replace_all(populated_sql, token, "null");
} }
} }
}
return populated_sql; return populated_sql.str();
} }
@ -1015,7 +983,8 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
} }
box2d<double> box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol); box2d<double> box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol);
std::string table_with_bbox = populate_tokens(table_, FMAX, box, 0, 0, mapnik::attributes()); std::string table_with_bbox = populate_tokens(table_, FLT_MAX, box, 0, 0,
mapnik::attributes{});
s << " FROM " << table_with_bbox; s << " FROM " << table_with_bbox;

View file

@ -37,10 +37,10 @@
// boost // boost
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/regex.hpp>
// stl // stl
#include <memory> #include <memory>
#include <regex>
#include <vector> #include <vector>
#include <string> #include <string>
@ -84,12 +84,12 @@ private:
box2d<double> const& env, box2d<double> const& env,
double pixel_width, double pixel_width,
double pixel_height, double pixel_height,
mapnik::attributes const& vars) const; mapnik::attributes const& vars,
bool intersect = true) const;
std::string populate_tokens(std::string const& sql) const; std::string populate_tokens(std::string const& sql) const;
std::shared_ptr<IResultSet> get_resultset(std::shared_ptr<Connection> &conn, std::string const& sql, CnxPool_ptr const& pool, processor_context_ptr ctx= processor_context_ptr()) const; std::shared_ptr<IResultSet> get_resultset(std::shared_ptr<Connection> &conn, std::string const& sql, CnxPool_ptr const& pool, processor_context_ptr ctx= processor_context_ptr()) const;
static const std::string GEOMETRY_COLUMNS; static const std::string GEOMETRY_COLUMNS;
static const std::string SPATIAL_REF_SYS; static const std::string SPATIAL_REF_SYS;
static const double FMAX;
const std::string uri_; const std::string uri_;
const std::string username_; const std::string username_;
@ -109,10 +109,6 @@ private:
bool simplify_geometries_; bool simplify_geometries_;
layer_descriptor desc_; layer_descriptor desc_;
ConnectionCreator<Connection> creator_; ConnectionCreator<Connection> creator_;
const std::string bbox_token_;
const std::string scale_denom_token_;
const std::string pixel_width_token_;
const std::string pixel_height_token_;
int pool_max_size_; int pool_max_size_;
bool persist_connection_; bool persist_connection_;
bool extent_from_subquery_; bool extent_from_subquery_;
@ -126,7 +122,7 @@ private:
mapnik::value_double simplify_prefilter_; mapnik::value_double simplify_prefilter_;
bool simplify_dp_preserve_; bool simplify_dp_preserve_;
mapnik::value_double simplify_clip_resolution_; mapnik::value_double simplify_clip_resolution_;
boost::regex pattern_; std::regex re_tokens_;
int intersect_min_scale_; int intersect_min_scale_;
int intersect_max_scale_; int intersect_max_scale_;
bool key_field_as_attribute_; bool key_field_as_attribute_;