@@ -58,6 +58,9 @@ struct AttrDb
5858 SQLiteStmt insertAttributeWithContext;
5959 SQLiteStmt queryAttribute;
6060 SQLiteStmt queryAttributes;
61+ SQLiteStmt upsertAttribute;
62+ SQLiteStmt insertAttributeIfNotExists;
63+ SQLiteStmt deleteMissingChildren;
6164 std::unique_ptr<SQLiteTxn> txn;
6265 };
6366
@@ -92,6 +95,17 @@ struct AttrDb
9295
9396 state->queryAttributes .create (state->db , " select name from Attributes where parent = ?" );
9497
98+ state->upsertAttribute .create (
99+ state->db ,
100+ " insert into Attributes(parent, name, type, value) values (?, ?, ?, ?) "
101+ " on conflict(parent, name) do update set type = excluded.type, value = excluded.value "
102+ " returning rowid" );
103+
104+ state->insertAttributeIfNotExists .create (
105+ state->db , " insert or ignore into Attributes(parent, name, type, value) values (?, ?, ?, ?)" );
106+
107+ state->deleteMissingChildren .create (state->db , " delete from Attributes where parent = ? and type = 3" );
108+
95109 state->txn = std::make_unique<SQLiteTxn>(state->db );
96110 }
97111
@@ -126,18 +140,20 @@ struct AttrDb
126140 return doSQLite ([&]() {
127141 auto state (_state->lock ());
128142
129- state-> insertAttribute . use ()
130- . apply (key. first )
131- .apply (symbols[ key.second ] )
132- .apply (AttrType::FullAttrs )
133- .apply (0 , false )
134- . exec ( );
135-
136- AttrId rowId = state-> db . getLastInsertedRowId ( );
143+ // Seal the attribute names: we now know the complete set.
144+ auto upsertAttribute (state-> upsertAttribute . use ( )
145+ .apply (key.first )
146+ .apply (symbols[key. second ] )
147+ .apply (AttrType::FullAttrs )
148+ . apply ( 0 , false ) );
149+ upsertAttribute. next ();
150+ auto rowId = (AttrId) upsertAttribute. getInt ( 0 );
137151 assert (rowId);
152+ state->deleteMissingChildren .use ().apply (rowId).exec ();
138153
154+ // Insert children as placeholders, but don't replace existing entries
139155 for (auto & attr : attrs)
140- state->insertAttribute .use ()
156+ state->insertAttributeIfNotExists .use ()
141157 .apply (rowId)
142158 .apply (symbols[attr])
143159 .apply (AttrType::Placeholder)
@@ -461,9 +477,13 @@ Value & AttrCursor::forceValue()
461477 }
462478
463479 if (root->db && (!cachedValue || std::get_if<placeholder_t >(&cachedValue->second ))) {
464- if (v.type () == nString)
465- cachedValue = {root->db ->setString (getKey (), v.string_view (), v.context ()), string_t {v.string_view (), {}}};
466- else if (v.type () == nPath) {
480+ if (v.type () == nString) {
481+ NixStringContext context;
482+ copyContext (v, context);
483+ cachedValue = {
484+ root->db ->setString (getKey (), v.string_view (), v.context ()),
485+ string_t {v.string_view (), std::move (context)}};
486+ } else if (v.type () == nPath) {
467487 auto path = v.path ().path ;
468488 cachedValue = {root->db ->setString (getKey (), path.abs ()), string_t {path.abs (), {}}};
469489 } else if (v.type () == nBool)
0 commit comments