# include "config.h"
#endif
-// to ensure compatability between sqlite3_int64 and PositionedID,
-// force the type used by sqlite to match PositionedID explicitly
-#define SQLITE_INT64_TYPE int64_t
-#define SQLITE_UINT64_TYPE uint64_t
-
#include "NavDataCache.hxx"
// std
// boost
#include <boost/foreach.hpp>
-#include "sqlite3.h"
+
+#ifdef SYSTEM_SQLITE
+// the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE,
+// so we have to hope sizeof(int64_t) matches sizeof(sqlite3_int64).
+// otherwise things will go bad quickly.
+ #include "sqlite3.h"
+#else
+// to ensure compatability between sqlite3_int64 and PositionedID,
+// force the type used by sqlite to match PositionedID explicitly
+#define SQLITE_INT64_TYPE int64_t
+#define SQLITE_UINT64_TYPE uint64_t
+
+ #include "fg_sqlite3.h"
+#endif
// SimGear
+#include <simgear/sg_inlines.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/bucket/newbucket.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+#include <Main/options.hxx>
#include "markerbeacon.hxx"
#include "navrecord.hxx"
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
#include <Airports/runways.hxx>
#include <ATC/CommStation.hxx>
#include "fix.hxx"
#include "PositionedOctree.hxx"
#include <Airports/apt_loader.hxx>
#include <Navaids/airways.hxx>
+#include "poidb.hxx"
#include <Airports/parking.hxx>
#include <Airports/gnnode.hxx>
+#include "CacheSchema.h"
using std::string;
namespace {
-const int SCHEMA_VERSION = 5;
-
+const int MAX_RETRIES = 10;
+
+const int CACHE_SIZE_KBYTES= 32 * 1024;
+
// bind a std::string to a sqlite statement. The std::string must live the
// entire duration of the statement execution - do not pass a temporary
// std::string, or the compiler may delete it, freeing the C-string storage,
outer(o),
db(NULL),
path(p),
+ readOnly(false),
cacheHits(0),
- cacheMisses(0)
+ cacheMisses(0),
+ transactionLevel(0),
+ transactionAborted(false)
{
}
~NavDataCachePrivate()
{
- BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
- sqlite3_finalize(stmt);
- }
- prepared.clear();
-
- sqlite3_close(db);
+ close();
}
void init()
{
SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache at:" << path);
- sqlite3_open_v2(path.c_str(), &db,
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
-
+
+ readOnly = fgGetBool("/sim/fghome-readonly", false);
+
+ int openFlags = readOnly ? SQLITE_OPEN_READONLY :
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+ // see http://code.google.com/p/flightgear-bugs/issues/detail?id=1055
+ // for the UTF8 / path logic here
+ std::string pathUtf8 = simgear::strutils::convertWindowsLocal8BitToUtf8(path.str());
+ sqlite3_open_v2(pathUtf8.c_str(), &db, openFlags, NULL);
sqlite3_stmt_ptr checkTables =
prepare("SELECT count(*) FROM sqlite_master WHERE name='properties'");
execSelect(checkTables);
bool didCreate = false;
- if (sqlite3_column_int(checkTables, 0) == 0) {
+ if (!readOnly && (sqlite3_column_int(checkTables, 0) == 0)) {
SG_LOG(SG_NAVCACHE, SG_INFO, "will create tables");
initTables();
didCreate = true;
}
readPropertyQuery = prepare("SELECT value FROM properties WHERE key=?");
- writePropertyQuery = prepare("INSERT OR REPLACE INTO properties "
- "(key, value) VALUES (?,?)");
+ writePropertyQuery = prepare("INSERT INTO properties (key, value) VALUES (?,?)");
+ clearProperty = prepare("DELETE FROM properties WHERE key=?1");
if (didCreate) {
writeIntProperty("schema-version", SCHEMA_VERSION);
}
}
+ // see http://www.sqlite.org/pragma.html#pragma_cache_size
+ // for the details, small cache would cause thrashing.
+ std::ostringstream q;
+ q << "PRAGMA cache_size=-" << CACHE_SIZE_KBYTES << ";";
+ runSQL(q.str());
prepareQueries();
}
+ void close()
+ {
+ BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
+ sqlite3_finalize(stmt);
+ }
+ prepared.clear();
+ sqlite3_close(db);
+ }
+
void checkCacheFile()
{
SG_LOG(SG_NAVCACHE, SG_INFO, "running DB integrity check");
SGTimeStamp st;
st.stamp();
- sqlite3_stmt_ptr stmt = prepare("PRAGMA integrity_check(1)");
+ sqlite3_stmt_ptr stmt = prepare("PRAGMA quick_check(1)");
if (!execSelect(stmt)) {
throw sg_exception("DB integrity check failed to run");
}
SG_LOG(SG_NAVCACHE, SG_INFO, "NavDataCache integrity check took:" << st.elapsedMSec());
finalize(stmt);
}
+
+ bool isCachedFileModified(const SGPath& path, bool verbose);
void callSqlite(int result, const string& sql)
{
bool stepSelect(sqlite3_stmt_ptr stmt)
{
- int result = sqlite3_step(stmt);
- if (result == SQLITE_ROW) {
- return true; // at least one result row
- }
+ int retries = 0;
+ int result;
+ while (retries < MAX_RETRIES) {
+ result = sqlite3_step(stmt);
+ if (result == SQLITE_ROW) {
+ return true; // at least one result row
+ }
+
+ if (result == SQLITE_DONE) {
+ return false; // no result rows
+ }
+
+ if (result != SQLITE_BUSY) {
+ break;
+ }
+
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache contention on select, will retry:" << retries);
+ SGTimeStamp::sleepForMSec(++retries * 10);
+ } // of retry loop for DB locked
- if (result == SQLITE_DONE) {
- return false; // no result rows
+ if (retries >= MAX_RETRIES) {
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "exceeded maximum number of SQLITE_BUSY retries");
+ return false;
}
string errMsg;
SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite API abuse");
} else {
errMsg = sqlite3_errmsg(db);
- SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg
- << " while running:\n\t" << sqlite3_sql(stmt));
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg << " (" << result
+ << ") while running:\n\t" << sqlite3_sql(stmt));
}
throw sg_exception("Sqlite error:" + errMsg, sqlite3_sql(stmt));
sqlite3_int64 execInsert(sqlite3_stmt_ptr stmt)
{
execSelect(stmt);
- return sqlite3_last_insert_rowid(db);
+ sqlite3_int64 rowid = sqlite3_last_insert_rowid(db);
+ reset(stmt);
+ return rowid;
}
void execUpdate(sqlite3_stmt_ptr stmt)
{
execSelect(stmt);
+ reset(stmt);
}
-
+
void initTables()
{
- runSQL("CREATE TABLE properties ("
- "key VARCHAR,"
- "value VARCHAR"
- ")");
-
- runSQL("CREATE TABLE stat_cache ("
- "path VARCHAR unique,"
- "stamp INT"
- ")");
-
- runSQL("CREATE TABLE positioned ("
- "type INT,"
- "ident VARCHAR collate nocase,"
- "name VARCHAR collate nocase,"
- "airport INT64,"
- "lon FLOAT,"
- "lat FLOAT,"
- "elev_m FLOAT,"
- "octree_node INT,"
- "cart_x FLOAT,"
- "cart_y FLOAT,"
- "cart_z FLOAT"
- ")");
-
- runSQL("CREATE INDEX pos_octree ON positioned(octree_node)");
- runSQL("CREATE INDEX pos_ident ON positioned(ident collate nocase)");
- runSQL("CREATE INDEX pos_name ON positioned(name collate nocase)");
- // allow efficient querying of 'all ATIS at this airport' or
- // 'all towers at this airport'
- runSQL("CREATE INDEX pos_apt_type ON positioned(airport, type)");
-
- runSQL("CREATE TABLE airport ("
- "has_metar BOOL"
- ")"
- );
-
- runSQL("CREATE TABLE comm ("
- "freq_khz INT,"
- "range_nm INT"
- ")"
- );
-
- runSQL("CREATE INDEX comm_freq ON comm(freq_khz)");
-
- runSQL("CREATE TABLE runway ("
- "heading FLOAT,"
- "length_ft FLOAT,"
- "width_m FLOAT,"
- "surface INT,"
- "displaced_threshold FLOAT,"
- "stopway FLOAT,"
- "reciprocal INT64,"
- "ils INT64"
- ")"
- );
-
- runSQL("CREATE TABLE navaid ("
- "freq INT,"
- "range_nm INT,"
- "multiuse FLOAT,"
- "runway INT64,"
- "colocated INT64"
- ")"
- );
-
- runSQL("CREATE INDEX navaid_freq ON navaid(freq)");
-
- runSQL("CREATE TABLE octree (children INT)");
-
- runSQL("CREATE TABLE airway ("
- "ident VARCHAR collate nocase,"
- "network INT" // high-level or low-level
- ")");
-
- runSQL("CREATE INDEX airway_ident ON airway(ident)");
-
- runSQL("CREATE TABLE airway_edge ("
- "network INT,"
- "airway INT64,"
- "a INT64,"
- "b INT64"
- ")");
-
- runSQL("CREATE INDEX airway_edge_from ON airway_edge(a)");
-
- runSQL("CREATE TABLE taxi_node ("
- "hold_type INT,"
- "on_runway BOOL,"
- "pushback BOOL"
- ")");
-
- runSQL("CREATE TABLE parking ("
- "heading FLOAT,"
- "radius INT,"
- "gate_type VARCHAR,"
- "airlines VARCHAR,"
- "pushback INT64"
- ")");
-
- runSQL("CREATE TABLE groundnet_edge ("
- "airport INT64,"
- "a INT64,"
- "b INT64"
- ")");
-
- runSQL("CREATE INDEX groundnet_edge_airport ON groundnet_edge(airport)");
- runSQL("CREATE INDEX groundnet_edge_from ON groundnet_edge(a)");
+ string_list commands = simgear::strutils::split(SCHEMA_SQL, ";");
+ BOOST_FOREACH(std::string sql, commands) {
+ if (sql.empty()) {
+ continue;
+ }
+
+ runSQL(sql);
+ } // of commands in scheme loop
}
void prepareQueries()
{
- clearProperty = prepare("DELETE FROM properties WHERE key=?1");
writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
+ beginTransactionStmt = prepare("BEGIN");
+ commitTransactionStmt = prepare("COMMIT");
+ rollbackTransactionStmt = prepare("ROLLBACK");
+
+
#define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node"
#define AND_TYPED "AND type>=?2 AND type <=?3"
statCacheCheck = prepare("SELECT stamp FROM stat_cache WHERE path=?");
setRunwayReciprocal = prepare("UPDATE runway SET reciprocal=?2 WHERE rowid=?1");
setRunwayILS = prepare("UPDATE runway SET ils=?2 WHERE rowid=?1");
- updateRunwayThreshold = prepare("UPDATE runway SET heading=?2, displaced_threshold=?3, stopway=?4 WHERE rowid=?1");
+ setNavaidColocated = prepare("UPDATE navaid SET colocated=?2 WHERE rowid=?1");
insertPositionedQuery = prepare("INSERT INTO positioned "
"(type, ident, name, airport, lon, lat, elev_m, octree_node, "
insertAirport = prepare("INSERT INTO airport (rowid, has_metar) VALUES (?, ?)");
insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)"
" VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
- updateILS = prepare("UPDATE navaid SET multiuse=?2 WHERE rowid=?1");
insertCommStation = prepare("INSERT INTO comm (rowid, freq_khz, range_nm)"
" VALUES (?, ?, ?)");
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)");
runwayLengthFtQuery = prepare("SELECT length_ft FROM runway WHERE rowid=?1");
+ removePOIQuery = prepare("DELETE FROM positioned WHERE type=?1 AND ident=?2");
+
// query statement
findClosestWithIdent = prepare("SELECT rowid FROM positioned WHERE ident=?1 "
AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
sqlite3_bind_int(searchAirports, 2, FGPositioned::AIRPORT);
sqlite3_bind_int(searchAirports, 3, FGPositioned::SEAPORT);
+ getAllAirports = prepare("SELECT ident, name FROM positioned WHERE type>=?1 AND type <=?2");
+ sqlite3_bind_int(getAllAirports, 1, FGPositioned::AIRPORT);
+ sqlite3_bind_int(getAllAirports, 2, FGPositioned::SEAPORT);
+
+
getAirportItemByIdent = prepare("SELECT rowid FROM positioned WHERE airport=?1 AND ident=?2 AND type=?3");
findAirportRunway = prepare("SELECT airport, rowid FROM positioned WHERE ident=?2 AND type=?3 AND airport="
void writeIntProperty(const string& key, int value)
{
+ sqlite_bind_stdstring(clearProperty, 1, key);
+ execUpdate(clearProperty);
+
sqlite_bind_stdstring(writePropertyQuery, 1, key);
sqlite3_bind_int(writePropertyQuery, 2, value);
- execSelect(writePropertyQuery);
+ execUpdate(writePropertyQuery);
}
- FGPositioned* loadFromStmt(sqlite3_stmt_ptr query);
+ FGPositioned* loadById(sqlite_int64 rowId, sqlite3_int64& aptId);
FGAirport* loadAirport(sqlite_int64 rowId,
FGPositioned::Type ty,
const string& id, const string& name, const SGGeod& pos)
{
- reset(loadAirportStmt);
sqlite3_bind_int64(loadAirportStmt, 1, rowId);
execSelect1(loadAirportStmt);
bool hasMetar = (sqlite3_column_int(loadAirportStmt, 0) > 0);
+ reset(loadAirportStmt);
+
return new FGAirport(rowId, id, pos, name, hasMetar, ty);
}
FGRunwayBase* loadRunway(sqlite3_int64 rowId, FGPositioned::Type ty,
const string& id, const SGGeod& pos, PositionedID apt)
{
- reset(loadRunwayStmt);
sqlite3_bind_int(loadRunwayStmt, 1, rowId);
execSelect1(loadRunwayStmt);
int surface = sqlite3_column_int(loadRunwayStmt, 3);
if (ty == FGPositioned::TAXIWAY) {
+ reset(loadRunwayStmt);
return new FGTaxiway(rowId, id, pos, heading, lengthM, widthM, surface);
+ } else if (ty == FGPositioned::HELIPAD) {
+ reset(loadRunwayStmt);
+ return new FGHelipad(rowId, apt, id, pos, heading, lengthM, widthM, surface);
} else {
double displacedThreshold = sqlite3_column_double(loadRunwayStmt, 4);
double stopway = sqlite3_column_double(loadRunwayStmt, 5);
PositionedID reciprocal = sqlite3_column_int64(loadRunwayStmt, 6);
PositionedID ils = sqlite3_column_int64(loadRunwayStmt, 7);
FGRunway* r = new FGRunway(rowId, apt, id, pos, heading, lengthM, widthM,
- displacedThreshold, stopway, surface, false);
+ displacedThreshold, stopway, surface);
if (reciprocal > 0) {
r->setReciprocalRunway(reciprocal);
r->setILS(ils);
}
+ reset(loadRunwayStmt);
return r;
}
}
const SGGeod& pos,
PositionedID airport)
{
- reset(loadCommStation);
sqlite3_bind_int64(loadCommStation, 1, rowId);
execSelect1(loadCommStation);
int range = sqlite3_column_int(loadCommStation, 0);
int freqKhz = sqlite3_column_int(loadCommStation, 1);
+ reset(loadCommStation);
CommStation* c = new CommStation(rowId, name, ty, pos, freqKhz, range);
c->setAirport(airport);
FGPositioned::Type ty, const string& id,
const string& name, const SGGeod& pos)
{
- reset(loadNavaid);
sqlite3_bind_int64(loadNavaid, 1, rowId);
execSelect1(loadNavaid);
if ((ty == FGPositioned::OM) || (ty == FGPositioned::IM) ||
(ty == FGPositioned::MM))
{
+ reset(loadNavaid);
return new FGMarkerBeaconRecord(rowId, ty, runway, pos);
}
int rangeNm = sqlite3_column_int(loadNavaid, 0),
- freq = sqlite3_column_int(loadNavaid, 1);
+ freq = sqlite3_column_int(loadNavaid, 1);
double mulituse = sqlite3_column_double(loadNavaid, 2);
- //sqlite3_int64 colocated = sqlite3_column_int64(loadNavaid, 4);
-
- return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
+ PositionedID colocated = sqlite3_column_int64(loadNavaid, 4);
+ reset(loadNavaid);
+
+ FGNavRecord* n =
+ (ty == FGPositioned::MOBILE_TACAN)
+ ? new FGMobileNavRecord
+ (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway)
+ : new FGNavRecord
+ (rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
+
+ if (colocated)
+ n->setColocatedDME(colocated);
+
+ return n;
}
FGPositioned* loadParking(sqlite3_int64 rowId,
const string& name, const SGGeod& pos,
PositionedID airport)
{
- reset(loadParkingPos);
sqlite3_bind_int64(loadParkingPos, 1, rowId);
execSelect1(loadParkingPos);
string aircraftType((char*) sqlite3_column_text(loadParkingPos, 2));
string airlines((char*) sqlite3_column_text(loadParkingPos, 3));
PositionedID pushBack = sqlite3_column_int64(loadParkingPos, 4);
+ reset(loadParkingPos);
return new FGParking(rowId, pos, heading, radius, name, aircraftType, airlines, pushBack);
}
FGPositioned* loadTaxiNode(sqlite3_int64 rowId, const SGGeod& pos,
PositionedID airport)
{
- reset(loadTaxiNodeStmt);
sqlite3_bind_int64(loadTaxiNodeStmt, 1, rowId);
execSelect1(loadTaxiNodeStmt);
int hold_type = sqlite3_column_int(loadTaxiNodeStmt, 0);
bool onRunway = sqlite3_column_int(loadTaxiNodeStmt, 1);
+ reset(loadTaxiNodeStmt);
+
return new FGTaxiNode(rowId, pos, onRunway, hold_type);
}
{
SGVec3d cartPos(SGVec3d::fromGeod(pos));
- reset(insertPositionedQuery);
sqlite3_bind_int(insertPositionedQuery, 1, ty);
sqlite_bind_stdstring(insertPositionedQuery, 2, ident);
sqlite_bind_stdstring(insertPositionedQuery, 3, name);
sqlite3_bind_double(insertPositionedQuery, 10, cartPos.y());
sqlite3_bind_double(insertPositionedQuery, 11, cartPos.z());
- PositionedID r = execInsert(insertPositionedQuery);
+ PositionedID r = execInsert(insertPositionedQuery);
return r;
}
- FGPositioned::List findAllByString(const string& s, const string& column,
+ FGPositionedList findAllByString(const string& s, const string& column,
FGPositioned::Filter* filter, bool exact)
{
string query = s;
findByStringDict[sql] = stmt;
}
- reset(stmt);
sqlite_bind_stdstring(stmt, 1, query);
if (filter) {
sqlite3_bind_int(stmt, 2, filter->minType());
sqlite3_bind_int(stmt, 3, filter->maxType());
}
- FGPositioned::List result;
+ FGPositionedList result;
// run the prepared SQL
while (stepSelect(stmt))
{
result.push_back(pos);
}
+ reset(stmt);
return result;
}
while (stepSelect(query)) {
result.push_back(sqlite3_column_int64(query, 0));
}
+ reset(query);
return result;
}
double runwayLengthFt(PositionedID rwy)
{
- reset(runwayLengthFtQuery);
sqlite3_bind_int64(runwayLengthFtQuery, 1, rwy);
execSelect1(runwayLengthFtQuery);
- return sqlite3_column_double(runwayLengthFtQuery, 0);
+ double length = sqlite3_column_double(runwayLengthFtQuery, 0);
+ reset(runwayLengthFtQuery);
+ return length;
}
void flushDeferredOctreeUpdates()
{
BOOST_FOREACH(Octree::Branch* nd, deferredOctreeUpdates) {
- reset(updateOctreeChildren);
sqlite3_bind_int64(updateOctreeChildren, 1, nd->guid());
sqlite3_bind_int(updateOctreeChildren, 2, nd->childMask());
execUpdate(updateOctreeChildren);
deferredOctreeUpdates.clear();
}
+
+ void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent)
+ {
+ sqlite3_bind_int(removePOIQuery, 1, ty);
+ sqlite_bind_stdstring(removePOIQuery, 2, aIdent);
+ execUpdate(removePOIQuery);
+ reset(removePOIQuery);
+ }
NavDataCache* outer;
sqlite3* db;
SGPath path;
-
+ bool readOnly;
+
/// the actual cache of ID -> instances. This holds an owning reference,
/// so once items are in the cache they will never be deleted until
/// the cache drops its reference
PositionedCache cache;
unsigned int cacheHits, cacheMisses;
+
+ /**
+ * record the levels of open transaction objects we have
+ */
+ unsigned int transactionLevel;
+ bool transactionAborted;
+ sqlite3_stmt_ptr beginTransactionStmt, commitTransactionStmt, rollbackTransactionStmt;
- SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath,
+ SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath, poiDatPath,
carrierDatPath, airwayDatPath;
sqlite3_stmt_ptr readPropertyQuery, writePropertyQuery,
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
insertCommStation, insertNavaid;
- sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS,
- setAirportPos, updateRunwayThreshold, updateILS;
+ sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated,
+ setAirportPos;
+ sqlite3_stmt_ptr removePOIQuery;
sqlite3_stmt_ptr findClosestWithIdent;
// octree (spatial index) related queries
sqlite3_stmt_ptr getOctreeChildren, insertOctree, updateOctreeChildren,
getOctreeLeafChildren;
- sqlite3_stmt_ptr searchAirports;
+ sqlite3_stmt_ptr searchAirports, getAllAirports;
sqlite3_stmt_ptr findCommByFreq, findNavsByFreq,
findNavsByFreqNoPos, findNavaidForRunway;
sqlite3_stmt_ptr getAirportItems, getAirportItemByIdent;
std::auto_ptr<RebuildThread> rebuilder;
};
- //////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
-FGPositioned* NavDataCache::NavDataCachePrivate::loadFromStmt(sqlite3_stmt_ptr query)
+FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid,
+ sqlite3_int64& aptId)
{
- execSelect1(query);
- sqlite3_int64 rowid = sqlite3_column_int64(query, 0);
- FGPositioned::Type ty = (FGPositioned::Type) sqlite3_column_int(query, 1);
- string ident = (char*) sqlite3_column_text(query, 2);
- string name = (char*) sqlite3_column_text(query, 3);
- sqlite3_int64 aptId = sqlite3_column_int64(query, 4);
- double lon = sqlite3_column_double(query, 5);
- double lat = sqlite3_column_double(query, 6);
- double elev = sqlite3_column_double(query, 7);
+ sqlite3_bind_int64(loadPositioned, 1, rowid);
+ execSelect1(loadPositioned);
+
+ assert(rowid == sqlite3_column_int64(loadPositioned, 0));
+ FGPositioned::Type ty = (FGPositioned::Type) sqlite3_column_int(loadPositioned, 1);
+
+ PositionedID prowid = static_cast<PositionedID>(rowid);
+ string ident = (char*) sqlite3_column_text(loadPositioned, 2);
+ string name = (char*) sqlite3_column_text(loadPositioned, 3);
+ aptId = sqlite3_column_int64(loadPositioned, 4);
+ double lon = sqlite3_column_double(loadPositioned, 5);
+ double lat = sqlite3_column_double(loadPositioned, 6);
+ double elev = sqlite3_column_double(loadPositioned, 7);
SGGeod pos = SGGeod::fromDegM(lon, lat, elev);
+
+ reset(loadPositioned);
switch (ty) {
case FGPositioned::AIRPORT:
return loadAirport(rowid, ty, ident, name, pos);
case FGPositioned::TOWER:
- return new AirportTower(rowid, aptId, ident, pos);
+ return new AirportTower(prowid, aptId, ident, pos);
case FGPositioned::RUNWAY:
+ case FGPositioned::HELIPAD:
case FGPositioned::TAXIWAY:
return loadRunway(rowid, ty, ident, pos, aptId);
case FGPositioned::DME:
case FGPositioned::TACAN:
case FGPositioned::MOBILE_TACAN:
- {
- if (aptId > 0) {
- FGAirport* apt = (FGAirport*) outer->loadById(aptId);
- if (apt->validateILSData()) {
- SG_LOG(SG_NAVCACHE, SG_INFO, "re-loaded ILS data for " << apt->ident());
- // queried data above is probably invalid, force us to go around again
- // (the next time through, validateILSData will return false)
- return outer->loadById(rowid);
- }
- }
-
return loadNav(rowid, ty, ident, name, pos);
- }
case FGPositioned::FIX:
return new FGFix(rowid, ident, pos);
case FGPositioned::WAYPOINT:
+ case FGPositioned::COUNTRY:
+ case FGPositioned::CITY:
+ case FGPositioned::TOWN:
+ case FGPositioned::VILLAGE:
{
- FGPositioned* wpt = new FGPositioned(rowid, FGPositioned::WAYPOINT, ident, pos);
+ FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos);
return wpt;
}
return NULL;
}
}
-
+
+bool NavDataCache::NavDataCachePrivate::isCachedFileModified(const SGPath& path, bool verbose)
+{
+ if (!path.exists()) {
+ throw sg_io_exception("isCachedFileModified: Missing file:" + path.str());
+ }
+
+ sqlite_bind_temp_stdstring(statCacheCheck, 1, path.str());
+ bool isModified = true;
+ sgDebugPriority logLevel = verbose ? SG_WARN : SG_DEBUG;
+ if (execSelect(statCacheCheck)) {
+ time_t modtime = sqlite3_column_int64(statCacheCheck, 0);
+ time_t delta = std::labs(modtime - path.modTime());
+ if (delta != 0)
+ {
+ SG_LOG(SG_NAVCACHE, logLevel, "NavCache: rebuild required for " << path <<
+ ". Timestamps: " << modtime << " != " << path.modTime());
+ }
+ else
+ {
+ SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: no rebuild required for " << path);
+ }
+
+ isModified = (delta != 0);
+ } else {
+ SG_LOG(SG_NAVCACHE, logLevel, "NavCache: initial build required for " << path);
+ }
+
+ reset(statCacheCheck);
+ return isModified;
+}
static NavDataCache* static_instance = NULL;
{
const int MAX_TRIES = 3;
SGPath homePath(globals->get_fg_home());
- homePath.append("navdata.cache");
+
+ std::ostringstream os;
+ string_list versionParts = simgear::strutils::split(VERSION, ".");
+ if (versionParts.size() < 2) {
+ os << "navdata.cache";
+ } else {
+ os << "navdata_" << versionParts[0] << "_" << versionParts[1] << ".cache";
+ }
+
+ homePath.append(os.str());
for (int t=0; t < MAX_TRIES; ++t) {
try {
SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what()
<< " (attempt " << t << ")");
d.reset();
- homePath.remove();
+
+ // only wipe the existing if not readonly
+ if (!fgGetBool("/sim/fghome-readonly", false)) {
+ homePath.remove();
+ }
}
} // of retry loop
d->fixDatPath = SGPath(globals->get_fg_root());
d->fixDatPath.append("Navaids/fix.dat.gz");
+
+ d->poiDatPath = SGPath(globals->get_fg_root());
+ d->poiDatPath.append("Navaids/poi.dat.gz");
d->carrierDatPath = SGPath(globals->get_fg_root());
d->carrierDatPath.append("Navaids/carrier_nav.dat.gz");
{
assert(static_instance == this);
static_instance = NULL;
- SG_LOG(SG_NAVCACHE, SG_INFO, "closing the navcache");
d.reset();
}
bool NavDataCache::isRebuildRequired()
{
- if (isCachedFileModified(d->aptDatPath) ||
- isCachedFileModified(d->metarDatPath) ||
- isCachedFileModified(d->navDatPath) ||
- isCachedFileModified(d->fixDatPath) ||
- isCachedFileModified(d->airwayDatPath))
+ if (d->readOnly) {
+ return false;
+ }
+
+ if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
+ SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: restore-defaults requested, will rebuild cache");
+ return true;
+ }
+
+ if (d->isCachedFileModified(d->aptDatPath, true) ||
+ d->isCachedFileModified(d->metarDatPath, true) ||
+ d->isCachedFileModified(d->navDatPath, true) ||
+ d->isCachedFileModified(d->fixDatPath, true) ||
+ d->isCachedFileModified(d->carrierDatPath, true) ||
+// since POI loading is disabled on Windows, don't check for it
+// this caused: https://code.google.com/p/flightgear-bugs/issues/detail?id=1227
+#ifndef SG_WINDOWS
+ d->isCachedFileModified(d->poiDatPath, true) ||
+#endif
+ d->isCachedFileModified(d->airwayDatPath, true))
{
- SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: rebuild required");
+ SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: main cache rebuild required");
return true;
}
-
- SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: no rebuild required");
+
+ string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";");
+ if (readStringProperty("scenery_paths") != sceneryPaths) {
+ SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: scenery paths changed,dropping ground net");
+ dropAllGroundnets();
+ writeStringProperty("scenery_paths", sceneryPaths);
+ }
+
+ SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: no main cache rebuild required");
return false;
}
void NavDataCache::doRebuild()
{
try {
- d->runSQL("BEGIN");
- d->runSQL("DELETE FROM positioned");
- d->runSQL("DELETE FROM airport");
- d->runSQL("DELETE FROM runway");
- d->runSQL("DELETE FROM navaid");
- d->runSQL("DELETE FROM comm");
- d->runSQL("DELETE FROM octree");
- d->runSQL("DELETE FROM airway");
- d->runSQL("DELETE FROM airway_edge");
-
- // initialise the root octree node
- d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)");
+ d->close(); // completely close the sqlite object
+ d->path.remove(); // remove the file on disk
+ d->init(); // start again from scratch
+ // initialise the root octree node
+ d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)");
+
SGTimeStamp st;
- st.stamp();
-
- airportDBLoad(d->aptDatPath);
- SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec());
-
- metarDataLoad(d->metarDatPath);
- stampCacheFile(d->aptDatPath);
- stampCacheFile(d->metarDatPath);
-
- st.stamp();
- loadFixes(d->fixDatPath);
- stampCacheFile(d->fixDatPath);
- SG_LOG(SG_NAVCACHE, SG_INFO, "fix.dat load took:" << st.elapsedMSec());
-
- st.stamp();
- navDBInit(d->navDatPath);
- stampCacheFile(d->navDatPath);
- SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec());
-
- loadCarrierNav(d->carrierDatPath);
- stampCacheFile(d->carrierDatPath);
-
- st.stamp();
- Airway::load(d->airwayDatPath);
- stampCacheFile(d->airwayDatPath);
- SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec());
-
- d->flushDeferredOctreeUpdates();
-
- d->runSQL("COMMIT");
+ {
+ Transaction txn(this);
+
+ st.stamp();
+ airportDBLoad(d->aptDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec());
+
+ metarDataLoad(d->metarDatPath);
+ stampCacheFile(d->aptDatPath);
+ stampCacheFile(d->metarDatPath);
+
+ st.stamp();
+ loadFixes(d->fixDatPath);
+ stampCacheFile(d->fixDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "fix.dat load took:" << st.elapsedMSec());
+
+ st.stamp();
+ navDBInit(d->navDatPath);
+ stampCacheFile(d->navDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec());
+
+ st.stamp();
+ txn.commit();
+ SG_LOG(SG_NAVCACHE, SG_INFO, "stage 1 commit took:" << st.elapsedMSec());
+ }
+
+#ifdef SG_WINDOWS
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "SKIPPING POI load on Windows");
+#else
+ {
+ Transaction txn(this);
+
+ st.stamp();
+ poiDBInit(d->poiDatPath);
+ stampCacheFile(d->poiDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "poi.dat load took:" << st.elapsedMSec());
+
+ st.stamp();
+ txn.commit();
+ SG_LOG(SG_NAVCACHE, SG_INFO, "POI commit took:" << st.elapsedMSec());
+ }
+#endif
+
+ {
+ Transaction txn(this);
+ loadCarrierNav(d->carrierDatPath);
+ stampCacheFile(d->carrierDatPath);
+
+ st.stamp();
+ Airway::load(d->airwayDatPath);
+ stampCacheFile(d->airwayDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec());
+
+ d->flushDeferredOctreeUpdates();
+
+ string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";");
+ writeStringProperty("scenery_paths", sceneryPaths);
+
+ st.stamp();
+ txn.commit();
+ SG_LOG(SG_NAVCACHE, SG_INFO, "final commit took:" << st.elapsedMSec());
+
+ }
+
} catch (sg_exception& e) {
SG_LOG(SG_NAVCACHE, SG_ALERT, "caught exception rebuilding navCache:" << e.what());
- // abandon the DB transation completely
- d->runSQL("ROLLBACK");
}
}
int NavDataCache::readIntProperty(const string& key)
{
- d->reset(d->readPropertyQuery);
sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
+ int result = 0;
if (d->execSelect(d->readPropertyQuery)) {
- return sqlite3_column_int(d->readPropertyQuery, 0);
+ result = sqlite3_column_int(d->readPropertyQuery, 0);
} else {
SG_LOG(SG_NAVCACHE, SG_WARN, "readIntProperty: unknown:" << key);
- return 0; // no such property
}
+
+ d->reset(d->readPropertyQuery);
+ return result;
}
double NavDataCache::readDoubleProperty(const string& key)
{
- d->reset(d->readPropertyQuery);
sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
+ double result = 0.0;
if (d->execSelect(d->readPropertyQuery)) {
- return sqlite3_column_double(d->readPropertyQuery, 0);
+ result = sqlite3_column_double(d->readPropertyQuery, 0);
} else {
SG_LOG(SG_NAVCACHE, SG_WARN, "readDoubleProperty: unknown:" << key);
- return 0.0; // no such property
}
+
+ d->reset(d->readPropertyQuery);
+ return result;
}
string NavDataCache::readStringProperty(const string& key)
{
- d->reset(d->readPropertyQuery);
sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
+ string result;
if (d->execSelect(d->readPropertyQuery)) {
- return (char*) sqlite3_column_text(d->readPropertyQuery, 0);
+ result = (char*) sqlite3_column_text(d->readPropertyQuery, 0);
} else {
SG_LOG(SG_NAVCACHE, SG_WARN, "readStringProperty: unknown:" << key);
- return string(); // no such property
}
+
+ d->reset(d->readPropertyQuery);
+ return result;
}
void NavDataCache::writeIntProperty(const string& key, int value)
void NavDataCache::writeStringProperty(const string& key, const string& value)
{
- d->reset(d->writePropertyQuery);
+ sqlite_bind_stdstring(d->clearProperty, 1, key);
+ d->execUpdate(d->clearProperty);
+
sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
sqlite_bind_stdstring(d->writePropertyQuery, 2, value);
- d->execSelect(d->writePropertyQuery);
+ d->execUpdate(d->writePropertyQuery);
}
void NavDataCache::writeDoubleProperty(const string& key, const double& value)
{
- d->reset(d->writePropertyQuery);
+ sqlite_bind_stdstring(d->clearProperty, 1, key);
+ d->execUpdate(d->clearProperty);
+
sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
sqlite3_bind_double(d->writePropertyQuery, 2, value);
- d->execSelect(d->writePropertyQuery);
+ d->execUpdate(d->writePropertyQuery);
}
string_list NavDataCache::readStringListProperty(const string& key)
{
- d->reset(d->readPropertyQuery);
sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
string_list result;
while (d->stepSelect(d->readPropertyQuery)) {
result.push_back((char*) sqlite3_column_text(d->readPropertyQuery, 0));
}
+ d->reset(d->readPropertyQuery);
return result;
}
void NavDataCache::writeStringListProperty(const string& key, const string_list& values)
{
- d->reset(d->clearProperty);
sqlite_bind_stdstring(d->clearProperty, 1, key);
d->execUpdate(d->clearProperty);
BOOST_FOREACH(string value, values) {
- d->reset(d->writePropertyMulti);
sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
d->execInsert(d->writePropertyMulti);
bool NavDataCache::isCachedFileModified(const SGPath& path) const
{
- if (!path.exists()) {
- throw sg_io_exception("isCachedFileModified: Missing file:" + path.str());
- }
-
- d->reset(d->statCacheCheck);
- sqlite_bind_temp_stdstring(d->statCacheCheck, 1, path.str());
- if (d->execSelect(d->statCacheCheck)) {
- time_t modtime = sqlite3_column_int64(d->statCacheCheck, 0);
- return (modtime != path.modTime());
- } else {
- return true;
- }
+ return d->isCachedFileModified(path, false);
}
void NavDataCache::stampCacheFile(const SGPath& path)
{
- d->reset(d->stampFileCache);
sqlite_bind_temp_stdstring(d->stampFileCache, 1, path.str());
sqlite3_bind_int64(d->stampFileCache, 2, path.modTime());
d->execInsert(d->stampFileCache);
void NavDataCache::beginTransaction()
{
- d->runSQL("BEGIN");
+ if (d->transactionLevel == 0) {
+ d->transactionAborted = false;
+ d->stepSelect(d->beginTransactionStmt);
+ sqlite3_reset(d->beginTransactionStmt);
+ }
+
+ ++d->transactionLevel;
}
void NavDataCache::commitTransaction()
{
- d->runSQL("COMMIT");
+ assert(d->transactionLevel > 0);
+ if (--d->transactionLevel == 0) {
+ // if a nested transaction aborted, we might end up here, but must
+ // still abort the entire transaction. That's bad, but safer than
+ // committing.
+ sqlite3_stmt_ptr q = d->transactionAborted ? d->rollbackTransactionStmt : d->commitTransactionStmt;
+
+ int retries = 0;
+ int result;
+ while (retries < MAX_RETRIES) {
+ result = sqlite3_step(q);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ // see http://www.sqlite.org/c3ref/get_autocommit.html for a hint
+ // what's going on here: autocommit in inactive inside BEGIN, so if
+ // it's active, the DB was rolled-back
+ if (sqlite3_get_autocommit(d->db)) {
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "commit: was rolled back!" << retries);
+ d->transactionAborted = true;
+ break;
+ }
+
+ if (result != SQLITE_BUSY) {
+ break;
+ }
+
+ SGTimeStamp::sleepForMSec(++retries * 10);
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache contention on commit, will retry:" << retries);
+ } // of retry loop for DB busy
+
+ string errMsg;
+ if (result != SQLITE_DONE) {
+ errMsg = sqlite3_errmsg(d->db);
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg << " for " << result
+ << " while running:\n\t" << sqlite3_sql(q));
+ }
+
+ sqlite3_reset(q);
+ }
}
void NavDataCache::abortTransaction()
{
- d->runSQL("ROLLBACK");
+ SG_LOG(SG_NAVCACHE, SG_WARN, "NavCache: aborting transaction");
+
+ assert(d->transactionLevel > 0);
+ if (--d->transactionLevel == 0) {
+ d->stepSelect(d->rollbackTransactionStmt);
+ sqlite3_reset(d->rollbackTransactionStmt);
+ }
+
+ d->transactionAborted = true;
}
-FGPositioned* NavDataCache::loadById(PositionedID rowid)
+FGPositionedRef NavDataCache::loadById(PositionedID rowid)
{
if (rowid == 0) {
return NULL;
return it->second; // cache it
}
- d->reset(d->loadPositioned);
- sqlite3_bind_int64(d->loadPositioned, 1, rowid);
- FGPositioned* pos = d->loadFromStmt(d->loadPositioned);
-
+ sqlite3_int64 aptId;
+ FGPositioned* pos = d->loadById(rowid, aptId);
d->cache.insert(it, PositionedCache::value_type(rowid, pos));
d->cacheMisses++;
+
+ // when we loaded an ILS, we must apply per-airport changes
+ if ((pos->type() == FGPositioned::ILS) && (aptId > 0)) {
+ FGAirport* apt = FGPositioned::loadById<FGAirport>(aptId);
+ apt->validateILSData();
+ }
return pos;
}
0 /* airport */,
false /* spatial index */);
- d->reset(d->insertAirport);
sqlite3_bind_int64(d->insertAirport, 1, rowId);
d->execInsert(d->insertAirport);
SGVec3d cartPos(SGVec3d::fromGeod(pos));
- d->reset(d->setAirportPos);
sqlite3_bind_int(d->setAirportPos, 1, item);
sqlite3_bind_double(d->setAirportPos, 2, pos.getLongitudeDeg());
sqlite3_bind_double(d->setAirportPos, 3, pos.getLatitudeDeg());
{
// only runways are spatially indexed; don't bother indexing taxiways
// or pavements
- bool spatialIndex = (ty == FGPositioned::RUNWAY);
+ bool spatialIndex = ( ty == FGPositioned::RUNWAY || ty == FGPositioned::HELIPAD);
sqlite3_int64 rowId = d->insertPositioned(ty, cleanRunwayNo(ident), "", pos, apt,
spatialIndex);
- d->reset(d->insertRunway);
sqlite3_bind_int64(d->insertRunway, 1, rowId);
sqlite3_bind_double(d->insertRunway, 2, heading);
sqlite3_bind_double(d->insertRunway, 3, length);
void NavDataCache::setRunwayReciprocal(PositionedID runway, PositionedID recip)
{
- d->reset(d->setRunwayReciprocal);
sqlite3_bind_int64(d->setRunwayReciprocal, 1, runway);
sqlite3_bind_int64(d->setRunwayReciprocal, 2, recip);
d->execUpdate(d->setRunwayReciprocal);
// and the opposite direction too!
- d->reset(d->setRunwayReciprocal);
sqlite3_bind_int64(d->setRunwayReciprocal, 2, runway);
sqlite3_bind_int64(d->setRunwayReciprocal, 1, recip);
d->execUpdate(d->setRunwayReciprocal);
void NavDataCache::setRunwayILS(PositionedID runway, PositionedID ils)
{
- d->reset(d->setRunwayILS);
sqlite3_bind_int64(d->setRunwayILS, 1, runway);
sqlite3_bind_int64(d->setRunwayILS, 2, ils);
d->execUpdate(d->setRunwayILS);
-}
-
-void NavDataCache::updateRunwayThreshold(PositionedID runwayID, const SGGeod &aThreshold,
- double aHeading, double aDisplacedThreshold,
- double aStopway)
-{
-// update the runway information
- d->reset(d->updateRunwayThreshold);
- sqlite3_bind_int64(d->updateRunwayThreshold, 1, runwayID);
- sqlite3_bind_double(d->updateRunwayThreshold, 2, aHeading);
- sqlite3_bind_double(d->updateRunwayThreshold, 3, aDisplacedThreshold);
- sqlite3_bind_double(d->updateRunwayThreshold, 4, aStopway);
- d->execUpdate(d->updateRunwayThreshold);
-
-// compute the new runway center, based on the threshold lat/lon and length,
- double offsetFt = (0.5 * d->runwayLengthFt(runwayID));
- SGGeod newCenter;
- double dummy;
- SGGeodesy::direct(aThreshold, aHeading, offsetFt * SG_FEET_TO_METER, newCenter, dummy);
-// now update the positional data
- updatePosition(runwayID, newCenter);
+ // and the in-memory one
+ if (d->cache.find(runway) != d->cache.end()) {
+ FGRunway* instance = (FGRunway*) d->cache[runway].ptr();
+ instance->setILS(ils);
+ }
}
PositionedID
sqlite3_int64 rowId = d->insertPositioned(ty, ident, name, pos, apt,
spatialIndex);
- d->reset(d->insertNavaid);
sqlite3_bind_int64(d->insertNavaid, 1, rowId);
sqlite3_bind_int(d->insertNavaid, 2, freq);
sqlite3_bind_int(d->insertNavaid, 3, range);
sqlite3_bind_double(d->insertNavaid, 4, multiuse);
sqlite3_bind_int64(d->insertNavaid, 5, runway);
+ sqlite3_bind_int64(d->insertNavaid, 6, 0);
return d->execInsert(d->insertNavaid);
}
-void NavDataCache::updateILS(PositionedID ils, const SGGeod& newPos, double aHdg)
+void NavDataCache::setNavaidColocated(PositionedID navaid, PositionedID colocatedDME)
{
- d->reset(d->updateILS);
- sqlite3_bind_int64(d->updateILS, 1, ils);
- sqlite3_bind_double(d->updateILS, 2, aHdg);
- d->execUpdate(d->updateILS);
- updatePosition(ils, newPos);
+ // Update DB entries...
+ sqlite3_bind_int64(d->setNavaidColocated, 1, navaid);
+ sqlite3_bind_int64(d->setNavaidColocated, 2, colocatedDME);
+ d->execUpdate(d->setNavaidColocated);
+
+ // ...and the in-memory copy of the navrecord
+ if (d->cache.find(navaid) != d->cache.end()) {
+ FGNavRecord* rec = (FGNavRecord*) d->cache[navaid].get();
+ rec->setColocatedDME(colocatedDME);
+ }
}
PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty,
PositionedID apt)
{
sqlite3_int64 rowId = d->insertPositioned(ty, "", name, pos, apt, true);
- d->reset(d->insertCommStation);
sqlite3_bind_int64(d->insertCommStation, 1, rowId);
sqlite3_bind_int(d->insertCommStation, 2, freq);
sqlite3_bind_int(d->insertCommStation, 3, range);
return d->insertPositioned(FGPositioned::FIX, ident, string(), aPos, 0, true);
}
-PositionedID NavDataCache::createUserWaypoint(const std::string& ident, const SGGeod& aPos)
+PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos)
{
- return d->insertPositioned(FGPositioned::WAYPOINT, ident, string(), aPos, 0,
+ return d->insertPositioned(ty, ident, string(), aPos, 0,
true /* spatial index */);
}
+
+bool NavDataCache::removePOI(FGPositioned::Type ty, const std::string& aIdent)
+{
+ d->removePositionedWithIdent(ty, aIdent);
+ // should remove from the live cache too?
+
+ return true;
+}
void NavDataCache::setAirportMetar(const string& icao, bool hasMetar)
{
- d->reset(d->setAirportMetar);
sqlite_bind_stdstring(d->setAirportMetar, 1, icao);
sqlite3_bind_int(d->setAirportMetar, 2, hasMetar);
d->execUpdate(d->setAirportMetar);
}
-FGPositioned::List NavDataCache::findAllWithIdent(const string& s,
- FGPositioned::Filter* filter, bool exact)
+//------------------------------------------------------------------------------
+FGPositionedList NavDataCache::findAllWithIdent( const string& s,
+ FGPositioned::Filter* filter,
+ bool exact )
{
return d->findAllByString(s, "ident", filter, exact);
}
-FGPositioned::List NavDataCache::findAllWithName(const string& s,
- FGPositioned::Filter* filter, bool exact)
+//------------------------------------------------------------------------------
+FGPositionedList NavDataCache::findAllWithName( const string& s,
+ FGPositioned::Filter* filter,
+ bool exact )
{
return d->findAllByString(s, "name", filter, exact);
}
-
-FGPositionedRef NavDataCache::findClosestWithIdent(const string& aIdent,
- const SGGeod& aPos, FGPositioned::Filter* aFilter)
+
+//------------------------------------------------------------------------------
+FGPositionedRef NavDataCache::findClosestWithIdent( const string& aIdent,
+ const SGGeod& aPos,
+ FGPositioned::Filter* aFilter )
{
- d->reset(d->findClosestWithIdent);
sqlite_bind_stdstring(d->findClosestWithIdent, 1, aIdent);
if (aFilter) {
sqlite3_bind_int(d->findClosestWithIdent, 2, aFilter->minType());
sqlite3_bind_double(d->findClosestWithIdent, 5, cartPos.y());
sqlite3_bind_double(d->findClosestWithIdent, 6, cartPos.z());
+ FGPositionedRef result;
+
while (d->stepSelect(d->findClosestWithIdent)) {
FGPositioned* pos = loadById(sqlite3_column_int64(d->findClosestWithIdent, 0));
if (aFilter && !aFilter->pass(pos)) {
continue;
}
- return pos;
+ result = pos;
+ break;
}
- return NULL; // no matches at all
+ d->reset(d->findClosestWithIdent);
+ return result;
}
int NavDataCache::getOctreeBranchChildren(int64_t octreeNodeId)
{
- d->reset(d->getOctreeChildren);
sqlite3_bind_int64(d->getOctreeChildren, 1, octreeNodeId);
d->execSelect1(d->getOctreeChildren);
- return sqlite3_column_int(d->getOctreeChildren, 0);
+ int children = sqlite3_column_int(d->getOctreeChildren, 0);
+ d->reset(d->getOctreeChildren);
+ return children;
}
void NavDataCache::defineOctreeNode(Octree::Branch* pr, Octree::Node* nd)
{
- d->reset(d->insertOctree);
sqlite3_bind_int64(d->insertOctree, 1, nd->guid());
d->execInsert(d->insertOctree);
// lowest three bits of node ID are 0..7 index of the child in the parent
int childIndex = nd->guid() & 0x07;
- d->reset(d->updateOctreeChildren);
sqlite3_bind_int64(d->updateOctreeChildren, 1, pr->guid());
// mask has bit N set where child N exists
int childMask = 1 << childIndex;
TypedPositionedVec
NavDataCache::getOctreeLeafChildren(int64_t octreeNodeId)
{
- d->reset(d->getOctreeLeafChildren);
sqlite3_bind_int64(d->getOctreeLeafChildren, 1, octreeNodeId);
TypedPositionedVec r;
sqlite3_column_int64(d->getOctreeLeafChildren, 0)));
}
+ d->reset(d->getOctreeLeafChildren);
return r;
}
*/
char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter)
{
- d->reset(d->searchAirports);
- string s = "%" + aFilter + "%";
- sqlite_bind_stdstring(d->searchAirports, 1, s);
-
+ sqlite3_stmt_ptr stmt;
unsigned int numMatches = 0, numAllocated = 16;
- char** result = (char**) malloc(sizeof(char*) * numAllocated);
+ if (aFilter.empty()) {
+ stmt = d->getAllAirports;
+ numAllocated = 4096; // start much larger for all airports
+ } else {
+ stmt = d->searchAirports;
+ string s = "%" + aFilter + "%";
+ sqlite_bind_stdstring(stmt, 1, s);
+ }
- while (d->stepSelect(d->searchAirports)) {
+ char** result = (char**) malloc(sizeof(char*) * numAllocated);
+ while (d->stepSelect(stmt)) {
if ((numMatches + 1) >= numAllocated) {
numAllocated <<= 1; // double in size!
// reallocate results array
// which gives a grand total of 7 + name-length + icao-length.
// note the ident can be three letters (non-ICAO local strip), four
// (default ICAO) or more (extended format ICAO)
- int nameLength = sqlite3_column_bytes(d->searchAirports, 1);
- int icaoLength = sqlite3_column_bytes(d->searchAirports, 0);
+ int nameLength = sqlite3_column_bytes(stmt, 1);
+ int icaoLength = sqlite3_column_bytes(stmt, 0);
char* entry = (char*) malloc(7 + nameLength + icaoLength);
char* dst = entry;
*dst++ = ' ';
- memcpy(dst, sqlite3_column_text(d->searchAirports, 1), nameLength);
+ memcpy(dst, sqlite3_column_text(stmt, 1), nameLength);
dst += nameLength;
*dst++ = ' ';
*dst++ = ' ';
*dst++ = ' ';
*dst++ = '(';
- memcpy(dst, sqlite3_column_text(d->searchAirports, 0), icaoLength);
+ memcpy(dst, sqlite3_column_text(stmt, 0), icaoLength);
dst += icaoLength;
*dst++ = ')';
*dst++ = 0;
}
result[numMatches] = NULL; // end of list marker
+ d->reset(stmt);
return result;
}
FGPositionedRef
NavDataCache::findCommByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::Filter* aFilter)
{
- d->reset(d->findCommByFreq);
sqlite3_bind_int(d->findCommByFreq, 1, freqKhz);
if (aFilter) {
sqlite3_bind_int(d->findCommByFreq, 2, aFilter->minType());
sqlite3_bind_double(d->findCommByFreq, 4, cartPos.x());
sqlite3_bind_double(d->findCommByFreq, 5, cartPos.y());
sqlite3_bind_double(d->findCommByFreq, 6, cartPos.z());
+ FGPositionedRef result;
while (d->execSelect(d->findCommByFreq)) {
FGPositioned* p = loadById(sqlite3_column_int64(d->findCommByFreq, 0));
continue;
}
- return p;
+ result = p;
+ break;
}
- return NULL;
+ d->reset(d->findCommByFreq);
+ return result;
}
PositionedIDVec
NavDataCache::findNavaidsByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::Filter* aFilter)
{
- d->reset(d->findNavsByFreq);
sqlite3_bind_int(d->findNavsByFreq, 1, freqKhz);
if (aFilter) {
sqlite3_bind_int(d->findNavsByFreq, 2, aFilter->minType());
PositionedIDVec
NavDataCache::findNavaidsByFreq(int freqKhz, FGPositioned::Filter* aFilter)
{
- d->reset(d->findNavsByFreqNoPos);
sqlite3_bind_int(d->findNavsByFreqNoPos, 1, freqKhz);
if (aFilter) {
sqlite3_bind_int(d->findNavsByFreqNoPos, 2, aFilter->minType());
maxTy = ty; // single-type range
}
- d->reset(d->getAirportItems);
sqlite3_bind_int64(d->getAirportItems, 1, apt);
sqlite3_bind_int(d->getAirportItems, 2, ty);
sqlite3_bind_int(d->getAirportItems, 3, maxTy);
NavDataCache::airportItemWithIdent(PositionedID apt, FGPositioned::Type ty,
const std::string& ident)
{
- d->reset(d->getAirportItemByIdent);
sqlite3_bind_int64(d->getAirportItemByIdent, 1, apt);
sqlite_bind_stdstring(d->getAirportItemByIdent, 2, ident);
sqlite3_bind_int(d->getAirportItemByIdent, 3, ty);
+ PositionedID result = 0;
- if (!d->execSelect(d->getAirportItemByIdent)) {
- return 0;
+ if (d->execSelect(d->getAirportItemByIdent)) {
+ result = sqlite3_column_int64(d->getAirportItemByIdent, 0);
}
- return sqlite3_column_int64(d->getAirportItemByIdent, 0);
+ d->reset(d->getAirportItemByIdent);
+ return result;
}
AirportRunwayPair
return AirportRunwayPair();
}
- d->reset(d->findAirportRunway);
+ AirportRunwayPair result;
sqlite_bind_stdstring(d->findAirportRunway, 1, parts[0]);
- sqlite_bind_stdstring(d->findAirportRunway, 2, parts[1]);
- if (!d->execSelect(d->findAirportRunway)) {
+ sqlite_bind_stdstring(d->findAirportRunway, 2, cleanRunwayNo(parts[1]));
+
+ if (d->execSelect(d->findAirportRunway)) {
+ result = AirportRunwayPair(sqlite3_column_int64(d->findAirportRunway, 0),
+ sqlite3_column_int64(d->findAirportRunway, 1));
+
+ } else {
SG_LOG(SG_NAVCACHE, SG_WARN, "findAirportRunway: unknown airport/runway:" << aName);
- return AirportRunwayPair();
}
- // success, extract the IDs and continue
- return AirportRunwayPair(sqlite3_column_int64(d->findAirportRunway, 0),
- sqlite3_column_int64(d->findAirportRunway, 1));
+ d->reset(d->findAirportRunway);
+ return result;
}
PositionedID
-NavDataCache::findILS(PositionedID airport, const string& runway, const string& navIdent)
+NavDataCache::findILS(PositionedID airport, const string& aRunway, const string& navIdent)
{
- d->reset(d->findILS);
+ string runway(cleanRunwayNo(aRunway));
+
sqlite_bind_stdstring(d->findILS, 1, navIdent);
sqlite3_bind_int64(d->findILS, 2, airport);
sqlite_bind_stdstring(d->findILS, 3, runway);
-
- if (!d->execSelect(d->findILS)) {
- return 0;
+ PositionedID result = 0;
+ if (d->execSelect(d->findILS)) {
+ result = sqlite3_column_int64(d->findILS, 0);
}
- return sqlite3_column_int64(d->findILS, 0);
+ d->reset(d->findILS);
+ return result;
}
int NavDataCache::findAirway(int network, const string& aName)
{
- d->reset(d->findAirway);
sqlite3_bind_int(d->findAirway, 1, network);
sqlite_bind_stdstring(d->findAirway, 2, aName);
+
+ int airway = 0;
if (d->execSelect(d->findAirway)) {
// already exists
- return sqlite3_column_int(d->findAirway, 0);
+ airway = sqlite3_column_int(d->findAirway, 0);
+ } else {
+ sqlite_bind_stdstring(d->insertAirway, 1, aName);
+ sqlite3_bind_int(d->insertAirway, 2, network);
+ airway = d->execInsert(d->insertAirway);
}
- d->reset(d->insertAirway);
- sqlite_bind_stdstring(d->insertAirway, 1, aName);
- sqlite3_bind_int(d->insertAirway, 2, network);
- return d->execInsert(d->insertAirway);
+ d->reset(d->findAirway);
+ return airway;
}
void NavDataCache::insertEdge(int network, int airwayID, PositionedID from, PositionedID to)
{
// assume all edges are bidirectional for the moment
for (int i=0; i<2; ++i) {
- d->reset(d->insertAirwayEdge);
sqlite3_bind_int(d->insertAirwayEdge, 1, network);
sqlite3_bind_int(d->insertAirwayEdge, 2, airwayID);
sqlite3_bind_int64(d->insertAirwayEdge, 3, from);
bool NavDataCache::isInAirwayNetwork(int network, PositionedID pos)
{
- d->reset(d->isPosInAirway);
sqlite3_bind_int(d->isPosInAirway, 1, network);
sqlite3_bind_int64(d->isPosInAirway, 2, pos);
bool ok = d->execSelect(d->isPosInAirway);
+ d->reset(d->isPosInAirway);
+
return ok;
}
AirwayEdgeVec NavDataCache::airwayEdgesFrom(int network, PositionedID pos)
{
- d->reset(d->airwayEdgesFrom);
sqlite3_bind_int(d->airwayEdgesFrom, 1, network);
sqlite3_bind_int64(d->airwayEdgesFrom, 2, pos);
sqlite3_column_int64(d->airwayEdgesFrom, 1)
));
}
+
+ d->reset(d->airwayEdgesFrom);
return result;
}
PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
{
- d->reset(d->findNavaidForRunway);
sqlite3_bind_int64(d->findNavaidForRunway, 1, runway);
sqlite3_bind_int(d->findNavaidForRunway, 2, ty);
- if (!d->execSelect(d->findNavaidForRunway)) {
- return 0;
+
+ PositionedID result = 0;
+ if (d->execSelect(d->findNavaidForRunway)) {
+ result = sqlite3_column_int64(d->findNavaidForRunway, 0);
}
- return sqlite3_column_int64(d->findNavaidForRunway, 0);
+ d->reset(d->findNavaidForRunway);
+ return result;
}
PositionedID
// we need to insert a row into the taxi_node table, otherwise we can't maintain
// the appropriate pushback flag.
- d->reset(d->insertTaxiNode);
sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
sqlite3_bind_int(d->insertTaxiNode, 2, 0);
sqlite3_bind_int(d->insertTaxiNode, 3, 0);
d->execInsert(d->insertTaxiNode);
- d->reset(d->insertParkingPos);
sqlite3_bind_int64(d->insertParkingPos, 1, rowId);
sqlite3_bind_double(d->insertParkingPos, 2, aHeading);
sqlite3_bind_int(d->insertParkingPos, 3, aRadius);
void NavDataCache::setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode)
{
- d->reset(d->setParkingPushBack);
sqlite3_bind_int64(d->setParkingPushBack, 1, parking);
sqlite3_bind_int64(d->setParkingPushBack, 2, pushBackNode);
d->execUpdate(d->setParkingPushBack);
NavDataCache::insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway)
{
sqlite3_int64 rowId = d->insertPositioned(FGPositioned::TAXI_NODE, string(), string(), aPos, aAirport, false);
- d->reset(d->insertTaxiNode);
sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
sqlite3_bind_int(d->insertTaxiNode, 2, aHoldType);
sqlite3_bind_int(d->insertTaxiNode, 3, aOnRunway);
void NavDataCache::insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to)
{
- d->reset(d->insertTaxiEdge);
sqlite3_bind_int64(d->insertTaxiEdge, 1, aAirport);
sqlite3_bind_int64(d->insertTaxiEdge, 2, from);
sqlite3_bind_int64(d->insertTaxiEdge, 3, to);
PositionedIDVec NavDataCache::groundNetNodes(PositionedID aAirport, bool onlyPushback)
{
sqlite3_stmt_ptr q = onlyPushback ? d->airportPushbackNodes : d->airportTaxiNodes;
- d->reset(q);
sqlite3_bind_int64(q, 1, aAirport);
return d->selectIds(q);
}
void NavDataCache::markGroundnetAsPushback(PositionedID nodeId)
{
- d->reset(d->markTaxiNodeAsPushback);
sqlite3_bind_int64(d->markTaxiNodeAsPushback, 1, nodeId);
d->execUpdate(d->markTaxiNodeAsPushback);
}
bool onRunway, FGRunway* aRunway)
{
sqlite3_stmt_ptr q = onRunway ? d->findNearestRunwayTaxiNode : d->findNearestTaxiNode;
- d->reset(q);
sqlite3_bind_int64(q, 1, airport);
SGVec3d cartPos(SGVec3d::fromGeod(aPos));
sqlite3_bind_double(q, 3, cartPos.y());
sqlite3_bind_double(q, 4, cartPos.z());
+ PositionedID result = 0;
while (d->execSelect(q)) {
PositionedID id = sqlite3_column_int64(q, 0);
if (!aRunway) {
- return id;
+ result = id;
+ break;
}
// ensure found node lies on the runway
FGPositionedRef node = loadById(id);
double course = SGGeodesy::courseDeg(node->geod(), aRunway->end());
if (fabs(headingDifferenceDeg(course, aRunway->headingDeg())) < 3.0 ) {
- return id;
+ result = id;
+ break;
}
}
- return 0;
+ d->reset(q);
+ return result;
}
PositionedIDVec NavDataCache::groundNetEdgesFrom(PositionedID pos, bool onlyPushback)
{
sqlite3_stmt_ptr q = onlyPushback ? d->pushbackEdgesFrom : d->taxiEdgesFrom;
- d->reset(q);
sqlite3_bind_int64(q, 1, pos);
return d->selectIds(q);
}
PositionedIDVec NavDataCache::findAirportParking(PositionedID airport, const std::string& flightType,
int radius)
{
- d->reset(d->findAirportParking);
sqlite3_bind_int64(d->findAirportParking, 1, airport);
sqlite3_bind_int(d->findAirportParking, 2, radius);
sqlite_bind_stdstring(d->findAirportParking, 3, flightType);
return d->selectIds(d->findAirportParking);
}
+void NavDataCache::dropGroundnetFor(PositionedID aAirport)
+{
+ sqlite3_stmt_ptr q = d->prepare("DELETE FROM parking WHERE rowid IN (SELECT rowid FROM positioned WHERE type=?1 AND airport=?2)");
+ sqlite3_bind_int(q, 1, FGPositioned::PARKING);
+ sqlite3_bind_int64(q, 2, aAirport);
+ d->execUpdate(q);
+
+ q = d->prepare("DELETE FROM taxi_node WHERE rowid IN (SELECT rowid FROM positioned WHERE (type=?1 OR type=?2) AND airport=?3)");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ sqlite3_bind_int64(q, 3, aAirport);
+ d->execUpdate(q);
+
+ q = d->prepare("DELETE FROM positioned WHERE (type=?1 OR type=?2) AND airport=?3");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ sqlite3_bind_int64(q, 3, aAirport);
+ d->execUpdate(q);
+
+ q = d->prepare("DELETE FROM groundnet_edge WHERE airport=?1");
+ sqlite3_bind_int64(q, 1, aAirport);
+ d->execUpdate(q);
+}
+
+void NavDataCache::dropAllGroundnets()
+{
+ SG_LOG(SG_NAVCACHE, SG_INFO, "dropping ground-net data");
+ beginTransaction();
+ d->runSQL("DELETE FROM groundnet_edge");
+ d->runSQL("DELETE FROM parking");
+ d->runSQL("DELETE FROM taxi_node");
+
+ sqlite3_stmt_ptr q = d->prepare("DELETE FROM positioned WHERE (type=?1 OR type=?2)");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ d->execUpdate(q);
+ commitTransaction();
+}
+
+bool NavDataCache::isReadOnly() const
+{
+ return d->readOnly;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Transaction RAII object
+
+NavDataCache::Transaction::Transaction(NavDataCache* cache) :
+ _instance(cache),
+ _committed(false)
+{
+ assert(cache);
+ _instance->beginTransaction();
+}
+
+NavDataCache::Transaction::~Transaction()
+{
+ if (!_committed) {
+ SG_LOG(SG_NAVCACHE, SG_INFO, "aborting cache transaction!");
+ _instance->abortTransaction();
+ }
+}
+
+void NavDataCache::Transaction::commit()
+{
+ assert(!_committed);
+ _committed = true;
+ _instance->commitTransaction();
+}
+
} // of namespace flightgear