using std::string;
-#define SG_NAVCACHE SG_GENERAL
+#define SG_NAVCACHE SG_NAVAID
//#define LAZY_OCTREE_UPDATES 1
namespace {
-const int SCHEMA_VERSION = 5;
+const int SCHEMA_VERSION = 6;
// 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
SGTimeStamp st;
st.stamp();
_cache->doRebuild();
- SG_LOG(SG_GENERAL, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec");
+ SG_LOG(SG_NAVCACHE, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec");
SGGuard<SGMutex> g(_lock);
_isFinished = 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);
return stepSelect(stmt);
}
+ static const int MAX_RETRIES = 10;
+
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_LOCKED) {
+ break;
+ }
+
+ ++retries;
+ } // 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_LOCKED retries");
+ return false;
}
string errMsg;
void prepareQueries()
{
- clearProperty = prepare("DELETE FROM properties WHERE key=?1");
writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
#define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node"
void writeIntProperty(const string& key, int value)
{
+ reset(clearProperty);
+ sqlite_bind_stdstring(clearProperty, 1, key);
+ execUpdate(clearProperty);
+
sqlite_bind_stdstring(writePropertyQuery, 1, key);
sqlite3_bind_int(writePropertyQuery, 2, value);
execSelect(writePropertyQuery);
isCachedFileModified(d->fixDatPath) ||
isCachedFileModified(d->airwayDatPath))
{
- 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, main cache rebuild required");
+ return true;
+ }
+
+ SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: no main cache rebuild required");
return false;
}
d->runSQL("DELETE FROM octree");
d->runSQL("DELETE FROM airway");
d->runSQL("DELETE FROM airway_edge");
+ d->runSQL("DELETE FROM taxi_node");
+ d->runSQL("DELETE FROM parking");
+ d->runSQL("DELETE FROM groundnet_edge");
+ d->runSQL("DELETE FROM stat_cache");
// initialise the root octree node
d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)");
d->flushDeferredOctreeUpdates();
+ string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";");
+ writeStringProperty("scenery_paths", sceneryPaths);
+
d->runSQL("COMMIT");
} catch (sg_exception& e) {
SG_LOG(SG_NAVCACHE, SG_ALERT, "caught exception rebuilding navCache:" << e.what());
void NavDataCache::writeStringProperty(const string& key, const string& value)
{
+ d->reset(d->clearProperty);
+ sqlite_bind_stdstring(d->clearProperty, 1, key);
+ d->execUpdate(d->clearProperty);
+
d->reset(d->writePropertyQuery);
sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
sqlite_bind_stdstring(d->writePropertyQuery, 2, value);
void NavDataCache::writeDoubleProperty(const string& key, const double& value)
{
+ d->reset(d->clearProperty);
+ sqlite_bind_stdstring(d->clearProperty, 1, key);
+ d->execUpdate(d->clearProperty);
+
d->reset(d->writePropertyQuery);
sqlite_bind_stdstring(d->writePropertyQuery, 1, key);
sqlite3_bind_double(d->writePropertyQuery, 2, value);
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());
+ time_t delta = std::labs(modtime - path.modTime());
+ if (delta != 0)
+ {
+ SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: rebuild required for " << path << ". Timestamps: " << modtime << " != " << path.modTime());
+ if (delta < 30)
+ {
+ // File system time stamp has slightly changed. It's unlikely that the user has managed to change a file, start fgfs,
+ // and then changed file again within x seconds - so it's suspicious...
+ SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache: suspicious file timestamp change. Please report this to FlightGear. "
+ << "Delta: " << delta << ", Timestamps: " << modtime << ", " << path.modTime() << ", path: " << path);
+ }
+ }
+ else
+ {
+ SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: no rebuild required for " << path);
+ }
+ return (delta != 0);
} else {
+ SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: initial build required for " << path);
return true;
}
}
void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
{
if (d->cache.find(item) != d->cache.end()) {
- SG_LOG(SG_GENERAL, SG_DEBUG, "updating position of an item in the cache");
+ SG_LOG(SG_NAVCACHE, SG_DEBUG, "updating position of an item in the cache");
d->cache[item]->modifyPosition(pos);
}
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 AND airport=?2)");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int64(q, 2, 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);
+}
+
} // of namespace flightgear