2009-02-09 20:43:57 +01:00
|
|
|
/*****************************************************************************
|
2011-11-14 04:37:50 +01:00
|
|
|
*
|
2009-02-09 20:43:57 +01:00
|
|
|
* This file is part of Mapnik (c++ mapping toolkit)
|
|
|
|
*
|
2011-10-22 14:50:24 +02:00
|
|
|
* Copyright (C) 2011 Artem Pavlenko
|
2009-02-09 20:43:57 +01:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
#ifndef MAPNIK_SQLITE_UTILS_HPP
|
|
|
|
#define MAPNIK_SQLITE_UTILS_HPP
|
2009-02-09 20:43:57 +01:00
|
|
|
|
2011-10-22 14:50:24 +02:00
|
|
|
// stl
|
2011-11-19 20:36:35 +01:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2011-10-18 23:32:25 +02:00
|
|
|
|
2009-02-09 20:43:57 +01:00
|
|
|
// mapnik
|
|
|
|
#include <mapnik/datasource.hpp>
|
2011-11-04 00:51:37 +01:00
|
|
|
#include <mapnik/geometry.hpp>
|
2011-10-29 06:24:47 +02:00
|
|
|
#include <mapnik/sql_utils.hpp>
|
|
|
|
|
2009-02-09 20:43:57 +01:00
|
|
|
|
|
|
|
// boost
|
|
|
|
#include <boost/shared_ptr.hpp>
|
2011-10-29 06:24:47 +02:00
|
|
|
#include <boost/make_shared.hpp>
|
2011-10-21 12:41:20 +02:00
|
|
|
#include <boost/algorithm/string.hpp>
|
2011-10-29 06:24:47 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2011-11-10 02:44:50 +01:00
|
|
|
#include <boost/filesystem/operations.hpp>
|
2009-02-09 20:43:57 +01:00
|
|
|
|
|
|
|
// sqlite
|
|
|
|
extern "C" {
|
2011-11-14 04:37:50 +01:00
|
|
|
#include <sqlite3.h>
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
#include "sqlite_resultset.hpp"
|
|
|
|
#include "sqlite_prepared.hpp"
|
|
|
|
#include "sqlite_connection.hpp"
|
2009-02-09 20:43:57 +01:00
|
|
|
|
2011-10-18 23:32:25 +02:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
//==============================================================================
|
2011-10-18 23:32:25 +02:00
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
class sqlite_utils
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
|
|
|
public:
|
2011-10-29 06:24:47 +02:00
|
|
|
|
2011-11-10 18:42:04 +01:00
|
|
|
static bool needs_quoting(std::string const& name)
|
|
|
|
{
|
|
|
|
if (name.size() > 0)
|
|
|
|
{
|
|
|
|
const char first = name[0];
|
|
|
|
if (is_quote_char(first))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((first >= '0' && first <= '9') ||
|
|
|
|
(name.find("-") != std::string::npos)
|
2011-11-14 04:37:50 +01:00
|
|
|
)
|
2011-11-10 18:42:04 +01:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_quote_char(const char z)
|
|
|
|
{
|
|
|
|
if (z == '"' || z == '\'' || z == '[' || z == '`')
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static void dequote(std::string & z)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::algorithm::trim_if(z,boost::algorithm::is_any_of("[]'\"`"));
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static std::string index_for_table(std::string const& table, std::string const& field)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-14 01:41:54 +01:00
|
|
|
std::string table_trimmed = table;
|
|
|
|
dequote(table_trimmed);
|
|
|
|
return "\"idx_" + table_trimmed + "_" + field + "\"";
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static std::string index_for_db(std::string const& file)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
//std::size_t idx = file.find_last_of(".");
|
|
|
|
//if(idx != std::string::npos) {
|
|
|
|
// return file.substr(0,idx) + ".index" + file.substr(idx);
|
|
|
|
//}
|
|
|
|
//else
|
|
|
|
//{
|
2011-11-14 04:37:50 +01:00
|
|
|
return file + ".index";
|
2011-11-04 00:51:37 +01:00
|
|
|
//}
|
2011-11-10 04:18:12 +01:00
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-10 04:18:12 +01:00
|
|
|
static void get_tables(boost::shared_ptr<sqlite_connection> ds,
|
|
|
|
std::vector<std::string> & tables)
|
|
|
|
{
|
|
|
|
std::ostringstream sql;
|
|
|
|
// todo handle finding tables from attached db's
|
|
|
|
sql << " SELECT name FROM sqlite_master"
|
|
|
|
<< " WHERE type IN ('table','view')"
|
|
|
|
<< " AND name NOT LIKE 'sqlite_%'"
|
|
|
|
<< " AND name NOT LIKE 'idx_%'"
|
|
|
|
<< " AND name NOT LIKE '%geometry_columns%'"
|
|
|
|
<< " AND name NOT LIKE '%ref_sys%'"
|
|
|
|
<< " UNION ALL"
|
|
|
|
<< " SELECT name FROM sqlite_temp_master"
|
|
|
|
<< " WHERE type IN ('table','view')"
|
|
|
|
<< " ORDER BY 1";
|
|
|
|
sqlite3_stmt* stmt = 0;
|
|
|
|
const int rc = sqlite3_prepare_v2 (*(*ds), sql.str().c_str(), -1, &stmt, 0);
|
|
|
|
if (rc == SQLITE_OK)
|
|
|
|
{
|
|
|
|
boost::shared_ptr<sqlite_resultset> rs = boost::make_shared<sqlite_resultset>(stmt);
|
|
|
|
while (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
const int type_oid = rs->column_type(0);
|
|
|
|
if (type_oid == SQLITE_TEXT)
|
|
|
|
{
|
|
|
|
const char * data = rs->column_text(0);
|
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
tables.push_back(std::string(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static void query_extent(boost::shared_ptr<sqlite_resultset> rs,
|
|
|
|
mapnik::box2d<double>& extent)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
while (rs->is_valid() && rs->step_next())
|
2011-10-18 22:19:03 +02:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
int size;
|
2011-12-06 13:53:16 +01:00
|
|
|
const char* data = static_cast<const char*>(rs->column_blob(0, size));
|
2011-11-04 00:51:37 +01:00
|
|
|
if (data)
|
2011-10-18 22:19:03 +02:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::ptr_vector<mapnik::geometry_type> paths;
|
2011-12-06 13:53:16 +01:00
|
|
|
mapnik::geometry_utils::from_wkb(paths, data, size, mapnik::wkbAuto);
|
2011-11-04 00:51:37 +01:00
|
|
|
for (unsigned i=0; i<paths.size(); ++i)
|
|
|
|
{
|
|
|
|
mapnik::box2d<double> const& bbox = paths[i].envelope();
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
if (bbox.valid())
|
|
|
|
{
|
|
|
|
if (first)
|
|
|
|
{
|
|
|
|
first = false;
|
|
|
|
extent = bbox;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extent.expand_to_include(bbox);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-10-18 22:19:03 +02:00
|
|
|
}
|
2011-09-15 23:58:20 +02:00
|
|
|
}
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-10 04:18:12 +01:00
|
|
|
static bool create_spatial_index(std::string const& index_db,
|
2011-11-04 00:51:37 +01:00
|
|
|
std::string const& index_table,
|
2011-11-14 01:02:27 +01:00
|
|
|
boost::shared_ptr<sqlite_resultset> rs)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
/* TODO
|
2011-11-14 04:37:50 +01:00
|
|
|
- speedups
|
|
|
|
- return early/recover from failure
|
|
|
|
- allow either in db or in separate
|
2011-11-04 00:51:37 +01:00
|
|
|
*/
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
if (!rs->is_valid())
|
2011-11-10 04:18:12 +01:00
|
|
|
return false;
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
#if SQLITE_VERSION_NUMBER >= 3005000
|
2011-11-04 00:51:37 +01:00
|
|
|
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
2011-10-29 06:24:47 +02:00
|
|
|
#else
|
2011-11-04 00:51:37 +01:00
|
|
|
int flags;
|
2011-10-29 06:24:47 +02:00
|
|
|
#endif
|
|
|
|
|
2011-11-10 02:44:50 +01:00
|
|
|
bool existed = boost::filesystem::exists(index_db);
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::shared_ptr<sqlite_connection> ds = boost::make_shared<sqlite_connection>(index_db,flags);
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-10 02:44:50 +01:00
|
|
|
bool one_success = false;
|
|
|
|
try
|
2011-04-01 03:20:34 +02:00
|
|
|
{
|
2011-11-14 04:19:22 +01:00
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
ds->execute("PRAGMA synchronous=OFF");
|
|
|
|
ds->execute("BEGIN IMMEDIATE TRANSACTION");
|
2011-11-14 04:19:22 +01:00
|
|
|
|
|
|
|
/*
|
2011-11-14 04:37:50 +01:00
|
|
|
ds->execute("PRAGMA journal_mode=WAL");
|
|
|
|
ds->execute("PRAGMA fullfsync=1");
|
|
|
|
ds->execute("PRAGMA locking_mode = EXCLUSIVE");
|
|
|
|
ds->execute("BEGIN EXCLUSIVE TRANSACTION");
|
2011-11-14 04:19:22 +01:00
|
|
|
*/
|
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
// first drop the index if it already exists
|
|
|
|
std::ostringstream spatial_index_drop_sql;
|
|
|
|
spatial_index_drop_sql << "DROP TABLE IF EXISTS " << index_table;
|
|
|
|
ds->execute(spatial_index_drop_sql.str());
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
// create the spatial index
|
|
|
|
std::ostringstream create_idx;
|
2011-11-14 04:37:50 +01:00
|
|
|
create_idx << "create virtual table "
|
2011-11-14 01:02:27 +01:00
|
|
|
<< index_table
|
|
|
|
<< " using rtree(pkid, xmin, xmax, ymin, ymax)";
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
// insert for prepared statement
|
|
|
|
std::ostringstream insert_idx;
|
|
|
|
insert_idx << "insert into "
|
|
|
|
<< index_table
|
|
|
|
<< " values (?,?,?,?,?)";
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
ds->execute(create_idx.str());
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-11-14 01:02:27 +01:00
|
|
|
prepared_index_statement ps(ds,insert_idx.str());
|
|
|
|
|
2011-11-10 02:44:50 +01:00
|
|
|
while (rs->is_valid() && rs->step_next())
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
2011-11-10 02:44:50 +01:00
|
|
|
int size;
|
|
|
|
const char* data = (const char*) rs->column_blob(0, size);
|
|
|
|
if (data)
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
2011-11-10 02:44:50 +01:00
|
|
|
boost::ptr_vector<mapnik::geometry_type> paths;
|
2011-12-06 13:53:16 +01:00
|
|
|
mapnik::geometry_utils::from_wkb(paths, data, size, mapnik::wkbAuto);
|
2011-12-12 17:30:46 +01:00
|
|
|
mapnik::box2d<double> bbox;
|
2011-11-10 02:44:50 +01:00
|
|
|
for (unsigned i=0; i<paths.size(); ++i)
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
2011-12-12 17:30:46 +01:00
|
|
|
if (i==0)
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
2011-12-12 17:30:46 +01:00
|
|
|
bbox = paths[i].envelope();
|
2011-11-04 00:51:37 +01:00
|
|
|
}
|
|
|
|
else
|
2011-12-12 17:30:46 +01:00
|
|
|
{
|
|
|
|
bbox.expand_to_include(paths[i].envelope());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bbox.valid())
|
|
|
|
{
|
|
|
|
|
|
|
|
ps.bind(bbox);
|
|
|
|
|
|
|
|
const int type_oid = rs->column_type(1);
|
|
|
|
if (type_oid != SQLITE_INTEGER)
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
|
|
|
std::ostringstream error_msg;
|
2011-12-12 17:30:46 +01:00
|
|
|
error_msg << "Sqlite Plugin: invalid type for key field '"
|
|
|
|
<< rs->column_name(1) << "' when creating index '" << index_table
|
|
|
|
<< "' type was: " << type_oid << "";
|
2011-11-04 00:51:37 +01:00
|
|
|
throw mapnik::datasource_exception(error_msg.str());
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-12-12 17:30:46 +01:00
|
|
|
const sqlite_int64 pkid = rs->column_integer64(1);
|
|
|
|
ps.bind(pkid);
|
2011-11-04 00:51:37 +01:00
|
|
|
}
|
2011-12-12 17:30:46 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
std::ostringstream error_msg;
|
|
|
|
error_msg << "SQLite Plugin: encountered invalid bbox at '"
|
|
|
|
<< rs->column_name(1) << "' == " << rs->column_integer64(1);
|
|
|
|
throw mapnik::datasource_exception(error_msg.str());
|
2011-11-04 00:51:37 +01:00
|
|
|
}
|
2011-12-12 17:30:46 +01:00
|
|
|
|
|
|
|
ps.step_next();
|
|
|
|
one_success = true;
|
2011-11-04 00:51:37 +01:00
|
|
|
}
|
|
|
|
}
|
2011-04-01 03:20:34 +02:00
|
|
|
}
|
2011-11-10 02:44:50 +01:00
|
|
|
catch (mapnik::datasource_exception const& ex)
|
|
|
|
{
|
|
|
|
if (!existed)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
boost::filesystem::remove(index_db);
|
|
|
|
}
|
|
|
|
catch (...) {};
|
|
|
|
}
|
|
|
|
throw mapnik::datasource_exception(ex.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (one_success)
|
|
|
|
{
|
|
|
|
ds->execute("COMMIT");
|
2011-11-10 04:18:12 +01:00
|
|
|
return true;
|
2011-11-10 02:44:50 +01:00
|
|
|
}
|
|
|
|
else if (!existed)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
boost::filesystem::remove(index_db);
|
|
|
|
}
|
|
|
|
catch (...) {};
|
|
|
|
}
|
2011-11-10 04:18:12 +01:00
|
|
|
return false;
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
|
|
|
|
2011-11-19 20:36:35 +01:00
|
|
|
typedef struct {
|
|
|
|
sqlite_int64 pkid;
|
|
|
|
mapnik::box2d<double> bbox;
|
|
|
|
} rtree_type;
|
|
|
|
|
|
|
|
static void build_tree(boost::shared_ptr<sqlite_resultset> rs,
|
|
|
|
std::vector<sqlite_utils::rtree_type> & rtree_list)
|
|
|
|
{
|
|
|
|
|
|
|
|
while (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
int size;
|
2011-12-06 13:53:16 +01:00
|
|
|
const char* data = static_cast<const char*>(rs->column_blob(0, size));
|
2011-11-19 20:36:35 +01:00
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
boost::ptr_vector<mapnik::geometry_type> paths;
|
2011-12-06 13:53:16 +01:00
|
|
|
mapnik::geometry_utils::from_wkb(paths, data, size, mapnik::wkbAuto);
|
2011-11-19 20:36:35 +01:00
|
|
|
for (unsigned i=0; i<paths.size(); ++i)
|
|
|
|
{
|
|
|
|
mapnik::box2d<double> const& bbox = paths[i].envelope();
|
|
|
|
if (bbox.valid())
|
|
|
|
{
|
|
|
|
|
|
|
|
const int type_oid = rs->column_type(1);
|
|
|
|
if (type_oid != SQLITE_INTEGER)
|
|
|
|
{
|
|
|
|
std::ostringstream error_msg;
|
|
|
|
error_msg << "Sqlite Plugin: invalid type for key field '"
|
|
|
|
<< rs->column_name(1) << "' when creating index "
|
|
|
|
<< "type was: " << type_oid << "";
|
|
|
|
throw mapnik::datasource_exception(error_msg.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
const sqlite_int64 pkid = rs->column_integer64(1);
|
|
|
|
|
|
|
|
rtree_type entry = rtree_type();
|
|
|
|
entry.pkid = pkid;
|
|
|
|
entry.bbox = bbox;
|
|
|
|
rtree_list.push_back(entry);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::ostringstream error_msg;
|
|
|
|
error_msg << "SQLite Plugin: encountered invalid bbox at '"
|
|
|
|
<< rs->column_name(1) << "' == " << rs->column_integer64(1);
|
|
|
|
throw mapnik::datasource_exception(error_msg.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool create_spatial_index2(std::string const& index_db,
|
|
|
|
std::string const& index_table,
|
|
|
|
std::vector<rtree_type> const& rtree_list)
|
|
|
|
{
|
|
|
|
|
|
|
|
#if SQLITE_VERSION_NUMBER >= 3005000
|
|
|
|
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
|
|
|
#else
|
|
|
|
int flags;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool existed = boost::filesystem::exists(index_db);
|
|
|
|
boost::shared_ptr<sqlite_connection> ds = boost::make_shared<sqlite_connection>(index_db,flags);
|
|
|
|
|
|
|
|
bool one_success = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
|
|
|
|
ds->execute("PRAGMA synchronous=OFF");
|
|
|
|
ds->execute("BEGIN IMMEDIATE TRANSACTION");
|
|
|
|
|
|
|
|
// first drop the index if it already exists
|
|
|
|
std::ostringstream spatial_index_drop_sql;
|
|
|
|
spatial_index_drop_sql << "DROP TABLE IF EXISTS " << index_table;
|
|
|
|
ds->execute(spatial_index_drop_sql.str());
|
|
|
|
|
|
|
|
// create the spatial index
|
|
|
|
std::ostringstream create_idx;
|
|
|
|
create_idx << "create virtual table "
|
|
|
|
<< index_table
|
|
|
|
<< " using rtree(pkid, xmin, xmax, ymin, ymax)";
|
|
|
|
|
|
|
|
// insert for prepared statement
|
|
|
|
std::ostringstream insert_idx;
|
|
|
|
insert_idx << "insert into "
|
|
|
|
<< index_table
|
|
|
|
<< " values (?,?,?,?,?)";
|
|
|
|
|
|
|
|
ds->execute(create_idx.str());
|
|
|
|
|
|
|
|
prepared_index_statement ps(ds,insert_idx.str());
|
|
|
|
|
|
|
|
std::vector<rtree_type>::const_iterator it = rtree_list.begin();
|
|
|
|
std::vector<rtree_type>::const_iterator end = rtree_list.end();
|
|
|
|
for (; it != end; ++it)
|
|
|
|
{
|
|
|
|
ps.bind(it->bbox);
|
|
|
|
ps.bind(it->pkid);
|
|
|
|
ps.step_next();
|
|
|
|
one_success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (mapnik::datasource_exception const& ex)
|
|
|
|
{
|
|
|
|
if (!existed)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
boost::filesystem::remove(index_db);
|
|
|
|
}
|
|
|
|
catch (...) {};
|
|
|
|
}
|
|
|
|
throw mapnik::datasource_exception(ex.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (one_success)
|
|
|
|
{
|
|
|
|
ds->execute("COMMIT");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (!existed)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
boost::filesystem::remove(index_db);
|
|
|
|
}
|
|
|
|
catch (...) {};
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static bool detect_extent(boost::shared_ptr<sqlite_connection> ds,
|
|
|
|
bool has_spatial_index,
|
|
|
|
mapnik::box2d<double> & extent,
|
|
|
|
std::string const& index_table,
|
|
|
|
std::string const& metadata,
|
|
|
|
std::string const& geometry_field,
|
|
|
|
std::string const& geometry_table,
|
|
|
|
std::string const& key_field,
|
|
|
|
std::string const& table
|
2011-11-14 04:37:50 +01:00
|
|
|
)
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
if (has_spatial_index)
|
2011-10-18 22:19:03 +02:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
std::ostringstream s;
|
2011-11-14 04:37:50 +01:00
|
|
|
s << "SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM "
|
|
|
|
<< index_table;
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
|
|
|
|
if (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
if (! rs->column_isnull(0))
|
|
|
|
{
|
2011-11-14 04:37:50 +01:00
|
|
|
try
|
2011-11-04 00:51:37 +01:00
|
|
|
{
|
|
|
|
double xmin = boost::lexical_cast<double>(rs->column_double(0));
|
|
|
|
double ymin = boost::lexical_cast<double>(rs->column_double(1));
|
|
|
|
double xmax = boost::lexical_cast<double>(rs->column_double(2));
|
|
|
|
double ymax = boost::lexical_cast<double>(rs->column_double(3));
|
|
|
|
extent.init(xmin, ymin, xmax, ymax);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (boost::bad_lexical_cast& ex)
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << "SQLite Plugin: warning: could not determine extent from query: "
|
|
|
|
<< "'" << s.str() << "' \n problem was: " << ex.what() << std::endl;
|
|
|
|
std::clog << ss.str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (! metadata.empty())
|
2009-02-09 20:43:57 +01:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
std::ostringstream s;
|
|
|
|
s << "SELECT xmin, ymin, xmax, ymax FROM " << metadata;
|
|
|
|
s << " WHERE LOWER(f_table_name) = LOWER('" << geometry_table << "')";
|
|
|
|
boost::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
|
|
|
|
if (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
double xmin = rs->column_double (0);
|
|
|
|
double ymin = rs->column_double (1);
|
|
|
|
double xmax = rs->column_double (2);
|
|
|
|
double ymax = rs->column_double (3);
|
|
|
|
extent.init (xmin, ymin, xmax, ymax);
|
|
|
|
return true;
|
|
|
|
}
|
2009-02-09 20:43:57 +01:00
|
|
|
}
|
2011-11-04 00:51:37 +01:00
|
|
|
else if (! key_field.empty())
|
2011-07-11 19:46:53 +02:00
|
|
|
{
|
2011-11-04 00:51:37 +01:00
|
|
|
std::ostringstream s;
|
|
|
|
s << "SELECT " << geometry_field << "," << key_field
|
|
|
|
<< " FROM (" << table << ")";
|
|
|
|
boost::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
|
|
|
|
sqlite_utils::query_extent(rs,extent);
|
|
|
|
return true;
|
2011-07-11 19:46:53 +02:00
|
|
|
}
|
2011-11-04 00:51:37 +01:00
|
|
|
return false;
|
2011-07-11 19:46:53 +02:00
|
|
|
}
|
2009-02-09 20:43:57 +01:00
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static bool has_rtree(std::string const& index_table,boost::shared_ptr<sqlite_connection> ds)
|
2011-10-29 06:24:47 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::ostringstream s;
|
|
|
|
s << "SELECT pkid,xmin,xmax,ymin,ymax FROM " << index_table << " LIMIT 1";
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::shared_ptr<sqlite_resultset> rs = ds->execute_query(s.str());
|
2011-10-29 06:24:47 +02:00
|
|
|
if (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2011-11-14 01:02:27 +01:00
|
|
|
catch (std::exception const& ex)
|
2011-10-29 06:24:47 +02:00
|
|
|
{
|
2011-11-14 01:02:27 +01:00
|
|
|
//std::clog << "no: " << ex.what() << "\n";
|
2011-10-29 06:24:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static bool detect_types_from_subquery(std::string const& query,
|
2011-11-14 04:37:50 +01:00
|
|
|
std::string & geometry_field,
|
|
|
|
mapnik::layer_descriptor & desc,
|
|
|
|
boost::shared_ptr<sqlite_connection> ds)
|
2011-10-29 06:24:47 +02:00
|
|
|
{
|
|
|
|
bool found = false;
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::shared_ptr<sqlite_resultset> rs(ds->execute_query(query));
|
2011-10-29 06:24:47 +02:00
|
|
|
if (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
for (int i = 0; i < rs->column_count(); ++i)
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
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(mapnik::attribute_descriptor(fld_name, mapnik::Integer));
|
|
|
|
break;
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
case SQLITE_FLOAT:
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Double));
|
|
|
|
break;
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
case SQLITE_TEXT:
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
|
|
|
|
break;
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
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(mapnik::attribute_descriptor(fld_name, mapnik::String));
|
2011-12-16 02:18:22 +01:00
|
|
|
break;
|
2011-10-29 06:24:47 +02:00
|
|
|
|
|
|
|
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;
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
default:
|
|
|
|
#ifdef MAPNIK_DEBUG
|
|
|
|
std::clog << "Sqlite Plugin: unknown type_oid=" << type_oid << std::endl;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
static bool table_info(std::string & key_field,
|
2011-11-14 04:37:50 +01:00
|
|
|
bool detected_types,
|
|
|
|
std::string & field,
|
|
|
|
std::string & table,
|
|
|
|
mapnik::layer_descriptor & desc,
|
|
|
|
boost::shared_ptr<sqlite_connection> ds)
|
2011-10-29 06:24:47 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
// http://www.sqlite.org/pragma.html#pragma_table_info
|
|
|
|
// use pragma table_info to detect primary key
|
2011-11-14 04:37:50 +01:00
|
|
|
// and to detect types if no subquery is used or
|
2011-10-29 06:24:47 +02:00
|
|
|
// if the subquery-based type detection failed
|
|
|
|
std::ostringstream s;
|
|
|
|
s << "PRAGMA table_info(" << table << ")";
|
2011-11-04 00:51:37 +01:00
|
|
|
boost::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
|
2011-10-29 06:24:47 +02:00
|
|
|
bool found_table = false;
|
|
|
|
bool found_pk = false;
|
|
|
|
while (rs->is_valid() && rs->step_next())
|
|
|
|
{
|
|
|
|
found_table = true;
|
|
|
|
const char* fld_name = rs->column_text(1);
|
|
|
|
std::string fld_type(rs->column_text(2));
|
|
|
|
int fld_pk = rs->column_integer(5);
|
|
|
|
boost::algorithm::to_lower(fld_type);
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
// TODO - how to handle primary keys on multiple columns ?
|
|
|
|
if (key_field.empty() && ! found_pk && fld_pk != 0)
|
|
|
|
{
|
|
|
|
key_field = fld_name;
|
|
|
|
found_pk = true;
|
|
|
|
}
|
|
|
|
if (! detected_types)
|
|
|
|
{
|
|
|
|
// see 2.1 "Column Affinity" at http://www.sqlite.org/datatype3.html
|
|
|
|
// TODO - refactor this somehow ?
|
|
|
|
if (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"))
|
2011-11-14 04:37:50 +01:00
|
|
|
||
|
2011-10-29 06:24:47 +02:00
|
|
|
(boost::algorithm::icontains(fld_name, "geom") ||
|
|
|
|
boost::algorithm::icontains(fld_name, "point") ||
|
|
|
|
boost::algorithm::icontains(fld_name, "linestring") ||
|
|
|
|
boost::algorithm::icontains(fld_name, "polygon")))
|
2011-11-14 04:37:50 +01:00
|
|
|
)
|
2011-10-29 06:24:47 +02:00
|
|
|
{
|
|
|
|
field = std::string(fld_name);
|
|
|
|
}
|
|
|
|
else if (boost::algorithm::contains(fld_type, "int"))
|
|
|
|
{
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Integer));
|
|
|
|
}
|
|
|
|
else if (boost::algorithm::contains(fld_type, "text") ||
|
|
|
|
boost::algorithm::contains(fld_type, "char") ||
|
|
|
|
boost::algorithm::contains(fld_type, "clob"))
|
|
|
|
{
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
|
|
|
|
}
|
|
|
|
else if (boost::algorithm::contains(fld_type, "real") ||
|
|
|
|
boost::algorithm::contains(fld_type, "float") ||
|
|
|
|
boost::algorithm::contains(fld_type, "double"))
|
|
|
|
{
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Double));
|
|
|
|
}
|
|
|
|
else if (boost::algorithm::contains(fld_type, "blob"))
|
|
|
|
{
|
|
|
|
if (! field.empty())
|
|
|
|
{
|
|
|
|
desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
|
|
|
|
}
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
#ifdef MAPNIK_DEBUG
|
2011-10-29 06:24:47 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// "Column Affinity" says default to "Numeric" but for now we pass..
|
|
|
|
//desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double));
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
// TODO - this should not fail when we specify geometry_field in XML file
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
std::clog << "Sqlite Plugin: column '"
|
|
|
|
<< std::string(fld_name)
|
|
|
|
<< "' unhandled due to unknown type: "
|
|
|
|
<< fld_type << std::endl;
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
#endif
|
2011-10-29 06:24:47 +02:00
|
|
|
}
|
|
|
|
}
|
2011-11-14 04:37:50 +01:00
|
|
|
|
2011-10-29 06:24:47 +02:00
|
|
|
return found_table;
|
|
|
|
}
|
2009-02-09 20:43:57 +01:00
|
|
|
};
|
|
|
|
|
2011-11-04 00:51:37 +01:00
|
|
|
#endif // MAPNIK_SQLITE_UTILS_HPP
|