Skip to content

Commit 1e0be61

Browse files
authored
Merge pull request #245 from project-minigraf/feature/repl-tx-count
feat: surface tx_count in QueryResult and REPL output
2 parents c2f8446 + 79fb7ac commit 1e0be61

8 files changed

Lines changed: 85 additions & 39 deletions

File tree

minigraf-c/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,12 @@ fn value_to_json(v: &Value) -> serde_json::Value {
203203

204204
fn query_result_to_json(result: QueryResult) -> String {
205205
let val = match result {
206-
QueryResult::Transacted(tx) => serde_json::json!({"transacted": tx}),
207-
QueryResult::Retracted(tx) => serde_json::json!({"retracted": tx}),
206+
QueryResult::Transacted { tx_id, tx_count } => {
207+
serde_json::json!({"transacted": tx_id, "tx_count": tx_count})
208+
}
209+
QueryResult::Retracted { tx_id, tx_count } => {
210+
serde_json::json!({"retracted": tx_id, "tx_count": tx_count})
211+
}
208212
QueryResult::Ok => serde_json::json!({"ok": true}),
209213
QueryResult::QueryResults { vars, results } => {
210214
let rows: Vec<Vec<serde_json::Value>> = results

minigraf-ffi/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,12 @@ fn value_to_json(v: &Value) -> serde_json::Value {
120120
fn query_result_to_json(result: QueryResult) -> String {
121121
use serde_json::json;
122122
let val = match result {
123-
QueryResult::Transacted(tx_id) => json!({"transacted": tx_id}),
124-
QueryResult::Retracted(tx_id) => json!({"retracted": tx_id}),
123+
QueryResult::Transacted { tx_id, tx_count } => {
124+
json!({"transacted": tx_id, "tx_count": tx_count})
125+
}
126+
QueryResult::Retracted { tx_id, tx_count } => {
127+
json!({"retracted": tx_id, "tx_count": tx_count})
128+
}
125129
QueryResult::Ok => json!({"ok": true}),
126130
QueryResult::QueryResults { vars, results } => {
127131
let rows: Vec<Vec<serde_json::Value>> = results
@@ -162,9 +166,13 @@ mod tests {
162166

163167
#[test]
164168
fn query_result_to_json_transacted() {
165-
let json = query_result_to_json(QueryResult::Transacted(12345));
169+
let json = query_result_to_json(QueryResult::Transacted {
170+
tx_id: 12345,
171+
tx_count: 1,
172+
});
166173
let v: serde_json::Value = serde_json::from_str(&json).expect("valid json");
167174
assert_eq!(v["transacted"], serde_json::json!(12345));
175+
assert_eq!(v["tx_count"], serde_json::json!(1));
168176
}
169177

170178
#[test]
@@ -231,9 +239,13 @@ mod tests {
231239

232240
#[test]
233241
fn query_result_to_json_retracted() {
234-
let json = query_result_to_json(QueryResult::Retracted(99));
242+
let json = query_result_to_json(QueryResult::Retracted {
243+
tx_id: 99,
244+
tx_count: 2,
245+
});
235246
let v: serde_json::Value = serde_json::from_str(&json).expect("valid json");
236247
assert_eq!(v["retracted"], serde_json::json!(99));
248+
assert_eq!(v["tx_count"], serde_json::json!(2));
237249
}
238250

239251
#[test]

minigraf-node/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ fn value_to_json(v: &Value) -> serde_json::Value {
2222

2323
fn query_result_to_json(result: QueryResult) -> String {
2424
let val = match result {
25-
QueryResult::Transacted(tx) => serde_json::json!({"transacted": tx}),
26-
QueryResult::Retracted(tx) => serde_json::json!({"retracted": tx}),
25+
QueryResult::Transacted { tx_id, tx_count } => {
26+
serde_json::json!({"transacted": tx_id, "tx_count": tx_count})
27+
}
28+
QueryResult::Retracted { tx_id, tx_count } => {
29+
serde_json::json!({"retracted": tx_id, "tx_count": tx_count})
30+
}
2731
QueryResult::Ok => serde_json::json!({"ok": true}),
2832
QueryResult::QueryResults { vars, results } => {
2933
let rows: Vec<Vec<serde_json::Value>> = results

src/browser/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,12 @@ fn query_result_to_json(result: QueryResult) -> String {
329329
use serde_json::{Value as JVal, json};
330330

331331
let val: JVal = match result {
332-
QueryResult::Transacted(tx_id) => json!({"transacted": tx_id}),
333-
QueryResult::Retracted(tx_id) => json!({"retracted": tx_id}),
332+
QueryResult::Transacted { tx_id, tx_count } => {
333+
json!({"transacted": tx_id, "tx_count": tx_count})
334+
}
335+
QueryResult::Retracted { tx_id, tx_count } => {
336+
json!({"retracted": tx_id, "tx_count": tx_count})
337+
}
334338
QueryResult::Ok => json!({"ok": true}),
335339
QueryResult::QueryResults { vars, results } => {
336340
let rows: Vec<Vec<JVal>> = results

src/db.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,9 @@ impl Minigraf {
509509

510510
// Return the same QueryResult the executor would have returned.
511511
if is_retract {
512-
Ok(QueryResult::Retracted(tx_id))
512+
Ok(QueryResult::Retracted { tx_id, tx_count })
513513
} else {
514-
Ok(QueryResult::Transacted(tx_id))
514+
Ok(QueryResult::Transacted { tx_id, tx_count })
515515
}
516516
} else {
517517
// Read-only: no lock needed

src/graph/storage.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,13 @@ impl FactStorage {
182182
/// has no per-fact override
183183
///
184184
/// # Returns
185-
/// The TxId (timestamp) assigned to all facts in this batch
185+
/// `(tx_id, tx_count)` — the Unix-ms timestamp and the monotonic counter
186+
/// assigned to all facts in this batch.
186187
pub(crate) fn transact_batch(
187188
&self,
188189
fact_tuples: Vec<(EntityId, Attribute, Value, Option<TransactOptions>)>,
189190
default_opts: Option<TransactOptions>,
190-
) -> Result<TxId> {
191+
) -> Result<(TxId, u64)> {
191192
let tx_id = tx_id_now();
192193
let tx_count = self
193194
.tx_counter
@@ -225,7 +226,7 @@ impl FactStorage {
225226
}
226227
d.facts.extend(facts);
227228

228-
Ok(tx_id)
229+
Ok((tx_id, tx_count))
229230
}
230231

231232
/// Retract a batch of facts with automatic timestamping
@@ -237,8 +238,12 @@ impl FactStorage {
237238
/// * `fact_tuples` - Vec of (EntityId, Attribute, Value) tuples to retract
238239
///
239240
/// # Returns
240-
/// The TxId (timestamp) assigned to these retractions
241-
pub(crate) fn retract(&self, fact_tuples: Vec<(EntityId, Attribute, Value)>) -> Result<TxId> {
241+
/// `(tx_id, tx_count)` — the Unix-ms timestamp and the monotonic counter
242+
/// assigned to these retractions.
243+
pub(crate) fn retract(
244+
&self,
245+
fact_tuples: Vec<(EntityId, Attribute, Value)>,
246+
) -> Result<(TxId, u64)> {
242247
let tx_id = tx_id_now();
243248
let tx_count = self
244249
.tx_counter
@@ -272,7 +277,7 @@ impl FactStorage {
272277
}
273278
d.facts.extend(retractions);
274279

275-
Ok(tx_id)
280+
Ok((tx_id, tx_count))
276281
}
277282

278283
/// Insert a fact with its original tx_id and tx_count preserved.
@@ -876,7 +881,7 @@ mod tests {
876881
std::thread::sleep(std::time::Duration::from_millis(2));
877882

878883
// Retract the fact
879-
let tx2 = storage
884+
let (tx2, _) = storage
880885
.retract(vec![(
881886
alice,
882887
":person/name".to_string(),

src/query/datalog/executor.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,27 @@ fn query_uses_per_fact_pseudo_attr(query: &DatalogQuery) -> bool {
4747
/// println!("{}: {:?}", vars[0], row[0]);
4848
/// }
4949
/// }
50-
/// QueryResult::Transacted(tx_id) => println!("wrote tx {}", tx_id),
51-
/// QueryResult::Retracted(tx_id) => println!("retracted tx {}", tx_id),
50+
/// QueryResult::Transacted { tx_id, tx_count } => println!("tx {} (count {})", tx_id, tx_count),
51+
/// QueryResult::Retracted { tx_id, tx_count } => println!("retracted tx {} (count {})", tx_id, tx_count),
5252
/// QueryResult::Ok => {}
5353
/// }
5454
/// ```
5555
#[derive(Debug, Clone, PartialEq)]
5656
pub enum QueryResult {
57-
/// Transaction completed successfully. The inner value is the transaction ID
58-
/// (Unix milliseconds), also displayed by the REPL as the `tx` counter.
59-
Transacted(TxId),
60-
/// Retraction completed successfully with the transaction ID.
61-
Retracted(TxId),
57+
/// Transaction completed successfully.
58+
Transacted {
59+
/// Unix-millisecond timestamp of the transaction.
60+
tx_id: TxId,
61+
/// Monotonic counter (1, 2, 3 …). This is the value used by `:as-of N`.
62+
tx_count: u64,
63+
},
64+
/// Retraction completed successfully.
65+
Retracted {
66+
/// Unix-millisecond timestamp of the retraction.
67+
tx_id: TxId,
68+
/// Monotonic counter (1, 2, 3 …). This is the value used by `:as-of N`.
69+
tx_count: u64,
70+
},
6271
/// Query results: list of variable bindings
6372
QueryResults {
6473
/// The variable names in the order they appear in the `:find` clause.
@@ -236,12 +245,12 @@ impl DatalogExecutor {
236245
fact_tuples.push((entity_id, attribute, value, per_fact_opts));
237246
}
238247

239-
let tx_id = self
248+
let (tx_id, tx_count) = self
240249
.storage
241250
.transact_batch(fact_tuples, tx_opts)
242251
.map_err(|e| anyhow!("Transaction failed: {}", e))?;
243252

244-
Ok(QueryResult::Transacted(tx_id))
253+
Ok(QueryResult::Transacted { tx_id, tx_count })
245254
}
246255

247256
/// Execute a retract command: retract facts from storage
@@ -266,12 +275,12 @@ impl DatalogExecutor {
266275
fact_tuples.push((entity_id, attribute, value));
267276
}
268277

269-
let tx_id = self
278+
let (tx_id, tx_count) = self
270279
.storage
271280
.retract(fact_tuples)
272281
.map_err(|e| anyhow!("Retraction failed: {}", e))?;
273282

274-
Ok(QueryResult::Retracted(tx_id))
283+
Ok(QueryResult::Retracted { tx_id, tx_count })
275284
}
276285

277286
/// Build a filtered fact snapshot for a query's temporal constraints.
@@ -1679,8 +1688,9 @@ mod tests {
16791688

16801689
let result = executor.execute(cmd).unwrap();
16811690
match result {
1682-
QueryResult::Transacted(tx_id) => {
1691+
QueryResult::Transacted { tx_id, tx_count } => {
16831692
assert!(tx_id > 0);
1693+
assert!(tx_count > 0);
16841694
}
16851695
_ => panic!("Expected Transacted result"),
16861696
}
@@ -1817,8 +1827,9 @@ mod tests {
18171827

18181828
let result = executor.execute(cmd).unwrap();
18191829
match result {
1820-
QueryResult::Retracted(tx_id) => {
1830+
QueryResult::Retracted { tx_id, tx_count } => {
18211831
assert!(tx_id > 0);
1832+
assert!(tx_count > 0);
18221833
}
18231834
_ => panic!("Expected Retracted result"),
18241835
}
@@ -1844,7 +1855,7 @@ mod tests {
18441855

18451856
let result = executor.execute(cmd).unwrap();
18461857
match result {
1847-
QueryResult::Transacted(_) => {}
1858+
QueryResult::Transacted { .. } => {}
18481859
_ => panic!("Expected Transacted result"),
18491860
}
18501861

src/repl.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@ impl<'a> Repl<'a> {
186186
use crate::query::datalog::QueryResult as DResult;
187187

188188
match result {
189-
DResult::Transacted(tx_id) => {
190-
println!("✓ Transacted successfully (tx: {})", tx_id);
189+
DResult::Transacted { tx_id, tx_count } => {
190+
println!("✓ Transacted successfully (tx: {tx_id}, count: {tx_count})");
191191
}
192-
DResult::Retracted(tx_id) => {
193-
println!("✓ Retracted successfully (tx: {})", tx_id);
192+
DResult::Retracted { tx_id, tx_count } => {
193+
println!("✓ Retracted successfully (tx: {tx_id}, count: {tx_count})");
194194
}
195195
DResult::QueryResults { vars, results } => {
196196
if results.is_empty() {
@@ -453,13 +453,19 @@ mod tests {
453453
#[test]
454454
fn print_result_transacted() {
455455
use crate::query::datalog::QueryResult as DResult;
456-
Repl::print_result(DResult::Transacted(12345678));
456+
Repl::print_result(DResult::Transacted {
457+
tx_id: 12345678,
458+
tx_count: 1,
459+
});
457460
}
458461

459462
#[test]
460463
fn print_result_retracted() {
461464
use crate::query::datalog::QueryResult as DResult;
462-
Repl::print_result(DResult::Retracted(12345678));
465+
Repl::print_result(DResult::Retracted {
466+
tx_id: 12345678,
467+
tx_count: 2,
468+
});
463469
}
464470

465471
#[test]

0 commit comments

Comments
 (0)