- cosmetics changes to sqlite datasource plugin

- added initial wkbAuto to auto determine WKB type in sqlite
This commit is contained in:
kunitoki 2011-10-18 22:19:03 +02:00
parent a6522fde77
commit b23697e1b3
7 changed files with 395 additions and 277 deletions

View file

@ -45,8 +45,9 @@ namespace mapnik
*/
enum wkbFormat
{
wkbGeneric=1,
wkbSpatiaLite=2
wkbAuto=1,
wkbGeneric=2,
wkbSpatiaLite=3
};
class MAPNIK_DECL geometry_utils

View file

@ -74,7 +74,7 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind)
row_offset_(*params_.get<int>("row_offset",0)),
row_limit_(*params_.get<int>("row_limit",0)),
desc_(*params_.get<std::string>("type"), *params_.get<std::string>("encoding","utf-8")),
format_(mapnik::wkbGeneric)
format_(mapnik::wkbAuto)
{
// TODO
// - change param from 'file' to 'dbname'
@ -83,7 +83,8 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind)
boost::optional<std::string> file = params_.get<std::string>("file");
if (!file) throw datasource_exception("Sqlite Plugin: missing <file> parameter");
if (table_.empty()) {
if (table_.empty())
{
throw mapnik::datasource_exception("Sqlite Plugin: missing <table> parameter");
}
@ -98,16 +99,16 @@ void sqlite_datasource::parse_attachdb(std::string const& attachdb) const
boost::char_separator<char> sep(",");
boost::tokenizer<boost::char_separator<char> > tok(attachdb, sep);
// The attachdb line is a comma sparated list of
// [dbname@]filename
// The attachdb line is a comma sparated list of [dbname@]filename
for (boost::tokenizer<boost::char_separator<char> >::iterator beg = tok.begin();
beg != tok.end(); ++beg)
{
std::string const& spec(*beg);
size_t atpos=spec.find('@');
size_t atpos = spec.find('@');
// See if it contains an @ sign
if (atpos==spec.npos) {
if (atpos == spec.npos)
{
throw datasource_exception("attachdb parameter has syntax dbname@filename[,...]");
}
@ -116,33 +117,34 @@ void sqlite_datasource::parse_attachdb(std::string const& attachdb) const
std::string filename = boost::trim_copy(spec.substr(atpos+1));
// Normalize the filename and make it relative to dataset_name_
if (filename.compare(":memory:") != 0) {
if (filename.compare(":memory:") != 0)
{
boost::filesystem::path child_path(filename);
// It is a relative path. Fix it.
if (!child_path.has_root_directory() && !child_path.has_root_name()) {
if (! child_path.has_root_directory() && ! child_path.has_root_name())
{
boost::filesystem::path absolute_path(dataset_name_);
// support symlinks
#if (BOOST_FILESYSTEM_VERSION == 3)
#if (BOOST_FILESYSTEM_VERSION == 3)
if (boost::filesystem::is_symlink(absolute_path))
{
absolute_path = boost::filesystem::read_symlink(absolute_path);
}
filename = boost::filesystem::absolute(absolute_path.parent_path()/filename).string();
filename = boost::filesystem::absolute(absolute_path.parent_path() / filename).string();
#else
#else
if (boost::filesystem::is_symlink(absolute_path))
{
//cannot figure out how to resolve on in v2 so just print a warning
std::clog << "###Warning: '" << absolute_path.string() << "' is a symlink which is not supported in attachdb\n";
}
filename = boost::filesystem::complete(absolute_path.branch_path()/filename).normalize().string();
filename = boost::filesystem::complete(absolute_path.branch_path() / filename).normalize().string();
#endif
#endif
}
}
@ -156,14 +158,18 @@ void sqlite_datasource::bind() const
if (is_bound_) return;
boost::optional<std::string> file = params_.get<std::string>("file");
if (!file) throw datasource_exception("Sqlite Plugin: missing <file> parameter");
if (! file) throw datasource_exception("Sqlite Plugin: missing <file> parameter");
boost::optional<std::string> key_field_name = params_.get<std::string>("key_field");
if (key_field_name) {
if (key_field_name)
{
std::string const& key_field_string = *key_field_name;
if (key_field_string.empty()) {
if (key_field_string.empty())
{
key_field_ = "rowid";
} else {
}
else
{
key_field_ = key_field_string;
}
}
@ -177,6 +183,10 @@ void sqlite_datasource::bind() const
{
if (*wkb == "spatialite")
format_ = mapnik::wkbSpatiaLite;
else if (*wkb == "generic")
format_ = mapnik::wkbGeneric;
else
format_ = mapnik::wkbAuto;
}
multiple_geometries_ = *params_.get<mapnik::boolean>("multiple_geometries",false);
@ -201,12 +211,14 @@ void sqlite_datasource::bind() const
// will default to attaching from cwd. Typicaly usage means that the
// map loader will produce full paths here.
boost::optional<std::string> attachdb = params_.get<std::string>("attachdb");
if (attachdb) {
if (attachdb)
{
parse_attachdb(*attachdb);
}
boost::optional<std::string> initdb = params_.get<std::string>("initdb");
if (initdb) {
if (initdb)
{
init_statements_.push_back(*initdb);
}
@ -216,7 +228,8 @@ void sqlite_datasource::bind() const
dataset_ = new sqlite_connection (dataset_name_);
// Execute init_statements_
for (std::vector<std::string>::const_iterator iter=init_statements_.begin(); iter!=init_statements_.end(); ++iter)
for (std::vector<std::string>::const_iterator iter = init_statements_.begin();
iter!=init_statements_.end(); ++iter)
{
#ifdef MAPNIK_DEBUG
std::clog << "Sqlite Plugin: Execute init sql: " << *iter << std::endl;
@ -224,7 +237,7 @@ void sqlite_datasource::bind() const
dataset_->execute(*iter);
}
if(geometry_table_.empty())
if (geometry_table_.empty())
{
geometry_table_ = mapnik::table_from_sql(table_);
}
@ -240,7 +253,7 @@ void sqlite_datasource::bind() const
use_pragma_table_info = false;
}
if (!use_pragma_table_info)
if (! use_pragma_table_info)
{
std::ostringstream s;
s << "SELECT " << fields_ << " FROM (" << table_ << ") LIMIT 1";
@ -250,43 +263,44 @@ void sqlite_datasource::bind() const
{
for (int i = 0; i < rs->column_count (); ++i)
{
const int type_oid = rs->column_type (i);
const char* fld_name = rs->column_name (i);
switch (type_oid)
{
case SQLITE_INTEGER:
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer));
break;
const int type_oid = rs->column_type (i);
const char* fld_name = rs->column_name (i);
switch (type_oid)
{
case SQLITE_INTEGER:
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Integer));
break;
case SQLITE_FLOAT:
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double));
break;
case SQLITE_FLOAT:
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Double));
break;
case SQLITE_TEXT:
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String));
break;
case SQLITE_TEXT:
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String));
break;
case SQLITE_NULL:
// sqlite reports based on value, not actual column type unless
// PRAGMA table_info is used so here we assume the column is a string
// which is a lesser evil than altogether dropping the column
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String));
case SQLITE_NULL:
// sqlite reports based on value, not actual column type unless
// PRAGMA table_info is used so here we assume the column is a string
// which is a lesser evil than altogether dropping the column
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String));
case SQLITE_BLOB:
if (geometry_field_.empty() &&
(boost::algorithm::icontains(fld_name,"geom") ||
boost::algorithm::icontains(fld_name,"point") ||
boost::algorithm::icontains(fld_name,"linestring") ||
boost::algorithm::icontains(fld_name,"polygon"))
)
geometry_field_ = std::string(fld_name);
break;
case SQLITE_BLOB:
if (geometry_field_.empty()
&& (boost::algorithm::icontains(fld_name, "geom") ||
boost::algorithm::icontains(fld_name, "point") ||
boost::algorithm::icontains(fld_name, "linestring") ||
boost::algorithm::icontains(fld_name, "polygon")))
{
geometry_field_ = std::string(fld_name);
}
break;
default:
default:
#ifdef MAPNIK_DEBUG
std::clog << "Sqlite Plugin: unknown type_oid=" << type_oid << std::endl;
std::clog << "Sqlite Plugin: unknown type_oid=" << type_oid << std::endl;
#endif
break;
break;
}
}
}
@ -301,85 +315,95 @@ void sqlite_datasource::bind() const
}
if (key_field_ == "rowid")
desc_.add_descriptor(attribute_descriptor("rowid",mapnik::Integer));
desc_.add_descriptor(attribute_descriptor("rowid", mapnik::Integer));
if (use_pragma_table_info)
{
std::ostringstream s;
s << "PRAGMA table_info(" << geometry_table_ << ")";
boost::scoped_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str()));
bool found_table = false;
while (rs->is_valid() && rs->step_next())
{
found_table = true;
// TODO - support unicode strings?
// TODO - support unicode strings
const char* fld_name = rs->column_text(1);
std::string fld_type(rs->column_text(2));
boost::algorithm::to_lower(fld_type);
// see 2.1 "Column Affinity" at http://www.sqlite.org/datatype3.html
if (geometry_field_.empty() &&
(
(boost::algorithm::icontains(fld_name,"geom") ||
boost::algorithm::icontains(fld_name,"point") ||
boost::algorithm::icontains(fld_name,"linestring") ||
boost::algorithm::icontains(fld_name,"polygon"))
||
(boost::algorithm::contains(fld_type,"geom") ||
boost::algorithm::contains(fld_type,"point") ||
boost::algorithm::contains(fld_type,"linestring") ||
boost::algorithm::contains(fld_type,"polygon"))
)
)
geometry_field_ = std::string(fld_name);
else if (boost::algorithm::contains(fld_type,"int"))
if (geometry_field_.empty()
&& (boost::algorithm::contains(fld_type, "geom") ||
boost::algorithm::contains(fld_type, "point") ||
boost::algorithm::contains(fld_type, "linestring") ||
boost::algorithm::contains(fld_type, "polygon")))
{
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Integer));
geometry_field_ = std::string(fld_name);
}
else if (boost::algorithm::contains(fld_type,"text") ||
boost::algorithm::contains(fld_type,"char") ||
boost::algorithm::contains(fld_type,"clob"))
else if (boost::algorithm::contains(fld_type, "int"))
{
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String));
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Integer));
}
else if (boost::algorithm::contains(fld_type,"real") ||
boost::algorithm::contains(fld_type,"float") ||
boost::algorithm::contains(fld_type,"double"))
else if (boost::algorithm::contains(fld_type, "text") ||
boost::algorithm::contains(fld_type, "char") ||
boost::algorithm::contains(fld_type, "clob"))
{
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double));
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String));
}
else if (boost::algorithm::contains(fld_type,"blob") && !geometry_field_.empty())
else if (boost::algorithm::contains(fld_type, "real") ||
boost::algorithm::contains(fld_type, "float") ||
boost::algorithm::contains(fld_type, "double"))
{
desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::String));
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::Double));
}
else if (boost::algorithm::contains(fld_type, "blob"))
{
if (! geometry_field_.empty())
{
desc_.add_descriptor(attribute_descriptor(fld_name, mapnik::String));
}
}
#ifdef MAPNIK_DEBUG
else
{
// "Column Affinity" says default to "Numeric" but for now we pass..
//desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double));
std::clog << "Sqlite Plugin: column '" << std::string(fld_name) << "' unhandled due to unknown type: " << fld_type << std::endl;
// TODO - this should not fail when we specify geometry_field in XML file
std::clog << "Sqlite Plugin: column '"
<< std::string(fld_name)
<< "' unhandled due to unknown type: "
<< fld_type << std::endl;
}
#endif
}
if (!found_table)
if (! found_table)
{
std::ostringstream s;
s << "Sqlite Plugin: could not query table '" << geometry_table_ << "' ";
if (using_subquery_)
s << " from subquery '" << table_ << "' ";
s << "using 'PRAGMA table_info(" << geometry_table_ << ")' ";
std::string sq_err = std::string(sqlite3_errmsg(*(*dataset_)));
if (sq_err != "unknown error")
s << ": " << sq_err;
throw datasource_exception(s.str());
}
}
if (geometry_field_.empty()) {
if (geometry_field_.empty())
{
throw datasource_exception("Sqlite Plugin: cannot detect geometry_field, please supply the name of the geometry_field to use.");
}
if (index_table_.size() == 0) {
if (index_table_.size() == 0)
{
// Generate implicit index_table name - need to do this after
// we have discovered meta-data or else we don't know the column name
index_table_ = "\"idx_" + mapnik::unquote_sql2(geometry_table_) + "_" + geometry_field_ + "\"";
@ -420,7 +444,7 @@ void sqlite_datasource::bind() const
}
}
if (!extent_initialized_ && has_spatial_index_)
if (! extent_initialized_ && has_spatial_index_)
{
std::ostringstream s;
s << "SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM "
@ -428,7 +452,8 @@ void sqlite_datasource::bind() const
boost::scoped_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str()));
if (rs->is_valid() && rs->step_next())
{
if (!rs->column_isnull(0)) {
if (!rs->column_isnull(0))
{
try
{
double xmin = lexical_cast<double>(rs->column_double(0));
@ -448,27 +473,37 @@ void sqlite_datasource::bind() const
}
#ifdef MAPNIK_DEBUG
if (!has_spatial_index_
if (! has_spatial_index_
&& use_spatial_index_
&& using_subquery_
&& key_field_ == "rowid"
&& !boost::algorithm::icontains(table_,"rowid")) {
&& ! boost::algorithm::icontains(table_,"rowid"))
{
// this is an impossible situation because rowid will be null via a subquery
std::clog << "Sqlite Plugin: WARNING: spatial index usage will fail because rowid is not present in your subquery. You have 4 options: 1) Add rowid into your select statement, 2) alias your primary key as rowid, 3) supply a 'key_field' value that references the primary key of your spatial table, or 4) avoid using a spatial index by setting 'use_spatial_index'=false";
std::clog << "Sqlite Plugin: WARNING: spatial index usage will fail because rowid "
<< "is not present in your subquery. You have 4 options: "
<< "1) Add rowid into your select statement, "
<< "2) alias your primary key as rowid, "
<< "3) supply a 'key_field' value that references the primary key of your spatial table, or "
<< "4) avoid using a spatial index by setting 'use_spatial_index'=false" << std::endl;
}
#endif
// final fallback to gather extent
if (!extent_initialized_ || !has_spatial_index_) {
if (! extent_initialized_ || ! has_spatial_index_)
{
std::ostringstream s;
s << "SELECT " << geometry_field_ << "," << key_field_
<< " FROM " << geometry_table_;
if (row_limit_ > 0) {
if (row_limit_ > 0)
{
s << " LIMIT " << row_limit_;
}
if (row_offset_ > 0) {
if (row_offset_ > 0)
{
s << " OFFSET " << row_offset_;
}
@ -485,15 +520,17 @@ void sqlite_datasource::bind() const
+ " values (?,?,?,?,?)" ;
sqlite3_stmt* stmt = 0;
if (use_spatial_index_) {
if (use_spatial_index_)
{
dataset_->execute(spatial_index_sql);
int rc = sqlite3_prepare_v2 (*(*dataset_), spatial_index_insert_sql.c_str(), -1, &stmt, 0);
if (rc != SQLITE_OK)
{
std::ostringstream index_error;
index_error << "Sqlite Plugin: auto-index table creation failed: '"
<< sqlite3_errmsg(*(*dataset_)) << "' query was: " << spatial_index_insert_sql;
throw datasource_exception(index_error.str());
std::ostringstream index_error;
index_error << "Sqlite Plugin: auto-index table creation failed: '"
<< sqlite3_errmsg(*(*dataset_)) << "' query was: "
<< spatial_index_insert_sql;
throw datasource_exception(index_error.str());
}
}
@ -502,26 +539,33 @@ void sqlite_datasource::bind() const
{
int size;
const char* data = (const char *) rs->column_blob (0, size);
if (data) {
if (data)
{
// create a tmp feature to be able to parse geometry
// ideally we would not have to do this.
// see: http://trac.mapnik.org/ticket/745
mapnik::feature_ptr feature(mapnik::feature_factory::create(0));
mapnik::geometry_utils::from_wkb(feature->paths(),data,size,multiple_geometries_,format_);
mapnik::geometry_utils::from_wkb(feature->paths(), data, size, multiple_geometries_, format_);
mapnik::box2d<double> const& bbox = feature->envelope();
if (bbox.valid()) {
if (bbox.valid())
{
extent_initialized_ = true;
if (first) {
if (first)
{
first = false;
extent_ = bbox;
} else {
}
else
{
extent_.expand_to_include(bbox);
}
// index creation
if (use_spatial_index_) {
if (use_spatial_index_)
{
const int type_oid = rs->column_type(1);
if (type_oid != SQLITE_INTEGER) {
if (type_oid != SQLITE_INTEGER)
{
std::ostringstream type_error;
type_error << "Sqlite Plugin: invalid type for key field '"
<< key_field_ << "' when creating index '" << index_table_
@ -534,28 +578,36 @@ void sqlite_datasource::bind() const
{
throw datasource_exception("invalid value for for key field while generating index");
}
if ((sqlite3_bind_double(stmt, 2 , bbox.minx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 3 , bbox.maxx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 4 , bbox.miny() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 5 , bbox.maxy() ) != SQLITE_OK)) {
throw datasource_exception("invalid value for for extent while generating index");
}
int res = sqlite3_step(stmt);
if (res != SQLITE_DONE) {
if ((sqlite3_bind_double(stmt, 2 , bbox.minx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 3 , bbox.maxx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 4 , bbox.miny() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 5 , bbox.maxy() ) != SQLITE_OK))
{
throw datasource_exception("invalid value for for extent while generating index");
}
const int res = sqlite3_step(stmt);
if (res != SQLITE_DONE)
{
std::ostringstream s;
s << "SQLite Plugin: inserting bbox into rtree index failed: "
<< "error code " << sqlite3_errcode(*(*dataset_)) << ": '"
<< sqlite3_errmsg(*(*dataset_)) << "' query was: " << spatial_index_insert_sql;
<< sqlite3_errmsg(*(*dataset_)) << "' query was: "
<< spatial_index_insert_sql;
throw datasource_exception(s.str());
}
sqlite3_reset(stmt);
}
}
else {
else
{
std::ostringstream index_error;
index_error << "SQLite Plugin: encountered invalid bbox at '"
<< key_field_ << "' == " << rs->column_integer64(1);
<< key_field_ << "' == " << rs->column_integer64(1);
throw datasource_exception(index_error.str());
}
}
@ -566,10 +618,10 @@ void sqlite_datasource::bind() const
{
throw datasource_exception("auto-indexing failed: set use_spatial_index=false to disable auto-indexing and avoid this error");
}
}
if (!extent_initialized_) {
if (!extent_initialized_)
{
std::ostringstream s;
s << "Sqlite Plugin: extent could not be determined for table '"
<< geometry_table_ << "' and geometry field '" << geometry_field_ << "'"
@ -591,29 +643,32 @@ sqlite_datasource::~sqlite_datasource()
std::string sqlite_datasource::name()
{
return "sqlite";
return "sqlite";
}
int sqlite_datasource::type() const
{
return type_;
return type_;
}
box2d<double> sqlite_datasource::envelope() const
{
if (!is_bound_) bind();
return extent_;
if (!is_bound_) bind();
return extent_;
}
layer_descriptor sqlite_datasource::get_descriptor() const
{
if (!is_bound_) bind();
return desc_;
if (!is_bound_) bind();
return desc_;
}
featureset_ptr sqlite_datasource::features(query const& q) const
{
if (!is_bound_) bind();
if (dataset_)
{
mapnik::box2d<double> const& e = q.get_bbox();
@ -634,39 +689,39 @@ featureset_ptr sqlite_datasource::features(query const& q) const
std::string query (table_);
/* todo
throw if select * and key_field == rowid?
or add schema support so sqlite throws
*/
// TODO
// throw if select * and key_field == rowid?
// or add schema support so sqlite throws
if (has_spatial_index_)
{
/*
Use rtree to limit record id's to a given bbox
then a btree to pull the records for those ids.
*/
std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16);
spatial_sql << " WHERE " << key_field_ << " IN (SELECT pkid FROM " << index_table_;
spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ;
spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")";
if (boost::algorithm::ifind_first(query, "WHERE"))
{
boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND ");
}
else if (boost::algorithm::ifind_first(query, geometry_table_))
{
boost::algorithm::ireplace_first(query, table_, table_ + " " + spatial_sql.str());
}
// Use rtree to limit record id's to a given bbox
// then a btree to pull the records for those ids.
std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16);
spatial_sql << " WHERE " << key_field_ << " IN (SELECT pkid FROM " << index_table_;
spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ;
spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")";
if (boost::algorithm::ifind_first(query, "WHERE"))
{
boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND ");
}
else if (boost::algorithm::ifind_first(query, geometry_table_))
{
boost::algorithm::ireplace_first(query, table_, table_ + " " + spatial_sql.str());
}
}
s << query ;
if (row_limit_ > 0) {
if (row_limit_ > 0)
{
s << " LIMIT " << row_limit_;
}
if (row_offset_ > 0) {
if (row_offset_ > 0)
{
s << " OFFSET " << row_offset_;
}
@ -677,7 +732,11 @@ featureset_ptr sqlite_datasource::features(query const& q) const
boost::shared_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str()));
return boost::make_shared<sqlite_featureset>(rs, desc_.get_encoding(), format_, multiple_geometries_, using_subquery_);
return boost::make_shared<sqlite_featureset>(rs,
desc_.get_encoding(),
format_,
multiple_geometries_,
using_subquery_);
}
return featureset_ptr();
@ -700,7 +759,10 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const
{
std::string fld_name = itr->get_name();
if (fld_name != key_field_)
{
s << ",\"" << itr->get_name() << "\"";
}
++itr;
}
@ -710,28 +772,30 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const
if (has_spatial_index_)
{
std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16);
spatial_sql << " WHERE " << key_field_ << " IN (SELECT pkid FROM " << index_table_;
spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ;
spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")";
if (boost::algorithm::ifind_first(query, "WHERE"))
{
boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND ");
}
else if (boost::algorithm::ifind_first(query, geometry_table_))
{
boost::algorithm::ireplace_first(query, table_, table_ + " " + spatial_sql.str());
}
std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16);
spatial_sql << " WHERE " << key_field_ << " IN (SELECT pkid FROM " << index_table_;
spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ;
spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")";
if (boost::algorithm::ifind_first(query, "WHERE"))
{
boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND ");
}
else if (boost::algorithm::ifind_first(query, geometry_table_))
{
boost::algorithm::ireplace_first(query, table_, table_ + " " + spatial_sql.str());
}
}
s << query ;
if (row_limit_ > 0) {
if (row_limit_ > 0)
{
s << " LIMIT " << row_limit_;
}
if (row_offset_ > 0) {
if (row_offset_ > 0)
{
s << " OFFSET " << row_offset_;
}
@ -741,9 +805,12 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const
boost::shared_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str()));
return boost::make_shared<sqlite_featureset>(rs, desc_.get_encoding(), format_, multiple_geometries_, using_subquery_);
return boost::make_shared<sqlite_featureset>(rs,
desc_.get_encoding(),
format_,
multiple_geometries_,
using_subquery_);
}
return featureset_ptr();
}

View file

@ -37,6 +37,9 @@
// sqlite
#include "sqlite_types.hpp"
//==============================================================================
class sqlite_datasource : public mapnik::datasource
{
public:

View file

@ -31,7 +31,6 @@
#include <mapnik/wkb.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/sql_utils.hpp>
#include <string.h>
// ogr
@ -61,137 +60,152 @@ sqlite_featureset::sqlite_featureset(boost::shared_ptr<sqlite_resultset> rs,
sqlite_featureset::~sqlite_featureset() {}
void sqlite_dequote(char *z){
char quote; /* Quote character (if any ) */
// TODO - refactor, make a static member using std::string or better UnicodeString
quote = z[0];
if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
int iIn = 1; /* Index of next byte to read from input */
int iOut = 0; /* Index of next byte to write to output */
void sqlite_dequote(char *z)
{
char quote = z[0];
/* If the first byte was a '[', then the close-quote character is a ']' */
if( quote=='[' ) quote = ']';
if (quote=='[' || quote=='\'' || quote=='"' || quote=='`')
{
int iIn = 1; // Index of next byte to read from input
int iOut = 0; // Index of next byte to write to output
while( z[iIn] ){
if( z[iIn]==quote ){
if( z[iIn+1]!=quote ) break;
z[iOut++] = quote;
iIn += 2;
}else{
z[iOut++] = z[iIn++];
}
// If the first byte was a '[', then the close-quote character is a ']'
if (quote == '[')
{
quote = ']';
}
while (z[iIn])
{
if (z[iIn] == quote)
{
if (z[iIn+1] != quote) break;
z[iOut++] = quote;
iIn += 2;
}
else
{
z[iOut++] = z[iIn++];
}
}
z[iOut] = '\0';
}
z[iOut] = '\0';
}
}
feature_ptr sqlite_featureset::next()
{
if (rs_->is_valid () && rs_->step_next ())
{
int size;
const char* data = (const char *) rs_->column_blob (0, size);
if (!data)
if (! data)
{
return feature_ptr();
}
int feature_id = rs_->column_integer (1);
feature_ptr feature(feature_factory::create(feature_id));
geometry_utils::from_wkb(feature->paths(),data,size,multiple_geometries_,format_);
geometry_utils::from_wkb(feature->paths(), data, size, multiple_geometries_, format_);
for (int i = 2; i < rs_->column_count (); ++i)
{
const int type_oid = rs_->column_type (i);
const char* fld_name = rs_->column_name(i);
if (!fld_name)
if (! fld_name)
continue;
if (!using_subquery_)
if (! using_subquery_)
{
switch (type_oid)
{
case SQLITE_INTEGER:
{
boost::put(*feature,fld_name,rs_->column_integer (i));
break;
}
case SQLITE_INTEGER:
{
boost::put(*feature, fld_name, rs_->column_integer (i));
break;
}
case SQLITE_FLOAT:
{
boost::put(*feature,fld_name,rs_->column_double (i));
break;
}
case SQLITE_FLOAT:
{
boost::put(*feature, fld_name, rs_->column_double (i));
break;
}
case SQLITE_TEXT:
{
int text_size;
const char * data = rs_->column_text(i,text_size);
UnicodeString ustr = tr_->transcode(data,text_size);
boost::put(*feature,fld_name,ustr);
break;
}
case SQLITE_TEXT:
{
int text_size;
const char * data = rs_->column_text(i, text_size);
UnicodeString ustr = tr_->transcode(data, text_size);
boost::put(*feature, fld_name, ustr);
break;
}
case SQLITE_NULL:
{
boost::put(*feature,fld_name,mapnik::value_null());
break;
}
case SQLITE_NULL:
{
boost::put(*feature,fld_name,mapnik::value_null());
break;
}
case SQLITE_BLOB:
break;
case SQLITE_BLOB:
break;
default:
default:
#ifdef MAPNIK_DEBUG
std::clog << "Sqlite Plugin: unhandled type_oid=" << type_oid << std::endl;
std::clog << "Sqlite Plugin: unhandled type_oid=" << type_oid << std::endl;
#endif
break;
break;
}
}
else
{
// TODO - refactor this code, it is C99 but not valid in C++ (even if GCC allows this)
// subqueries in sqlite lead to field double quoting which we need to strip
char fld_name2[strlen(fld_name)];
strcpy(fld_name2,fld_name);
sqlite_dequote(fld_name2);
switch (type_oid)
{
case SQLITE_INTEGER:
{
boost::put(*feature,fld_name2,rs_->column_integer (i));
break;
}
case SQLITE_INTEGER:
{
boost::put(*feature,fld_name2,rs_->column_integer (i));
break;
}
case SQLITE_FLOAT:
{
boost::put(*feature,fld_name2,rs_->column_double (i));
break;
}
case SQLITE_FLOAT:
{
boost::put(*feature,fld_name2,rs_->column_double (i));
break;
}
case SQLITE_TEXT:
{
int text_size;
const char * data = rs_->column_text(i,text_size);
UnicodeString ustr = tr_->transcode(data,text_size);
boost::put(*feature,fld_name2,ustr);
break;
}
case SQLITE_TEXT:
{
int text_size;
const char * data = rs_->column_text(i,text_size);
UnicodeString ustr = tr_->transcode(data,text_size);
boost::put(*feature,fld_name2,ustr);
break;
}
case SQLITE_NULL:
{
boost::put(*feature,fld_name2,mapnik::value_null());
break;
}
case SQLITE_NULL:
{
boost::put(*feature,fld_name2,mapnik::value_null());
break;
}
case SQLITE_BLOB:
break;
case SQLITE_BLOB:
break;
default:
default:
#ifdef MAPNIK_DEBUG
std::clog << "Sqlite Plugin: unhandled type_oid=" << type_oid << std::endl;
std::clog << "Sqlite Plugin: unhandled type_oid=" << type_oid << std::endl;
#endif
break;
break;
}
}
}
@ -201,4 +215,3 @@ feature_ptr sqlite_featureset::next()
return feature_ptr();
}

View file

@ -37,6 +37,8 @@
#include "sqlite_types.hpp"
//==============================================================================
class sqlite_featureset : public mapnik::Featureset
{
public:
@ -55,4 +57,5 @@ class sqlite_featureset : public mapnik::Featureset
bool using_subquery_;
};
#endif // SQLITE_FEATURESET_HPP

View file

@ -36,6 +36,8 @@ extern "C" {
}
//==============================================================================
class sqlite_resultset
{
public:
@ -48,7 +50,9 @@ public:
~sqlite_resultset ()
{
if (stmt_)
{
sqlite3_finalize (stmt_);
}
}
bool is_valid ()
@ -58,12 +62,18 @@ public:
bool step_next ()
{
int status = sqlite3_step (stmt_);
if (status != SQLITE_ROW && status != SQLITE_DONE) {
const int status = sqlite3_step (stmt_);
if (status != SQLITE_ROW && status != SQLITE_DONE)
{
std::ostringstream s;
s << "SQLite Plugin: retrieving next row failed";
std::string msg(sqlite3_errmsg(sqlite3_db_handle(stmt_)));
if (msg != "unknown error") s << ": " << msg;
if (msg != "unknown error")
{
s << ": " << msg;
}
throw mapnik::datasource_exception(s.str());
}
return status == SQLITE_ROW;
@ -134,6 +144,7 @@ private:
};
//==============================================================================
class sqlite_connection
{
@ -144,54 +155,56 @@ public:
{
// sqlite3_open_v2 is available earlier but
// shared cache not available until >= 3.6.18
#if SQLITE_VERSION_NUMBER >= 3006018
int rc = sqlite3_enable_shared_cache(1);
#if SQLITE_VERSION_NUMBER >= 3006018
const int rc = sqlite3_enable_shared_cache(1);
if (rc != SQLITE_OK)
{
throw mapnik::datasource_exception (sqlite3_errmsg (db_));
throw mapnik::datasource_exception (sqlite3_errmsg (db_));
}
int mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_SHAREDCACHE;
if (sqlite3_open_v2 (file.c_str(), &db_, mode, NULL))
#else
#else
#warning "Mapnik's sqlite plugin is compiling against a version of sqlite older than 3.6.18 which may make rendering slow..."
if (sqlite3_open (file.c_str(), &db_))
#endif
#endif
{
std::ostringstream s;
s << "Sqlite Plugin: ";
throw mapnik::datasource_exception (sqlite3_errmsg (db_));
s << "Sqlite Plugin: " << sqlite3_errmsg (db_);
throw mapnik::datasource_exception (s.str());
}
//sqlite3_enable_load_extension(db_, 1);
}
~sqlite_connection ()
virtual ~sqlite_connection ()
{
if (db_)
{
sqlite3_close (db_);
}
}
void throw_sqlite_error(const std::string& sql)
{
std::ostringstream s;
s << "Sqlite Plugin: ";
if (db_)
s << "'" << sqlite3_errmsg(db_) << "'";
else
s << "unknown error, lost connection";
s << "\nFull sql was: '" << sql << "'\n";
throw mapnik::datasource_exception( s.str() );
std::ostringstream s;
s << "Sqlite Plugin: ";
if (db_)
s << "'" << sqlite3_errmsg(db_) << "'";
else
s << "unknown error, lost connection";
s << "\nFull sql was: '" << sql << "'\n";
throw mapnik::datasource_exception (s.str());
}
sqlite_resultset* execute_query(const std::string& sql)
{
sqlite3_stmt* stmt = 0;
int rc = sqlite3_prepare_v2 (db_, sql.c_str(), -1, &stmt, 0);
const int rc = sqlite3_prepare_v2 (db_, sql.c_str(), -1, &stmt, 0);
if (rc != SQLITE_OK)
{
throw_sqlite_error(sql);
throw_sqlite_error(sql);
}
return new sqlite_resultset (stmt);
@ -199,7 +212,7 @@ public:
void execute(const std::string& sql)
{
int rc=sqlite3_exec(db_, sql.c_str(), 0, 0, 0);
const int rc = sqlite3_exec(db_, sql.c_str(), 0, 0, 0);
if (rc != SQLITE_OK)
{
throw_sqlite_error(sql);
@ -208,7 +221,7 @@ public:
int execute_with_code(const std::string& sql)
{
int rc=sqlite3_exec(db_, sql.c_str(), 0, 0, 0);
const int rc = sqlite3_exec(db_, sql.c_str(), 0, 0, 0);
return rc;
}
@ -222,5 +235,5 @@ private:
sqlite3* db_;
};
#endif //SQLITE_TYPES_HPP
#endif //SQLITE_TYPES_HPP

View file

@ -39,6 +39,7 @@ private:
wkbXDR=0,
wkbNDR=1
};
const char* wkb_;
unsigned size_;
unsigned pos_;
@ -64,6 +65,23 @@ public:
pos_(0),
format_(format)
{
// try to determine WKB format automatically
if (format_ == wkbAuto)
{
if (size > 38
&& wkb_[0] == 0x00
&& (wkb_[1] == 0x00 || wkb_[1] == 0x01)
&& wkb_[38] == 0x7C
&& wkb_[size - 1] == 0xFE)
{
format_ = wkbSpatiaLite;
}
else
{
format_ = wkbGeneric;
}
}
switch (format_)
{
case wkbSpatiaLite: