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
}
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);
return true;
}
+ 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->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);
- bool modified = (modtime != path.modTime());
- if (modified)
+ 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 (modtime != path.modTime());
+ return (delta != 0);
} else {
SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: initial build required for " << path);
return true;