From 83ec65edf51c2aadafed46b3162527f6a897885f Mon Sep 17 00:00:00 2001 From: Wouter den Breejen Date: Fri, 27 Jul 2007 16:22:53 +0000 Subject: [PATCH] --- src/libstore/build.cc | 6 +- src/libstore/db.cc | 143 ++++++++++++++++++++++------------- src/libstore/db.hh | 25 +++--- src/libstore/local-store.cc | 142 ++++++++++++++++++---------------- src/libstore/local-store.hh | 11 ++- src/libstore/remote-store.cc | 4 +- src/libstore/remote-store.hh | 4 +- src/libstore/store-api.hh | 5 +- src/libutil/types.hh | 3 +- src/nix-state/nix-state.cc | 98 +++++++++++++++--------- 10 files changed, 262 insertions(+), 179 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index a4953b948..a0924d751 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1794,7 +1794,7 @@ void DerivationGoal::computeClosure() contentHashes[i->second.path], allReferences[i->second.path], //set of component-references allStateReferences[i->second.path], //set of state-references - drvPath, 0); + drvPath, -1); } //Register the state path valid @@ -1807,14 +1807,14 @@ void DerivationGoal::computeClosure() Hash(), //emtpy hash state_references, state_stateReferences, - drvPath, 0); + drvPath, -1); //Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO) RevisionClosure rivisionMapping; rivisionMapping[statePath] = commitStatePathTxn(txn, statePath); //Save the new revision - setStateRevisionsTxn(txn, statePath, rivisionMapping); + setStateRevisionsTxn(txn, rivisionMapping); //Shared state Path sharedState = drv.stateOutputs.find("state")->second.sharedState; diff --git a/src/libstore/db.cc b/src/libstore/db.cc index c4e4b07c9..54e4d1850 100644 --- a/src/libstore/db.cc +++ b/src/libstore/db.cc @@ -451,13 +451,13 @@ void Database::enumTable(const Transaction & txn, TableId table, Path Database::makeStatePathRevision(const Path & statePath, const int revision) { - string prefix = "-REV-"; + string prefix = "-KEY-"; return statePath + prefix + int2String(revision); } void Database::splitStatePathRevision(const Path & revisionedStatePath, Path & statePath, int & revision) { - string prefix = "-REV-"; + string prefix = "-KEY-"; int pos = revisionedStatePath.find_last_of(prefix); statePath = revisionedStatePath.substr(0, pos - prefix.length() + 1); @@ -515,95 +515,128 @@ bool Database::lookupHighestRevivison(const Strings & keys, const Path & statePa } } - if(highestRev == -1) //no records found (TODO throw error?) + if(highestRev == -1) //no records found return false; key = makeStatePathRevision(statePath, highestRev); //final key that matches revision + statePath return true; } -////////////////////////////////////////////// +bool Database::revisionToTimeStamp(const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, int & timestamp) +{ + string key = makeStatePathRevision(statePath, revision); + Strings references; + bool notempty = queryStrings(txn, revisions_table, key, references); + + if(notempty){ + Path empty; + splitStatePathRevision(*(references.begin()), empty, timestamp); //extract the timestamp + //printMsg(lvlError, format("PRINT '%1%'") % timestamp); + return true; + } + else + return false; +} -void Database::setStateReferences(const Transaction & txn, TableId table, +void Database::setStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, const Path & statePath, const Strings & references, int revision) { //printMsg(lvlError, format("setStateReferences/Referrers %1%") % table); + int timestamp; if(revision == -1) - revision = getNewRevisionNumber(txn, table, statePath); + timestamp = getTimeStamp(); + else{ + bool found = revisionToTimeStamp(txn, revisions_table, statePath, revision, timestamp); + if(!found) + throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision); + } - /* - for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) - printMsg(lvlError, format("setStateReferences::: '%1%'") % *i); - */ + //for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) + // printMsg(lvlError, format("setStateReferences::: '%1%'") % *i); //Warning if it already exists Strings empty; - if( queryStateReferences(txn, table, statePath, empty, revision) ) - printMsg(lvlError, format("Warning: The revision '%1%' already exists for set-references/referrers of path '%2%' with db '%3%'") % revision % statePath % table); + if( queryStateReferences(txn, references_table, revisions_table, statePath, empty, -1, timestamp) ) + printMsg(lvlError, format("Warning: The timestamp '%1%' already exists for set-references/referrers of path '%2%' with db '%3%'") % timestamp % statePath % references_table); //Create the key - string key = makeStatePathRevision(statePath, revision); + string key = makeStatePathRevision(statePath, timestamp); //Insert - setStrings(txn, table, key, references); + setStrings(txn, references_table, key, references); } -bool Database::queryStateReferences(const Transaction & txn, TableId table, - const Path & statePath, Strings & references, int revision) +bool Database::queryStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, Strings & references, int revision, int timestamp) { - //printMsg(lvlError, format("queryStateReferences/Referrers %1%") % table); + //printMsg(lvlError, format("queryStateReferences/Referrers '%1%' with revision '%2%'") % references_table % revision); + + //Convert revision to timestamp number useing the revisions_table + if(timestamp == -1 && revision != -1){ + bool found = revisionToTimeStamp(txn, revisions_table, statePath, revision, timestamp); + if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false; + return false; + } Strings keys; - enumTable(txn, table, keys); //get all revisions - - //Check if this revision exists key in the table, if it doesnt well find the highest key lower than it - string key = makeStatePathRevision(statePath, revision); - bool found = false; - for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { - if(key == *i) - found = true; - } - - key = ""; - if(revision == -1){ + enumTable(txn, references_table, keys); + + //Mabye we need the latest timestamp? + string key = ""; + if(timestamp == -1){ bool foundsomething = lookupHighestRevivison(keys, statePath, key); if(!foundsomething) return false; + else + return queryStrings(txn, references_table, key, references); } - else if(!found){ + + //If a specific key is given: check if this timestamp exists key in the table + key = makeStatePathRevision(statePath, timestamp); + bool found = false; + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { + if(key == *i){ + found = true; + key = makeStatePathRevision(statePath, timestamp); + } + } + + //If it doesn't exist in the table then find the highest key lower than it + if(!found){ bool foundsomething = lookupHighestRevivison(keys, statePath, key, -1); if(!foundsomething) return false; - //printMsg(lvlError, format("Warning: References for revision '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % revision % statePath); + //printMsg(lvlError, format("Warning: References for timestamp '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % timestamp % statePath); } - else - key = makeStatePathRevision(statePath, revision); - - return queryStrings(txn, table, key, references); //now that we have the key, we can query the references + return queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references } -bool Database::queryStateReferrers(const Transaction & txn, TableId table, - const Path & statePath, Strings & referrers, int revision) +void Database::setStateReferrers(const Transaction & txn, TableId referrers_table, TableId revisions_table, + const Path & statePath, const Strings & referrers, int revision) +{ + //Exactly the same as the setStateReferences + setStateReferences(txn, referrers_table, revisions_table, statePath, referrers, revision); +} + +bool Database::queryStateReferrers(const Transaction & txn, TableId referrers_table, TableId revisions_table, + const Path & statePath, Strings & referrers, int revision, int timestamp) { //Exactly the same as queryStateReferences - return queryStateReferences(txn, table, statePath, referrers, revision); + return queryStateReferences(txn, referrers_table, revisions_table, statePath, referrers, revision, timestamp); } -///////////////////////////////////////////// - - void Database::setStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & root_statePath, const RevisionClosure & revisions) + const RevisionClosure & revisions) { int ts = getTimeStamp(); - //Insert all ss_epochs into table ...... with the current ts. + //Insert all ss_epochs into snapshots_table with the current ts. for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ string key = makeStatePathRevision((*i).first, ts); Strings data; - //the map<> takes care of the sorting on + //the map<> takes care of the sorting on the Path for (Snapshots::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) data.push_back(int2String((*j).second)); setStrings(txn, snapshots_table, key, data); @@ -612,24 +645,25 @@ void Database::setStateRevisions(const Transaction & txn, TableId revisions_tabl //Insert for each statePath a new revision record linked to the ss_epochs for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){ Path statePath = (*i).first; + int revision = getNewRevisionNumber(txn, revisions_table, statePath); //get a new revision number string key = makeStatePathRevision(statePath, revision); //get all its requisites - PathSet statePaths; - storePathRequisitesTxn(txn, statePath, false, statePaths, false, true, -1); - statePaths.insert(statePath); + PathSet statePath_references; + storePathRequisitesTxn(txn, statePath, false, statePath_references, false, true, -1); + statePath_references.insert(statePath); //save in db Strings data; - for (PathSet::const_iterator j = statePaths.begin(); j != statePaths.end(); ++j) + for (PathSet::const_iterator j = statePath_references.begin(); j != statePath_references.end(); ++j) data.push_back(makeStatePathRevision(*j, ts)); setStrings(txn, revisions_table, key, data); } } bool Database::queryStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & statePath, RevisionClosure & revisions, int root_revision) + const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, int root_revision) { string key; @@ -643,15 +677,15 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId revisions_ta else key = makeStatePathRevision(statePath, root_revision); - //Get references pointingg to snapshots_table from revisions_table with root_revision - Strings references; - bool notempty = queryStrings(txn, revisions_table, key, references); + //Get references pointing to snapshots_table from revisions_table with root_revision + Strings statePaths; + bool notempty = queryStrings(txn, revisions_table, key, statePaths); if(!notempty) throw Error(format("Root revision '%1%' not found of statePath '%2%'") % int2String(root_revision) % statePath); - // - for (Strings::iterator i = references.begin(); i != references.end(); ++i){ + //For each statePath add the revisions + for (Strings::iterator i = statePaths.begin(); i != statePaths.end(); ++i){ Path getStatePath; int getTimestamp; @@ -687,6 +721,7 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId revisions_ta } revisions[getStatePath] = snapshots; + timestamps[getStatePath] = getTimestamp; } diff --git a/src/libstore/db.hh b/src/libstore/db.hh index 5f8e9274b..89d659036 100644 --- a/src/libstore/db.hh +++ b/src/libstore/db.hh @@ -66,6 +66,9 @@ private: /* TODO */ int getNewRevisionNumber(const Transaction & txn, TableId table, const Path & statePath); + /* */ + bool revisionToTimeStamp(const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, int & timestamp); + public: Database(); ~Database(); @@ -103,24 +106,28 @@ public: void splitStatePathRevision(const Path & revisionedStatePath, Path & statePath, int & revision); /* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */ - void setStateReferences(const Transaction & txn, TableId table, + void setStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, const Path & statePath, const Strings & references, int revision = -1); /* Returns the references for a specific revision (and older until the next higher revision number in the table) */ - bool queryStateReferences(const Transaction & txn, TableId table, - const Path & statePath, Strings & references, int revision = -1); - - /* Returns the referrers for a specific revision (and older until the next higher revision number in the table) */ - bool queryStateReferrers(const Transaction & txn, TableId table, - const Path & statePath, Strings & referrers, int revision = -1); + bool queryStateReferences(const Transaction & txn, TableId references_table, TableId revisions_table, + const Path & statePath, Strings & references, int revision = -1, int timestamp = -1); + + /* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */ + void setStateReferrers(const Transaction & txn, TableId referrers_table, TableId revisions_table, + const Path & statePath, const Strings & referrers, int revision = -1); + /* Returns the referrers for a specific revision (and older until the next higher revision number in the table) */ + bool queryStateReferrers(const Transaction & txn, TableId referrers_table, TableId revisions_table, + const Path & statePath, Strings & referrers, int revision = -1, int timestamp = -1); + /* Set the revision number of the statePath and the revision numbers of all state paths in the references closure */ void setStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & statePath, const RevisionClosure & revisions); + const RevisionClosure & revisions); /* Returns all the revision numbers of the state references closure of the given state path */ bool queryStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const Path & statePath, RevisionClosure & revisions, int root_revision = -1); + const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, int root_revision = -1); /* Returns all available revision numbers of the given state path */ bool queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index de6a0bc8b..d36806df1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -434,53 +434,84 @@ void setReferences(const Transaction & txn, const Path & store_or_statePath, printMsg(lvlError, format("'%2%' has stateReferences: %1%") % *i % store_or_statePath); */ - static TableId dbXComponentReferrers; - static TableId dbXStateReferrers; - PathSet oldReferences2; - PathSet oldStateReferences2; - Paths oldReferences_X_c; - Paths oldReferences_X_s; - if(isRealisablePath(txn, store_or_statePath)) { + //Just overwrite the old references, since there is oly 1 revision of a storePath + Paths oldReferences_c_c; Paths oldReferences_c_s; - nixDB.queryStrings(txn, dbComponentComponentReferences, store_or_statePath, oldReferences_c_c); nixDB.queryStrings(txn, dbComponentStateReferences, store_or_statePath, oldReferences_c_s); - - oldReferences2 = PathSet(oldReferences_c_c.begin(), oldReferences_c_c.end()); - oldStateReferences2 = PathSet(oldReferences_c_s.begin(), oldReferences_c_s.end()); - if (oldReferences2 == references && oldStateReferences2 == stateReferences) return; + + PathSet oldReferences = PathSet(oldReferences_c_c.begin(), oldReferences_c_c.end()); + PathSet oldStateReferences = PathSet(oldReferences_c_s.begin(), oldReferences_c_s.end()); + if (oldReferences == references && oldStateReferences == stateReferences) return; nixDB.setStrings(txn, dbComponentComponentReferences, store_or_statePath, Paths(references.begin(), references.end())); nixDB.setStrings(txn, dbComponentStateReferences, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end())); - //set vars for the referrers update code below - dbXComponentReferrers = dbComponentComponentReferrers; - dbXStateReferrers = dbComponentStateReferrers; - oldReferences_X_c = oldReferences_c_c; - oldReferences_X_s = oldReferences_c_s; + //Handle referrers ...... + + //dbComponentComponentReferrers + //dbComponentStateReferrers + /* + references c -> c + oldReferences + stateReferences c -> s + oldStateReferences + */ + + //TODO + } else if(isRealisableStatePath(txn, store_or_statePath)) { + + //Write references and referrers to a special revision (since there are multiple revisions of a statePath) + + //query the references of revision (-1 is query the latest references) Paths oldStateReferences_s_c; Paths oldStateReferences_s_s; + nixDB.queryStateReferences(txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_c, revision); + nixDB.queryStateReferences(txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, oldStateReferences_s_s, revision); - nixDB.queryStateReferences(txn, dbStateComponentReferences, store_or_statePath, oldStateReferences_s_c, revision); - nixDB.queryStateReferences(txn, dbStateStateReferences, store_or_statePath, oldStateReferences_s_s, revision); + PathSet oldReferences = PathSet(oldStateReferences_s_c.begin(), oldStateReferences_s_c.end()); + PathSet oldStateReferences = PathSet(oldStateReferences_s_s.begin(), oldStateReferences_s_s.end()); + //if (oldReferences == references && oldStateReferences == stateReferences) return; //TODO Turn on ???????????? - oldReferences2 = PathSet(oldStateReferences_s_c.begin(), oldStateReferences_s_c.end()); - oldStateReferences2 = PathSet(oldStateReferences_s_s.begin(), oldStateReferences_s_s.end()); + //set the references of revision (-1 insert as a new timestamp) + nixDB.setStateReferences(txn, dbStateComponentReferences, dbStateRevisions, store_or_statePath, Paths(references.begin(), references.end()), revision); + nixDB.setStateReferences(txn, dbStateStateReferences, dbStateRevisions, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end()), revision); - nixDB.setStateReferences(txn, dbStateComponentReferences, store_or_statePath, Paths(references.begin(), references.end()), revision); - nixDB.setStateReferences(txn, dbStateStateReferences, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end()), revision); - - //set vars for the referrers update code below - dbXComponentReferrers = dbStateComponentReferrers; - dbXStateReferrers = dbStateStateReferrers; - oldReferences_X_c = oldStateReferences_s_c; - oldReferences_X_s = oldStateReferences_s_s; + + //REFERRERS + + //for all references old and new, state and component + PathSet all = pathSets_union(pathSets_union(references, oldReferences), pathSets_union(oldStateReferences, stateReferences)); //TODO SPLIT in state and non state + for (PathSet::const_iterator i = all.begin(); i != all.end(); ++i){ + + Path referredPath = *i; //Path referred to by the statePath + + //query the refererrers of revision (-1 is query the latest refererrers) + Paths referrers_s_c; + Paths referrers_s_s; + nixDB.queryStateReferrers(txn, dbStateComponentReferrers, dbStateRevisions, referredPath, referrers_s_c, revision); //may contain other referred by paths + nixDB.queryStateReferrers(txn, dbStateStateReferrers, dbStateRevisions, referredPath, referrers_s_s, revision); + PathSet oldReferrers = PathSet(referrers_s_c.begin(), referrers_s_c.end()); + PathSet oldStateReferrers = PathSet(referrers_s_s.begin(), referrers_s_s.end()); + + + //Add store_or_statePath in if nessacary (if in references) + //TODO + + //Remove store_or_statePath if nessacary (if not in references) + //TODO + + //set the new referrers of revision (-1 insert as a new timestamp) + //nixDB.setStateReferrers(txn, dbStateComponentReferences, dbStateRevisions, referredPath, ........, revision); + //nixDB.setStateReferrers(txn, dbStateStateReferences, dbStateRevisions, referredPath, ......, revision); + + } } else throw Error(format("Path '%1%' is not a valid component or state path") % store_or_statePath); @@ -488,32 +519,14 @@ void setReferences(const Transaction & txn, const Path & store_or_statePath, //store_or_statePath must be postfixed in case it is a statePath Path store_or_statePath2 = store_or_statePath; if(isRealisableStatePath(txn, store_or_statePath)) - store_or_statePath2 = nixDB.makeStatePathRevision(store_or_statePath, revision); + store_or_statePath2 = nixDB.makeStatePathRevision(store_or_statePath, revision); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! THIS OK ??? + //NO ???? SINCE THE KEYS SHOULD BE TIMESTAMPED ?? + //WE NEEED TO inlcude the revision !! + - //The follow 4 for-loops haved been made generic with variables, they work in the case of statePaths and storePaths - - /* Update the referrers mappings of all new referenced paths. */ - for (PathSet::const_iterator i = references.begin(); i != references.end(); ++i) - if (oldReferences2.find(*i) == oldReferences2.end()) - nixDB.setString(txn, dbXComponentReferrers, addPrefix(*i, store_or_statePath2), ""); - - /* Remove referrer mappings from paths that are no longer references. */ - for (Paths::iterator i = oldReferences_X_c.begin(); i != oldReferences_X_c.end(); ++i) - if (references.find(*i) == references.end()) - nixDB.delPair(txn, dbXComponentReferrers, addPrefix(*i, store_or_statePath2)); - - /* Update the state referrers mappings of all new referenced paths. */ - for (PathSet::const_iterator i = stateReferences.begin(); i != stateReferences.end(); ++i) - if (oldStateReferences2.find(*i) == oldStateReferences2.end()) - nixDB.setString(txn, dbXStateReferrers, addPrefix(*i, store_or_statePath2), ""); - - /* Remove referrer mappings from paths that are no longer state references. */ - for (Paths::iterator i = oldReferences_X_s.begin(); i != oldReferences_X_s.end(); ++i) - if (stateReferences.find(*i) == stateReferences.end()) - nixDB.delPair(txn, dbXStateReferrers, addPrefix(*i, store_or_statePath2)); } -void queryReferencesTxn(const Transaction & txn, const Path & store_or_statePath, PathSet & references, const int revision) +void queryReferencesTxn(const Transaction & txn, const Path & store_or_statePath, PathSet & references, const int revision, int timestamp) { Paths references2; @@ -521,7 +534,7 @@ void queryReferencesTxn(const Transaction & txn, const Path & store_or_statePath nixDB.queryStrings(txn, dbComponentComponentReferences, store_or_statePath, references2); else if(isRealisableStatePath(txn, store_or_statePath)){ Path statePath_ns = toNonSharedPathTxn(txn, store_or_statePath); //Lookup its where it points to if its shared - nixDB.queryStateReferences(txn, dbStateComponentReferences, statePath_ns, references2, revision); + nixDB.queryStateReferences(txn, dbStateComponentReferences, dbStateRevisions, statePath_ns, references2, revision, timestamp); } else throw Error(format("Path '%1%' is not a valid component or state path") % store_or_statePath); @@ -534,7 +547,8 @@ void LocalStore::queryReferences(const Path & storePath, PathSet & references, c nix::queryReferencesTxn(noTxn, storePath, references, revision); } -void queryStateReferencesTxn(const Transaction & txn, const Path & store_or_statePath, PathSet & stateReferences, const int revision) +/* TODO this is just a copy of queryReferencesTxn with small differences */ +void queryStateReferencesTxn(const Transaction & txn, const Path & store_or_statePath, PathSet & stateReferences, const int revision, int timestamp) { Paths stateReferences2; @@ -542,7 +556,7 @@ void queryStateReferencesTxn(const Transaction & txn, const Path & store_or_stat nixDB.queryStrings(txn, dbComponentStateReferences, store_or_statePath, stateReferences2); else if(isRealisableStatePath(txn, store_or_statePath)){ Path statePath_ns = toNonSharedPathTxn(txn, store_or_statePath); //Lookup its where it points to if its shared - nixDB.queryStateReferences(txn, dbStateStateReferences, statePath_ns, stateReferences2, revision); + nixDB.queryStateReferences(txn, dbStateStateReferences, dbStateRevisions, statePath_ns, stateReferences2, revision, timestamp); } else throw Error(format("Path '%1%' is not a valid component or state path") % store_or_statePath); @@ -582,7 +596,7 @@ static PathSet getXReferrers(const Transaction & txn, const Path & store_or_stat else if(isValidStatePathTxn(txn, store_or_statePath)){ Path statePath_ns = toNonSharedPathTxn(txn, store_or_statePath); //Lookup its where it points to if its shared Paths referrers; - nixDB.queryStateReferrers(txn, table, statePath_ns, referrers, revision); + nixDB.queryStateReferrers(txn, table, dbStateRevisions, statePath_ns, referrers, revision); PathSet p(referrers.begin(), referrers.end()); return p; } @@ -1669,24 +1683,24 @@ void queryAllValidPaths(const Transaction & txn, PathSet & allComponentPaths, Pa } -void setStateRevisionsTxn(const Transaction & txn, const Path & statePath, const RevisionClosure & revisions) +void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions) { - nixDB.setStateRevisions(txn, dbStateRevisions, dbStateSnapshots, statePath, revisions); + nixDB.setStateRevisions(txn, dbStateRevisions, dbStateSnapshots, revisions); } -void LocalStore::setStateRevisions(const Path & statePath, const RevisionClosure & revisions) +void LocalStore::setStateRevisions(const RevisionClosure & revisions) { - nix::setStateRevisionsTxn(noTxn, statePath, revisions); + nix::setStateRevisionsTxn(noTxn, revisions); } -bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, const int revision) +bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) { - return nixDB.queryStateRevisions(txn, dbStateRevisions, dbStateSnapshots, statePath, revisions, revision); + return nixDB.queryStateRevisions(txn, dbStateRevisions, dbStateSnapshots, statePath, revisions, timestamps, revision); } -bool LocalStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, const int revision) +bool LocalStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) { - return nix::queryStateRevisionsTxn(noTxn, statePath, revisions, revision); + return nix::queryStateRevisionsTxn(noTxn, statePath, revisions, timestamps, revision); } bool queryAvailableStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionNumbers & revisions) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 4e9574259..aefd94e9d 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -100,9 +100,9 @@ public: void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); - void setStateRevisions(const Path & statePath, const RevisionClosure & revisions); + void setStateRevisions(const RevisionClosure & revisions); - bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, const int revision); + bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision); bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); @@ -224,16 +224,15 @@ bool isStateDrvTxn(const Transaction & txn, const Derivation & drv); void queryAllValidPaths(const Transaction & txn, PathSet & allComponentPaths, PathSet & allStatePaths); bool isValidStatePathTxn(const Transaction & txn, const Path & path); -void queryReferencesTxn(const Transaction & txn, const Path & path, PathSet & references, const int revision); -void queryStateReferencesTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferences, const int revision); +void queryReferencesTxn(const Transaction & txn, const Path & path, PathSet & references, const int revision, int timestamp = -1); +void queryStateReferencesTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferences, const int revision, int timestamp = -1); void queryReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & referrers, const int revision); void queryStateReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferrers, const int revision); - Path queryStatePathDrvTxn(const Transaction & txn, const Path & statePath); void storePathRequisitesTxn(const Transaction & txn, const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); -void setStateRevisionsTxn(const Transaction & txn, const Path & statePath, const RevisionClosure & revisions); +void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions); bool isValidPathTxn(const Transaction & txn, const Path & path); bool isValidStatePathTxn(const Transaction & txn, const Path & path); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index dd19a7069..53b2dc0c3 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -469,13 +469,13 @@ void RemoteStore::storePathRequisites(const Path & storeOrstatePath, const bool } //TODO -void RemoteStore::setStateRevisions(const Path & statePath, const RevisionClosure & revisions) +void RemoteStore::setStateRevisions(const RevisionClosure & revisions) { } //TODO -bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, const int revision) +bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) { return false; } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 94f4441e5..911117f50 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -88,9 +88,9 @@ public: void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); - void setStateRevisions(const Path & statePath, const RevisionClosure & revisions); + void setStateRevisions(const RevisionClosure & revisions); - bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, const int revision); + bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision); bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 1cb06bc17..3742e6608 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -220,14 +220,15 @@ public: virtual void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision) = 0; /* TODO */ - virtual void setStateRevisions(const Path & statePath, const RevisionClosure & revisions) = 0; + virtual void setStateRevisions(const RevisionClosure & revisions) = 0; /* TODO */ - virtual bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, const int revision) = 0; + virtual bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) = 0; /* TODO */ virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) = 0; + /* TODO */ virtual void commitStatePath(const Path & statePath) = 0; }; diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 7fac9238a..070b9ecf5 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -61,7 +61,8 @@ typedef set PathSet; //state types typedef list RevisionNumbers; //the Strings (list) of StateReferences and this list are connected by position typedef map Snapshots; //Automatically sorted on Path :) -typedef map RevisionClosure; +typedef map RevisionClosure; +typedef map RevisionClosureTS; typedef map StateReferences; diff --git a/src/nix-state/nix-state.cc b/src/nix-state/nix-state.cc index 0a0900813..85ff2e2ab 100644 --- a/src/nix-state/nix-state.cc +++ b/src/nix-state/nix-state.cc @@ -236,10 +236,11 @@ static void revertToRevision(Strings opFlags, Strings opArgs) bool isStateComponent; string program_args; Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args); - DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs; bool recursive = true; //TODO !!!!!!!!!!!!!!!!! + //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! add TXN here ??????????? + PathSet statePaths; if(recursive) PathSet statePaths = getAllStateDerivationsRecursively(componentPath, revision_arg); //get dependecies (if neccecary | recusively) of all state components that need to be updated @@ -248,12 +249,21 @@ static void revertToRevision(Strings opFlags, Strings opArgs) //Get the revisions recursively to also roll them back RevisionClosure getRivisions; - bool b = store->queryStateRevisions(statePath, getRivisions, revision_arg); + RevisionClosureTS getTimestamps; + bool b = store->queryStateRevisions(statePath, getRivisions, getTimestamps, revision_arg); //Revert each statePath in the list for (RevisionClosure::iterator i = getRivisions.begin(); i != getRivisions.end(); ++i){ Path statePath = (*i).first; Snapshots revisioned_paths = (*i).second; + int timestamp = getTimestamps[statePath]; + + //get new timestamp (just before restoring the path) for references update ??? + + + //get its derivation-state-items + Derivation statePath_drv = derivationFromPath(queryStatePathDrvTxn(noTxn, statePath)); + DerivationStateOutputDirs stateOutputDirs = statePath_drv.stateOutputDirs; //TODO Sort snapshots??? eg first restore root, then the subdirs?? @@ -275,19 +285,18 @@ static void revertToRevision(Strings opFlags, Strings opArgs) //Now that were still here, we need to copy the state from the previous version back Strings p_args; p_args.push_back("-c"); //we use the shell to execute the cp command becuase the shell expands the '*' - string cpcommand = "cp -R"; + string cpcommand = "cp"; if(revertPathOrFile.substr(revertPathOrFile.length() -1 , revertPathOrFile.length()) == "/"){ //is dir string revert_to_path = revertPathOrFile.substr(0, revertPathOrFile.length() -1) + "@" + unsignedInt2String(epoch); - cpcommand += " " + revert_to_path + "/*"; + cpcommand += " -R " + revert_to_path + "/*"; //clean all contents of the folder first (so were sure the path is clean) if(pathExists(revertPathOrFile)) deletePath(revertPathOrFile); - //If path was not deleted in the previous version, we need to make sure it exists or cp will fail - if(epoch == 0) + if(epoch == 0) //Path was deleted so were done continue; - else + else //If path was not deleted in the previous version, we need to make sure it exists or cp will fail ensureDirExists(revertPathOrFile); //If the the dir has not contents then a cp ..../* will error since * cannot be expanded. So in this case were done and dont have to revert. @@ -310,20 +319,40 @@ static void revertToRevision(Strings opFlags, Strings opArgs) } //Revert - printMsg(lvlError, format("Reverting '%1%'") % revertPathOrFile); + printMsg(lvlError, format("Reverting '%1%@%2%'") % revertPathOrFile % unsignedInt2String(epoch)); + printMsg(lvlError, format("Command: '%1%'") % cpcommand); cpcommand += " " + revertPathOrFile; p_args.push_back(cpcommand); runProgram_AndPrintOutput("sh", true, p_args, "sh-cp"); //TODO does this work on windows? } + + //Also revert state references to the specific revision (the revision is already converted to a timestamp here) + PathSet state_stateReferences; + queryStateReferencesTxn(noTxn, statePath, state_stateReferences, -1, timestamp); + + PathSet state_references; + queryReferencesTxn(noTxn, statePath, state_references, -1, timestamp); + + //nixDB.setStateReferences(txn, dbStateComponentReferences, dbStateRevisions, statePath, ..., -1, NEWTIMESTAMP); + //nixDB.setStateReferences(txn, dbStateStateReferences, dbStateRevisions, statePath, ........, -1, NEWTIMESTAMP); + + //TODO SET REFERRERS ALSO BACK !!!!!!!!!! + //setReferences is based on a revision, but make can change that ?? + printMsg(lvlError, format("Reverted state of '%1%' to revision '%2%'") % statePath % revision_arg); } + + + + + } //TODO include this call in the validate function //TODO ONLY CALL THIS FUNCTION ON A NON-SHARED STATE PATH!!!!!!!!!!! void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath - , PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences, const int revision) //only for recursion + , PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences) //only for recursion { //Check if is a state Path if(! isValidStatePathTxn(txn, statePath)) @@ -389,7 +418,7 @@ void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePa if(diff_references_added.size() != 0 || diff_references_removed.size() != 0 || diff_state_references_added.size() != 0 || diff_state_references_removed.size() != 0 ) { - printMsg(lvlError, format("Updating new references to revision %1% for statepath: '%2%'") % revision % statePath); + printMsg(lvlError, format("Updating new references for statepath: '%1%'") % statePath); Path drvPath = queryStatePathDrvTxn(txn, statePath); registerValidPath(txn, statePath, @@ -397,7 +426,7 @@ void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePa state_references, state_stateReferences, drvPath, - revision); + -1); //Set at a new timestamp } } @@ -417,15 +446,10 @@ void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path //call scanForAllReferences again on all statePaths for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i){ - //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - int revision = 0; - //Get last revision number from DB !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - //TODO first snapshot all paths....? - //Scan, update, call recursively PathSet newFoundComponentReferences; PathSet newFoundStateReferences; - scanAndUpdateAllReferencesTxn(txn, *i, newFoundComponentReferences, newFoundStateReferences, revision); + scanAndUpdateAllReferencesTxn(txn, *i, newFoundComponentReferences, newFoundStateReferences); //Call the function recursively again on all newly found references //TODO test if this works PathSet allNewReferences = pathSets_union(newFoundComponentReferences, newFoundStateReferences); @@ -452,34 +476,33 @@ static void opRunComponent(Strings opFlags, Strings opArgs) //add locks ... ? //svn lock ... ? - //get all current dependecies (if neccecary | recusively) of all state components that need to be updated - PathSet statePaths; - store->storePathRequisites(root_componentPath, false, statePaths, false, true, -1); - statePaths.insert(root_statePath); - - //Replace all shared paths in the set for their real paths - statePaths = toNonSharedPathSetTxn(noTxn, statePaths); - //TODO maybe also scan the parameters for state or component hashes? //program_args //TODO Transaction txn; //createStoreTransaction(txn); - - + //******************* Run **************************** if(!only_commit) executeShellCommand(root_componentPath + root_binary + " " + root_program_args); - //******************* With everything in place, we call the commit script on all statePaths (in)directly referenced ********************** - - //Start transaction TODO - - //Scan for new references if neccecary + //******************* Scan for new references if neccecary if(scanforReferences) scanAndUpdateAllReferencesRecusivelyTxn(txn, root_statePath); + + //get all current (maybe updated by the scan) dependecies (if neccecary | recusively) of all state components that need to be updated + PathSet statePaths; + store->storePathRequisites(root_componentPath, false, statePaths, false, true, -1); + statePaths.insert(root_statePath); + + //Start transaction TODO + + //Replace all shared paths in the set for their real paths + statePaths = toNonSharedPathSetTxn(noTxn, statePaths); + + //******************* With everything in place, we call the commit script on all statePaths (in)directly referenced ********************** //Commit all statePaths RevisionClosure rivisionMapping; @@ -487,14 +510,15 @@ static void opRunComponent(Strings opFlags, Strings opArgs) rivisionMapping[*i] = commitStatePathTxn(txn, *i); //Save new revisions - setStateRevisionsTxn(txn, root_statePath, rivisionMapping); + setStateRevisionsTxn(txn, rivisionMapping); //Commit transaction //txn.commit(); //Debugging RevisionClosure getRivisions; - bool b = store->queryStateRevisions(root_statePath, getRivisions, -1); + RevisionClosureTS empty; + bool b = store->queryStateRevisions(root_statePath, getRivisions, empty, -1); for (RevisionClosure::iterator i = getRivisions.begin(); i != getRivisions.end(); ++i){ //printMsg(lvlError, format("State %1% has revision %2%") % (*i).first % int2String((*i).second)); } @@ -607,8 +631,6 @@ void run(Strings args) //updateRevisionNumbers("/nix/state/xf582zrz6xl677llr07rvskgsi3dli1d-hellohardcodedstateworld-dep1-1.0-test"); //return; - //printMsg(lvlError, format("NOW: '%1%'") % getTimeStamp()); - //auto sort map test; test["q"] = "324"; @@ -616,6 +638,10 @@ void run(Strings args) test["a"] = "a"; for (map::const_iterator j = test.begin(); j != test.end(); ++j) printMsg(lvlError, format("KEY: '%1%'") % (*j).first); + + printMsg(lvlError, format("NOW: '%1%'") % getTimeStamp()); + return; + */ /* test */