refactor(db): unwind vestigial worker source plumbing (ADR-0052 follow-up)
ADR-0052 moved success journaling out of the worker to the dispatch layer, leaving the `source` that handlers threaded purely for the worker's old history.log write dead. Remove it: - drop `_source` from finalize_persistence and do_rebuild_from_text - inline + delete the three read-only *_request wrappers - drop the now-unused `source` param from the ~30 forwarding worker handlers (leaf + composite), compiler-guided - remove the `source` field from the DescribeTable/QueryData/RunSelect requests and their DatabaseHandle methods (call sites updated) The only worker `source` left is the snapshot/undo label (snapshot_then / stage_pre_mutation / begin_batch). Purely mechanical, no behaviour change. 2471 pass / 0 fail / 1 ignored, clippy clean.
This commit is contained in:
@@ -93,7 +93,7 @@ fn rename_column_with_case_variant_table_keeps_metadata_in_step() {
|
||||
.expect("rename column via a case-variant table name");
|
||||
|
||||
let desc = r
|
||||
.block_on(db.describe_table("Items".to_string(), None))
|
||||
.block_on(db.describe_table("Items".to_string()))
|
||||
.expect("describe Items");
|
||||
let amount = desc
|
||||
.columns
|
||||
@@ -126,7 +126,7 @@ fn insert_with_case_variant_table_persists_and_survives_rebuild() {
|
||||
|
||||
let db = fresh_rebuild(db, &project, &r);
|
||||
let rows = r
|
||||
.block_on(db.query_data("Items".to_string(), None, None, None))
|
||||
.block_on(db.query_data("Items".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 1, "the wrong-case insert survived the rebuild (no data loss)");
|
||||
@@ -146,7 +146,7 @@ fn add_column_with_case_variant_table_survives_rebuild() {
|
||||
);
|
||||
|
||||
let db = fresh_rebuild(db, &project, &r);
|
||||
let desc = r.block_on(db.describe_table("Items".to_string(), None)).expect("describe");
|
||||
let desc = r.block_on(db.describe_table("Items".to_string())).expect("describe");
|
||||
let qty = desc.columns.iter().find(|c| c.name == "qty").expect("qty added");
|
||||
assert_eq!(qty.user_type, Some(Type::Int), "qty's user-type survived the rebuild");
|
||||
// The CHECK is intact too (a negative qty is refused under the real table).
|
||||
@@ -224,12 +224,12 @@ fn add_relationship_with_case_variant_tables_survives_rebuild() {
|
||||
add 1:n relationship from parent.id to child.parent_id\n",
|
||||
);
|
||||
// The parent's inbound relationship is visible under the stored case.
|
||||
let p = r.block_on(db.describe_table("Parent".to_string(), None)).expect("describe Parent");
|
||||
let p = r.block_on(db.describe_table("Parent".to_string())).expect("describe Parent");
|
||||
assert_eq!(p.inbound_relationships.len(), 1, "relationship recorded under the stored case");
|
||||
assert_eq!(p.inbound_relationships[0].other_table, "Child");
|
||||
|
||||
let db = fresh_rebuild(db, &project, &r);
|
||||
let p = r.block_on(db.describe_table("Parent".to_string(), None)).expect("describe Parent");
|
||||
let p = r.block_on(db.describe_table("Parent".to_string())).expect("describe Parent");
|
||||
assert_eq!(p.inbound_relationships.len(), 1, "relationship survived the rebuild");
|
||||
assert_eq!(p.inbound_relationships[0].other_table, "Child");
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ fn compound_fk_declares_enforces_and_round_trips() {
|
||||
);
|
||||
|
||||
// describe shows the compound endpoints symmetrically.
|
||||
let city = db.describe_table("City".to_string(), None).await.unwrap();
|
||||
let city = db.describe_table("City".to_string()).await.unwrap();
|
||||
let outbound = &city.outbound_relationships[0];
|
||||
assert_eq!(
|
||||
outbound.local_columns,
|
||||
@@ -329,7 +329,7 @@ fn compound_fk_create_fk_makes_both_child_columns() {
|
||||
)
|
||||
.await
|
||||
.expect("add compound relationship with --create-fk");
|
||||
let city = db.describe_table("City".to_string(), None).await.unwrap();
|
||||
let city = db.describe_table("City".to_string()).await.unwrap();
|
||||
for col in ["c_country", "c_code"] {
|
||||
assert!(
|
||||
city.columns.iter().any(|c| c.name == col),
|
||||
@@ -527,7 +527,7 @@ fn compound_fk_survives_rebuild_from_text() {
|
||||
.await;
|
||||
assert!(bad.is_err(), "compound FK still enforced after rebuild from text");
|
||||
// Endpoints survived the round-trip intact.
|
||||
let city = db.describe_table("City".to_string(), None).await.unwrap();
|
||||
let city = db.describe_table("City".to_string()).await.unwrap();
|
||||
assert_eq!(
|
||||
city.outbound_relationships[0].other_columns,
|
||||
vec!["country".to_string(), "code".to_string()],
|
||||
@@ -563,7 +563,7 @@ fn compound_fk_undo_removes_the_relationship() {
|
||||
.await
|
||||
.expect("add compound relationship");
|
||||
assert_eq!(
|
||||
db.describe_table("City".to_string(), None)
|
||||
db.describe_table("City".to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.outbound_relationships
|
||||
@@ -573,7 +573,7 @@ fn compound_fk_undo_removes_the_relationship() {
|
||||
// One undo step removes the whole relationship (ADR-0013/0006).
|
||||
db.undo().await.unwrap().expect("undo applied");
|
||||
assert!(
|
||||
db.describe_table("City".to_string(), None)
|
||||
db.describe_table("City".to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.outbound_relationships
|
||||
|
||||
@@ -76,7 +76,7 @@ fn rebuild_restores_schema_only_project() {
|
||||
|
||||
// Phase 4: confirm Customers exists with the right shape.
|
||||
let desc = rt()
|
||||
.block_on(async { db.describe_table("Customers".to_string(), None).await })
|
||||
.block_on(async { db.describe_table("Customers".to_string()).await })
|
||||
.expect("describe_table");
|
||||
assert_eq!(desc.name, "Customers");
|
||||
let cols: Vec<&str> = desc.columns.iter().map(|c| c.name.as_str()).collect();
|
||||
@@ -143,7 +143,7 @@ fn rebuild_restores_rows_from_csv() {
|
||||
});
|
||||
|
||||
let rows = rt()
|
||||
.block_on(async { db.query_data("Customers".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("Customers".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert_eq!(rows.rows.len(), 2);
|
||||
let names: Vec<Option<String>> = rows.rows.iter().map(|r| r[1].clone()).collect();
|
||||
@@ -371,7 +371,7 @@ fn rebuild_preserves_created_at_from_yaml() {
|
||||
// Trigger any successful command so project.yaml is
|
||||
// rewritten from the now-rebuilt db state.
|
||||
rt().block_on(async {
|
||||
db.describe_table("T".to_string(), Some("show table T".to_string()))
|
||||
db.describe_table("T".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
// describe is read-only; force a rewrite by adding a column.
|
||||
@@ -451,7 +451,7 @@ fn rebuild_restores_indexes() {
|
||||
});
|
||||
|
||||
let desc = rt()
|
||||
.block_on(async { db.describe_table("Customers".to_string(), None).await })
|
||||
.block_on(async { db.describe_table("Customers".to_string()).await })
|
||||
.expect("describe_table");
|
||||
assert_eq!(desc.indexes.len(), 1, "index should survive rebuild");
|
||||
assert_eq!(desc.indexes[0].name, "idx_email");
|
||||
|
||||
@@ -173,7 +173,7 @@ fn rebuild_against_populated_db_wipes_and_reloads() {
|
||||
.expect("rebuild");
|
||||
});
|
||||
let rows = rt()
|
||||
.block_on(async { db.query_data("Customers".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("Customers".to_string(), None, None).await })
|
||||
.unwrap();
|
||||
assert_eq!(rows.rows.len(), 1);
|
||||
assert_eq!(rows.rows[0][1].as_deref(), Some("Edna"));
|
||||
|
||||
@@ -362,7 +362,7 @@ fn end_to_end_export_then_import_real_project() {
|
||||
|
||||
// Round-trip: the inserted row is back.
|
||||
let data_view = rt()
|
||||
.block_on(async { imported_db.query_data("Customers".to_string(), None, None, None).await })
|
||||
.block_on(async { imported_db.query_data("Customers".to_string(), None, None).await })
|
||||
.expect("query data");
|
||||
assert_eq!(data_view.rows.len(), 1);
|
||||
// Serial id auto-filled to 1; Name was the inserted value.
|
||||
|
||||
+6
-6
@@ -107,7 +107,7 @@ fn generates_junction_with_compound_pk_and_two_enforced_fks() {
|
||||
assert!(tables.contains(&"Students_Courses".to_string()), "tables: {tables:?}");
|
||||
|
||||
// Two FK columns, both part of the compound PK.
|
||||
let desc = db.describe_table("Students_Courses".to_string(), None).await.unwrap();
|
||||
let desc = db.describe_table("Students_Courses".to_string()).await.unwrap();
|
||||
let cols: Vec<(&str, bool)> =
|
||||
desc.columns.iter().map(|c| (c.name.as_str(), c.primary_key)).collect();
|
||||
assert_eq!(
|
||||
@@ -191,7 +191,7 @@ fn compound_parent_pk_contributes_one_fk_column_each() {
|
||||
.await
|
||||
.expect("create m:n");
|
||||
|
||||
let desc = db.describe_table("Students_Sections".to_string(), None).await.unwrap();
|
||||
let desc = db.describe_table("Students_Sections".to_string()).await.unwrap();
|
||||
let names: Vec<&str> = desc.columns.iter().map(|c| c.name.as_str()).collect();
|
||||
assert_eq!(names, vec!["Students_id", "Sections_course_id", "Sections_term"]);
|
||||
// All three form the compound PK.
|
||||
@@ -221,7 +221,7 @@ fn deleting_a_parent_cascades_to_the_junction() {
|
||||
|
||||
// Deleting the student cascades to the junction (ON DELETE CASCADE).
|
||||
db.delete("Students".to_string(), RowFilter::AllRows, None).await.unwrap();
|
||||
let rows = db.query_data("Students_Courses".to_string(), None, None, None).await.unwrap();
|
||||
let rows = db.query_data("Students_Courses".to_string(), None, None).await.unwrap();
|
||||
assert!(rows.rows.is_empty(), "junction rows should cascade-delete, got {:?}", rows.rows);
|
||||
});
|
||||
}
|
||||
@@ -249,7 +249,7 @@ fn create_m2n_is_one_undo_step() {
|
||||
let tables = db.list_tables().await.unwrap();
|
||||
assert!(!tables.contains(&"Students_Courses".to_string()), "undo should remove the junction: {tables:?}");
|
||||
// The parents' relationships are gone too (the junction held them).
|
||||
let students = db.describe_table("Students".to_string(), None).await.unwrap();
|
||||
let students = db.describe_table("Students".to_string()).await.unwrap();
|
||||
assert!(students.inbound_relationships.is_empty(), "no leftover relationship after undo");
|
||||
});
|
||||
}
|
||||
@@ -321,7 +321,7 @@ fn the_junction_can_be_renamed() {
|
||||
assert!(tables.contains(&"Enrollments".to_string()), "tables: {tables:?}");
|
||||
assert!(!tables.contains(&"Students_Courses".to_string()));
|
||||
// Both relationships survive the rename (rebuild-preserving).
|
||||
let desc = db.describe_table("Enrollments".to_string(), None).await.unwrap();
|
||||
let desc = db.describe_table("Enrollments".to_string()).await.unwrap();
|
||||
assert_eq!(desc.outbound_relationships.len(), 2, "FKs preserved across rename");
|
||||
});
|
||||
}
|
||||
@@ -362,7 +362,7 @@ fn junction_survives_save_and_rebuild() {
|
||||
db.rebuild_from_text(project.path().to_path_buf(), None).await.expect("rebuild");
|
||||
let tables = db.list_tables().await.unwrap();
|
||||
assert!(tables.contains(&"Students_Courses".to_string()), "junction survived: {tables:?}");
|
||||
let desc = db.describe_table("Students_Courses".to_string(), None).await.unwrap();
|
||||
let desc = db.describe_table("Students_Courses".to_string()).await.unwrap();
|
||||
assert_eq!(desc.outbound_relationships.len(), 2, "both FKs reconstructed");
|
||||
assert!(desc.columns.iter().all(|c| c.primary_key), "compound PK reconstructed");
|
||||
});
|
||||
|
||||
@@ -108,13 +108,13 @@ fn replay_runs_advanced_sql_create_table_as_a_write() {
|
||||
|
||||
// The SQL DDL line actually created the structural table…
|
||||
let desc = rt()
|
||||
.block_on(async { db.describe_table("Widget".to_string(), None).await })
|
||||
.block_on(async { db.describe_table("Widget".to_string()).await })
|
||||
.expect("describe");
|
||||
let names: Vec<String> = desc.columns.iter().map(|c| c.name.clone()).collect();
|
||||
assert_eq!(names, vec!["id".to_string(), "name".to_string()]);
|
||||
// …and the following insert (serial id auto-filled) ran against it.
|
||||
let rows = rt()
|
||||
.block_on(async { db.query_data("Widget".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("Widget".to_string(), None, None).await })
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 1);
|
||||
@@ -139,7 +139,7 @@ fn replay_three_lines_dispatches_three_commands() {
|
||||
|
||||
// The dispatched commands actually mutated state.
|
||||
let data_result = rt()
|
||||
.block_on(async { db.query_data("T".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("T".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert_eq!(data_result.rows.len(), 1, "row inserted");
|
||||
assert_eq!(data_result.rows[0][1].as_deref(), Some("Alice"));
|
||||
@@ -174,7 +174,7 @@ fn replay_of_actual_history_log_runs_ok_commands_and_skips_err() {
|
||||
assert_completed(&events, 3);
|
||||
|
||||
let data_result = rt()
|
||||
.block_on(async { db.query_data("T".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("T".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert_eq!(data_result.rows.len(), 1, "only the ok INSERT applied");
|
||||
assert_eq!(data_result.rows[0][1].as_deref(), Some("alpha"));
|
||||
@@ -227,7 +227,7 @@ fn replay_skips_app_lifecycle_commands_silently() {
|
||||
other => panic!("expected ReplayCompleted, got {other:?}"),
|
||||
}
|
||||
let data_result = rt()
|
||||
.block_on(async { db.query_data("T".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("T".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert!(
|
||||
data_result.columns.iter().any(|c| c == "v"),
|
||||
@@ -401,14 +401,14 @@ fn replay_aborts_on_first_parse_failure_and_reports_line() {
|
||||
// but earlier commands stayed applied (table T exists with
|
||||
// the `name` column).
|
||||
let desc = rt()
|
||||
.block_on(async { db.describe_table("T".to_string(), None).await })
|
||||
.block_on(async { db.describe_table("T".to_string()).await })
|
||||
.expect("describe_table");
|
||||
assert!(
|
||||
desc.columns.iter().any(|c| c.name == "name"),
|
||||
"earlier add column should have stayed applied"
|
||||
);
|
||||
let data_result = rt()
|
||||
.block_on(async { db.query_data("T".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("T".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert!(
|
||||
data_result.rows.is_empty(),
|
||||
@@ -467,7 +467,7 @@ fn replay_rejects_wrong_type_value_in_a_hand_built_script() {
|
||||
// The earlier two lines stayed applied; the failing insert
|
||||
// did not run — state is intact.
|
||||
let data_result = rt()
|
||||
.block_on(async { db.query_data("T".to_string(), None, None, None).await })
|
||||
.block_on(async { db.query_data("T".to_string(), None, None).await })
|
||||
.expect("query_data");
|
||||
assert!(
|
||||
data_result.rows.is_empty(),
|
||||
@@ -527,7 +527,7 @@ fn replay_skips_nested_replay_with_a_warning() {
|
||||
other => panic!("expected ReplayCompleted (nested replay skipped), got {other:?}"),
|
||||
}
|
||||
// The nested file's table was NOT created (the replay was skipped).
|
||||
let cols = rt().block_on(async { db.query_data("T".to_string(), None, None, None).await });
|
||||
let cols = rt().block_on(async { db.query_data("T".to_string(), None, None).await });
|
||||
assert!(cols.is_err(), "inner.commands' table T must not exist (nested replay skipped)");
|
||||
}
|
||||
|
||||
|
||||
@@ -462,7 +462,7 @@ fn app_show_table_renders_relationships_as_compact_diagrams() {
|
||||
rt.block_on(seed_schema(&db));
|
||||
// Orders holds the FK to Customers — an outbound relationship.
|
||||
let desc = rt
|
||||
.block_on(db.describe_table("Orders".to_string(), None))
|
||||
.block_on(db.describe_table("Orders".to_string()))
|
||||
.expect("describe Orders");
|
||||
|
||||
let mut app = App::new();
|
||||
|
||||
+17
-17
@@ -111,7 +111,7 @@ fn e2e_alter_drop_compound_primary_key_member_is_refused() {
|
||||
|
||||
/// The current user-facing type of column `name` in table `T`.
|
||||
fn col_type(db: &Database, r: &tokio::runtime::Runtime, name: &str) -> Option<Type> {
|
||||
r.block_on(db.describe_table("T".to_string(), None))
|
||||
r.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe")
|
||||
.columns
|
||||
.into_iter()
|
||||
@@ -120,7 +120,7 @@ fn col_type(db: &Database, r: &tokio::runtime::Runtime, name: &str) -> Option<Ty
|
||||
}
|
||||
|
||||
fn column_names(db: &Database, r: &tokio::runtime::Runtime) -> Vec<String> {
|
||||
r.block_on(db.describe_table("T".to_string(), None))
|
||||
r.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe")
|
||||
.columns
|
||||
.into_iter()
|
||||
@@ -163,7 +163,7 @@ fn e2e_alter_table_add_rename_drop_and_raw_default_check() {
|
||||
|
||||
// The DEFAULT backfilled the pre-existing row to qty = 0.
|
||||
let rows = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 1);
|
||||
@@ -252,7 +252,7 @@ fn e2e_alter_column_type_clean_and_lossy_convert() {
|
||||
}
|
||||
|
||||
let rows = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 1);
|
||||
@@ -292,7 +292,7 @@ fn e2e_alter_column_type_int_to_serial_is_allowed() {
|
||||
}
|
||||
assert_eq!(col_type(&db, &r, "n"), Some(Type::Serial), "int→serial converted the column");
|
||||
let rows = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows[0][1].as_deref(), Some("100"), "the existing value is preserved");
|
||||
@@ -635,7 +635,7 @@ fn e2e_drop_composite_unique_is_one_undo_step() {
|
||||
.expect("write");
|
||||
r.block_on(run_replay(&db, project.path(), "u.commands"));
|
||||
let has_unique = || {
|
||||
!r.block_on(db.describe_table("T".to_string(), None))
|
||||
!r.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe")
|
||||
.unique_constraints
|
||||
.is_empty()
|
||||
@@ -878,7 +878,7 @@ fn e2e_describe_shows_table_level_constraints() {
|
||||
"events: {events:?}"
|
||||
);
|
||||
|
||||
let desc = r.block_on(db.describe_table("T".to_string(), None)).expect("describe");
|
||||
let desc = r.block_on(db.describe_table("T".to_string())).expect("describe");
|
||||
assert_eq!(
|
||||
desc.unique_constraints,
|
||||
vec![vec!["a".to_string(), "b".to_string()]],
|
||||
@@ -976,7 +976,7 @@ fn e2e_rename_table_with_rows_csv_follows_and_survives_rebuild() {
|
||||
assert!(!csv_path(&project, "Orders").exists(), "data/Orders.csv removed");
|
||||
|
||||
let rows = r
|
||||
.block_on(db.query_data("Purchases".to_string(), None, None, None))
|
||||
.block_on(db.query_data("Purchases".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 2);
|
||||
@@ -991,7 +991,7 @@ fn e2e_rename_table_with_rows_csv_follows_and_survives_rebuild() {
|
||||
"Purchases round-tripped through a fresh rebuild: {tables:?}"
|
||||
);
|
||||
let rows = r
|
||||
.block_on(db.query_data("Purchases".to_string(), None, None, None))
|
||||
.block_on(db.query_data("Purchases".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 2);
|
||||
@@ -1077,7 +1077,7 @@ fn e2e_rename_fk_parent_updates_metadata_and_still_enforces() {
|
||||
);
|
||||
|
||||
// The child's outbound relationship now points at the new parent name.
|
||||
let c = r.block_on(db.describe_table("C".to_string(), None)).expect("describe C");
|
||||
let c = r.block_on(db.describe_table("C".to_string())).expect("describe C");
|
||||
assert_eq!(c.outbound_relationships.len(), 1);
|
||||
assert_eq!(c.outbound_relationships[0].other_table, "Parent");
|
||||
|
||||
@@ -1129,7 +1129,7 @@ fn e2e_rename_fk_child_updates_metadata_and_still_enforces() {
|
||||
);
|
||||
|
||||
// The parent's inbound relationship now names the renamed child.
|
||||
let p = r.block_on(db.describe_table("P".to_string(), None)).expect("describe P");
|
||||
let p = r.block_on(db.describe_table("P".to_string())).expect("describe P");
|
||||
assert_eq!(p.inbound_relationships.len(), 1);
|
||||
assert_eq!(p.inbound_relationships[0].other_table, "Child");
|
||||
|
||||
@@ -1168,7 +1168,7 @@ fn e2e_rename_self_referential_table_updates_both_ends() {
|
||||
);
|
||||
|
||||
// Both ends of the self-reference now name `Tree`.
|
||||
let t = r.block_on(db.describe_table("Tree".to_string(), None)).expect("describe Tree");
|
||||
let t = r.block_on(db.describe_table("Tree".to_string())).expect("describe Tree");
|
||||
assert_eq!(t.outbound_relationships[0].other_table, "Tree");
|
||||
assert_eq!(t.inbound_relationships[0].other_table, "Tree");
|
||||
|
||||
@@ -1216,7 +1216,7 @@ fn e2e_rename_table_keeps_its_index_with_a_stale_name() {
|
||||
"events: {events:?}"
|
||||
);
|
||||
|
||||
let u = r.block_on(db.describe_table("Users".to_string(), None)).expect("describe Users");
|
||||
let u = r.block_on(db.describe_table("Users".to_string())).expect("describe Users");
|
||||
assert_eq!(u.indexes.len(), 1, "the index followed the rename");
|
||||
assert_eq!(
|
||||
u.indexes[0].name, "T_email_idx",
|
||||
@@ -1226,7 +1226,7 @@ fn e2e_rename_table_keeps_its_index_with_a_stale_name() {
|
||||
|
||||
// Survives a fresh rebuild (recreated from IndexSchema on table Users).
|
||||
let db = fresh_rebuild(db, &project, &r);
|
||||
let u = r.block_on(db.describe_table("Users".to_string(), None)).expect("describe Users");
|
||||
let u = r.block_on(db.describe_table("Users".to_string())).expect("describe Users");
|
||||
assert_eq!(u.indexes.len(), 1);
|
||||
assert_eq!(u.indexes[0].name, "T_email_idx");
|
||||
}
|
||||
@@ -1255,7 +1255,7 @@ fn e2e_rename_table_is_one_undo_step() {
|
||||
"undo restored the old table name: {tables:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
r.block_on(db.query_data("Orders".to_string(), None, None, None)).expect("query").rows.len(),
|
||||
r.block_on(db.query_data("Orders".to_string(), None, None)).expect("query").rows.len(),
|
||||
1,
|
||||
"the row is back under the old name"
|
||||
);
|
||||
@@ -1427,7 +1427,7 @@ fn e2e_alter_column_set_default_applies() {
|
||||
))
|
||||
.expect("insert omitting qty");
|
||||
let rows = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(
|
||||
@@ -1473,7 +1473,7 @@ fn e2e_alter_column_drop_default_removes_it() {
|
||||
))
|
||||
.expect("insert omitting qty");
|
||||
let rows = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(
|
||||
|
||||
@@ -55,7 +55,7 @@ fn insert_row(db: &Database, r: &tokio::runtime::Runtime, id: i64, email: &str)
|
||||
}
|
||||
|
||||
fn index(db: &Database, r: &tokio::runtime::Runtime, name: &str) -> Option<(Vec<String>, bool)> {
|
||||
r.block_on(db.describe_table("T".to_string(), None))
|
||||
r.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe")
|
||||
.indexes
|
||||
.into_iter()
|
||||
|
||||
@@ -64,7 +64,7 @@ fn created_table_appears_with_playground_types() {
|
||||
assert!(tables.contains(&"Widget".to_string()));
|
||||
|
||||
let desc = r
|
||||
.block_on(db.describe_table("Widget".to_string(), None))
|
||||
.block_on(db.describe_table("Widget".to_string()))
|
||||
.expect("describe");
|
||||
let types: Vec<(String, Option<Type>)> = desc
|
||||
.columns
|
||||
@@ -98,7 +98,7 @@ fn integer_primary_key_is_plain_int() {
|
||||
))
|
||||
.expect("create");
|
||||
let desc = r
|
||||
.block_on(db.describe_table("T".to_string(), None))
|
||||
.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe");
|
||||
assert_eq!(desc.columns[0].user_type, Some(Type::Int));
|
||||
}
|
||||
@@ -137,7 +137,7 @@ fn serial_pk_autoincrements_in_multi_column_table() {
|
||||
}
|
||||
|
||||
let data = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query");
|
||||
let id_idx = data
|
||||
.columns
|
||||
@@ -220,7 +220,7 @@ fn table_without_primary_key_is_allowed() {
|
||||
))
|
||||
.expect("insert into PK-less table");
|
||||
let data = r
|
||||
.block_on(db.query_data("Notes".to_string(), None, None, None))
|
||||
.block_on(db.query_data("Notes".to_string(), None, None))
|
||||
.expect("query");
|
||||
assert_eq!(data.rows.len(), 1);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ fn default_is_applied_when_column_omitted() {
|
||||
))
|
||||
.expect("insert");
|
||||
let data = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query");
|
||||
let n_idx = data.columns.iter().position(|c| c == "n").expect("n column");
|
||||
assert_eq!(data.rows[0][n_idx].as_deref(), Some("7"), "DEFAULT 7 applied");
|
||||
@@ -381,7 +381,7 @@ fn check_default_and_composite_unique_survive_rebuild() {
|
||||
// A valid row inserts; DEFAULT n=7 survived.
|
||||
r.block_on(ins("1", "1", "5")).expect("valid row");
|
||||
let data = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query");
|
||||
let n_idx = data.columns.iter().position(|c| c == "n").expect("n column");
|
||||
assert_eq!(data.rows[0][n_idx].as_deref(), Some("7"), "DEFAULT survived rebuild");
|
||||
@@ -679,7 +679,7 @@ fn sql_create_table_is_one_undo_step() {
|
||||
/// Sorted `id` column values of table `T`.
|
||||
fn ids(db: &Database, r: &tokio::runtime::Runtime) -> Vec<Option<String>> {
|
||||
let d = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query");
|
||||
let idx = d.columns.iter().position(|c| c == "id").expect("id column");
|
||||
let mut v: Vec<Option<String>> = d.rows.iter().map(|row| row[idx].clone()).collect();
|
||||
@@ -801,7 +801,7 @@ fn dropping_a_column_a_table_check_references_fails_cleanly() {
|
||||
|
||||
// The table is intact: both columns survive (rollback) ...
|
||||
let desc = r
|
||||
.block_on(db.describe_table("T".to_string(), None))
|
||||
.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe still works");
|
||||
assert_eq!(
|
||||
desc.columns.iter().map(|c| c.name.clone()).collect::<Vec<_>>(),
|
||||
@@ -925,14 +925,14 @@ fn foreign_key_creates_named_relationship_visible_in_describe() {
|
||||
.expect("create child with FK");
|
||||
|
||||
// The child has an outbound relationship; the parent an inbound one.
|
||||
let child = r.block_on(db.describe_table("child".to_string(), None)).expect("describe child");
|
||||
let child = r.block_on(db.describe_table("child".to_string())).expect("describe child");
|
||||
assert_eq!(child.outbound_relationships.len(), 1, "child references parent");
|
||||
let rel = &child.outbound_relationships[0];
|
||||
assert_eq!(rel.name, "parent_id_to_child_pid", "auto-named per ADR-0013");
|
||||
assert_eq!(rel.other_table, "parent");
|
||||
assert_eq!(rel.local_columns, vec!["pid".to_string()]);
|
||||
|
||||
let parent = r.block_on(db.describe_table("parent".to_string(), None)).expect("describe parent");
|
||||
let parent = r.block_on(db.describe_table("parent".to_string())).expect("describe parent");
|
||||
assert_eq!(parent.inbound_relationships.len(), 1, "parent is referenced by child");
|
||||
}
|
||||
|
||||
@@ -954,7 +954,7 @@ fn explicit_constraint_name_is_used() {
|
||||
Some("create table child (id serial primary key, pid int, constraint child_to_parent foreign key (pid) references parent(id))".to_string()),
|
||||
))
|
||||
.expect("create child with named FK");
|
||||
let child = r.block_on(db.describe_table("child".to_string(), None)).expect("describe");
|
||||
let child = r.block_on(db.describe_table("child".to_string())).expect("describe");
|
||||
assert_eq!(child.outbound_relationships[0].name, "child_to_parent");
|
||||
}
|
||||
|
||||
@@ -974,7 +974,7 @@ fn bare_references_resolves_to_parent_single_column_pk() {
|
||||
Some("create table child (id serial primary key, pid int references parent)".to_string()),
|
||||
))
|
||||
.expect("create child with bare REFERENCES");
|
||||
let child = r.block_on(db.describe_table("child".to_string(), None)).expect("describe");
|
||||
let child = r.block_on(db.describe_table("child".to_string())).expect("describe");
|
||||
assert_eq!(child.outbound_relationships[0].other_columns, vec!["id".to_string()], "resolved to parent PK");
|
||||
}
|
||||
|
||||
@@ -1108,7 +1108,7 @@ fn create_table_with_fk_is_one_undo_step() {
|
||||
// parent (now un-referenced) can be described without a dangling rel.
|
||||
r.block_on(db.undo()).expect("undo").expect("a step was undone");
|
||||
assert!(!r.block_on(db.list_tables()).unwrap().contains(&"child".to_string()));
|
||||
let parent = r.block_on(db.describe_table("parent".to_string(), None)).expect("describe parent");
|
||||
let parent = r.block_on(db.describe_table("parent".to_string())).expect("describe parent");
|
||||
assert!(parent.inbound_relationships.is_empty(), "the relationship was undone with the table");
|
||||
}
|
||||
|
||||
@@ -1152,7 +1152,7 @@ fn foreign_key_on_delete_cascade_takes_effect() {
|
||||
))
|
||||
.expect("delete parent");
|
||||
let child_rows = r
|
||||
.block_on(db.query_data("child".to_string(), None, None, None))
|
||||
.block_on(db.query_data("child".to_string(), None, None))
|
||||
.expect("query child");
|
||||
assert!(child_rows.rows.is_empty(), "ON DELETE CASCADE removed the child row");
|
||||
}
|
||||
@@ -1232,7 +1232,7 @@ fn fk_survives_a_rebuild_triggering_column_add() {
|
||||
.expect("add column via rebuild");
|
||||
|
||||
// The relationship still exists after the rebuild.
|
||||
let child = r.block_on(db.describe_table("child".to_string(), None)).expect("describe");
|
||||
let child = r.block_on(db.describe_table("child".to_string())).expect("describe");
|
||||
assert_eq!(child.outbound_relationships.len(), 1, "FK survived the column-add rebuild");
|
||||
// And the engine still enforces it (now and after a fresh rebuild).
|
||||
insert_parent_row(&db, &r);
|
||||
@@ -1275,7 +1275,7 @@ fn fk_referential_actions_survive_rebuild() {
|
||||
))
|
||||
.expect("create");
|
||||
r.block_on(db.rebuild_from_text(p.path().to_path_buf(), None)).expect("rebuild");
|
||||
let child = r.block_on(db.describe_table("child".to_string(), None)).expect("describe");
|
||||
let child = r.block_on(db.describe_table("child".to_string())).expect("describe");
|
||||
let rel = &child.outbound_relationships[0];
|
||||
assert_eq!(rel.on_delete, ReferentialAction::Cascade, "ON DELETE survived rebuild");
|
||||
assert_eq!(rel.on_update, ReferentialAction::SetNull, "ON UPDATE survived rebuild");
|
||||
@@ -1299,7 +1299,7 @@ fn dropping_the_child_clears_the_fk_relationship() {
|
||||
.expect("create");
|
||||
r.block_on(db.drop_table("child".to_string(), Some("drop table child".to_string())))
|
||||
.expect("drop child");
|
||||
let parent = r.block_on(db.describe_table("parent".to_string(), None)).expect("describe parent");
|
||||
let parent = r.block_on(db.describe_table("parent".to_string())).expect("describe parent");
|
||||
assert!(parent.inbound_relationships.is_empty(), "dropping the child cleared the relationship");
|
||||
}
|
||||
|
||||
@@ -1341,7 +1341,7 @@ fn bare_self_reference_resolves_to_own_pk() {
|
||||
Some("create table emp (id int primary key, mgr int references emp)".to_string()),
|
||||
))
|
||||
.expect("create self-referential emp with a bare reference");
|
||||
let emp = r.block_on(db.describe_table("emp".to_string(), None)).expect("describe");
|
||||
let emp = r.block_on(db.describe_table("emp".to_string())).expect("describe");
|
||||
assert_eq!(emp.outbound_relationships[0].other_columns, vec!["id".to_string()], "bare self-ref resolved to own PK");
|
||||
// Enforced: a non-existent manager is rejected.
|
||||
r.block_on(db.insert(
|
||||
|
||||
@@ -154,7 +154,7 @@ fn delete_without_where_runs_across_all_rows() {
|
||||
let csv = read_csv(&project, "t").unwrap_or_default();
|
||||
assert!(!csv.contains('a') && !csv.contains('b') && !csv.contains('c'), "no rows left: {csv:?}");
|
||||
let remaining = rt
|
||||
.block_on(db.query_data("t".to_string(), None, None, None))
|
||||
.block_on(db.query_data("t".to_string(), None, None))
|
||||
.expect("query t");
|
||||
assert!(remaining.rows.is_empty(), "table empty after unfiltered delete");
|
||||
}
|
||||
@@ -302,8 +302,8 @@ fn cascade_to_two_children_reports_both() {
|
||||
assert_eq!(by_child.get("Orders"), Some(&2), "two orders cascaded");
|
||||
assert_eq!(by_child.get("Reviews"), Some(&1), "one review cascaded");
|
||||
// Both child CSVs re-persisted to the post-cascade (empty) state.
|
||||
let orders = rt.block_on(db.query_data("Orders".to_string(), None, None, None)).unwrap();
|
||||
let reviews = rt.block_on(db.query_data("Reviews".to_string(), None, None, None)).unwrap();
|
||||
let orders = rt.block_on(db.query_data("Orders".to_string(), None, None)).unwrap();
|
||||
let reviews = rt.block_on(db.query_data("Reviews".to_string(), None, None)).unwrap();
|
||||
assert!(orders.rows.is_empty() && reviews.rows.is_empty(), "both children emptied");
|
||||
let _ = &project;
|
||||
}
|
||||
@@ -361,7 +361,7 @@ fn delete_violating_fk_fails_and_persists_nothing() {
|
||||
let result = run_delete(&db, &rt, input);
|
||||
assert!(result.is_err(), "delete of a referenced parent must be rejected");
|
||||
// Rolled back: Alice survives.
|
||||
let customers = rt.block_on(db.query_data("Customers".to_string(), None, None, None)).unwrap();
|
||||
let customers = rt.block_on(db.query_data("Customers".to_string(), None, None)).unwrap();
|
||||
assert_eq!(customers.rows.len(), 1, "parent row preserved after rejected delete");
|
||||
// No history line for the failed statement (written only on success).
|
||||
let history = std::fs::read_to_string(project.path().join("history.log")).unwrap_or_default();
|
||||
|
||||
@@ -149,7 +149,7 @@ fn seed(db: &Database, rt: &tokio::runtime::Runtime, sql: &str) {
|
||||
}
|
||||
|
||||
fn query(db: &Database, rt: &tokio::runtime::Runtime, table: &str) -> Vec<Vec<Option<String>>> {
|
||||
rt.block_on(db.query_data(table.to_string(), None, None, None))
|
||||
rt.block_on(db.query_data(table.to_string(), None, None))
|
||||
.unwrap_or_else(|e| panic!("query_data {table}: {e:?}"))
|
||||
.rows
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ fn make_t_with_index(db: &Database, r: &tokio::runtime::Runtime) -> String {
|
||||
}
|
||||
|
||||
fn index_names(db: &Database, r: &tokio::runtime::Runtime) -> Vec<String> {
|
||||
r.block_on(db.describe_table("T".to_string(), None))
|
||||
r.block_on(db.describe_table("T".to_string()))
|
||||
.expect("describe")
|
||||
.indexes
|
||||
.into_iter()
|
||||
|
||||
@@ -150,7 +150,7 @@ fn drop_table_is_one_undo_step_and_restores_data() {
|
||||
assert!(r.block_on(db.undo()).expect("undo").is_some(), "the drop was one undo step");
|
||||
assert!(r.block_on(db.list_tables()).unwrap().contains(&"T".to_string()));
|
||||
let data = r
|
||||
.block_on(db.query_data("T".to_string(), None, None, None))
|
||||
.block_on(db.query_data("T".to_string(), None, None))
|
||||
.expect("query");
|
||||
assert_eq!(data.rows.len(), 1, "the dropped row was restored by undo");
|
||||
}
|
||||
|
||||
+10
-16
@@ -215,7 +215,7 @@ fn decimal_aggregation_display_trims_ieee754_noise() {
|
||||
|
||||
// The reported case: the aggregate no longer leaks float noise.
|
||||
let agg = rt
|
||||
.block_on(db.run_select("select sum(price * qty) from Products".to_string(), None))
|
||||
.block_on(db.run_select("select sum(price * qty) from Products".to_string()))
|
||||
.expect("aggregate select");
|
||||
assert_eq!(
|
||||
agg.rows[0][0].as_deref(),
|
||||
@@ -226,7 +226,7 @@ fn decimal_aggregation_display_trims_ieee754_noise() {
|
||||
// Raw decimal column is still exact — TEXT storage preserves
|
||||
// the input string verbatim, including the trailing zero.
|
||||
let raw = rt
|
||||
.block_on(db.run_select("select price from Products".to_string(), None))
|
||||
.block_on(db.run_select("select price from Products".to_string()))
|
||||
.expect("raw decimal select");
|
||||
let prices: Vec<&str> = raw.rows.iter().map(|r| r[0].as_deref().unwrap()).collect();
|
||||
assert_eq!(
|
||||
@@ -240,10 +240,7 @@ fn decimal_aggregation_display_trims_ieee754_noise() {
|
||||
fn database_run_select_constant_returns_a_single_row() {
|
||||
let (_p, db, _dir) = open_project_db();
|
||||
let data = rt()
|
||||
.block_on(db.run_select(
|
||||
"select 1".to_string(),
|
||||
Some("select 1".to_string()),
|
||||
))
|
||||
.block_on(db.run_select("select 1".to_string()))
|
||||
.expect("`select 1` runs clean");
|
||||
assert_eq!(data.rows.len(), 1, "one result row");
|
||||
assert_eq!(data.rows[0].len(), 1, "one column");
|
||||
@@ -288,7 +285,7 @@ fn database_run_select_from_user_table_returns_inserted_rows() {
|
||||
.expect("insert row");
|
||||
});
|
||||
let data = rt
|
||||
.block_on(db.run_select("select Name from T".to_string(), None))
|
||||
.block_on(db.run_select("select Name from T".to_string()))
|
||||
.expect("SELECT runs");
|
||||
assert_eq!(data.rows.len(), 1);
|
||||
assert_eq!(data.rows[0][0].as_deref(), Some("Ada"));
|
||||
@@ -336,7 +333,7 @@ fn database_run_select_recovers_bool_column_type() {
|
||||
.expect("insert row");
|
||||
});
|
||||
let data = rt
|
||||
.block_on(db.run_select("select Active from Products".to_string(), None))
|
||||
.block_on(db.run_select("select Active from Products".to_string()))
|
||||
.expect("SELECT runs");
|
||||
assert_eq!(data.rows.len(), 2);
|
||||
assert_eq!(data.column_types, vec![Some(Type::Bool)]);
|
||||
@@ -374,7 +371,7 @@ fn database_run_select_recovers_text_type_through_alias() {
|
||||
// playground type is recovered.
|
||||
let data = rt
|
||||
.block_on(
|
||||
db.run_select("select Name as n from Users".to_string(), None),
|
||||
db.run_select("select Name as n from Users".to_string()),
|
||||
)
|
||||
.expect("SELECT runs");
|
||||
assert_eq!(data.columns, vec!["n".to_string()]);
|
||||
@@ -402,7 +399,7 @@ fn database_run_select_computed_expression_stays_typeless() {
|
||||
.expect("insert");
|
||||
});
|
||||
let data = rt
|
||||
.block_on(db.run_select("select Score + 1 from T".to_string(), None))
|
||||
.block_on(db.run_select("select Score + 1 from T".to_string()))
|
||||
.expect("SELECT runs");
|
||||
assert_eq!(data.column_types, vec![None]);
|
||||
}
|
||||
@@ -439,7 +436,6 @@ fn engine_aggregate_in_where_routes_through_catalog() {
|
||||
let err = rt
|
||||
.block_on(db.run_select(
|
||||
"select id from T where count(score) > 0".to_string(),
|
||||
None,
|
||||
))
|
||||
.expect_err("engine should reject aggregate in WHERE");
|
||||
let DbError::Sqlite { .. } = &err else {
|
||||
@@ -512,7 +508,6 @@ fn engine_group_by_missing_routes_through_catalog() {
|
||||
let _ = rt
|
||||
.block_on(db.run_select(
|
||||
"select category, count(*) from T group by category".to_string(),
|
||||
None,
|
||||
))
|
||||
.expect("benign GROUP BY query runs");
|
||||
// Direct unit test on the matcher: ensure a message that
|
||||
@@ -574,7 +569,6 @@ fn engine_scalar_subquery_too_many_rows_routes_through_catalog() {
|
||||
let _ = rt
|
||||
.block_on(db.run_select(
|
||||
"select (select v from T) from T".to_string(),
|
||||
None,
|
||||
))
|
||||
.expect("benign scalar subquery query runs");
|
||||
let synthetic = DbError::Sqlite {
|
||||
@@ -624,13 +618,13 @@ fn database_run_select_type_recovery_works_on_empty_table() {
|
||||
});
|
||||
// No INSERT — the table is empty.
|
||||
let data_text = rt
|
||||
.block_on(db.run_select("select col_text from Empty".to_string(), None))
|
||||
.block_on(db.run_select("select col_text from Empty".to_string()))
|
||||
.expect("SELECT runs even on empty table");
|
||||
assert!(data_text.rows.is_empty());
|
||||
assert_eq!(data_text.column_types, vec![Some(Type::Text)]);
|
||||
|
||||
let data_blob = rt
|
||||
.block_on(db.run_select("select col_blob from Empty".to_string(), None))
|
||||
.block_on(db.run_select("select col_blob from Empty".to_string()))
|
||||
.expect("SELECT runs even on empty table");
|
||||
assert!(data_blob.rows.is_empty());
|
||||
assert_eq!(
|
||||
@@ -723,7 +717,7 @@ fn database_run_select_recovers_all_ten_playground_types() {
|
||||
for (col, expected_type) in cases {
|
||||
let sql = format!("select {col} from AllTypes");
|
||||
let data = rt
|
||||
.block_on(db.run_select(sql.clone(), None))
|
||||
.block_on(db.run_select(sql.clone()))
|
||||
.expect("SELECT runs");
|
||||
assert_eq!(
|
||||
data.column_types,
|
||||
|
||||
@@ -501,7 +501,7 @@ fn update_all_rows_flag_in_advanced_updates_every_row() {
|
||||
"the --all-rows update replays through the DSL fall-back; events: {events:?}"
|
||||
);
|
||||
let rows = rt
|
||||
.block_on(db.query_data("t".to_string(), None, None, None))
|
||||
.block_on(db.query_data("t".to_string(), None, None))
|
||||
.expect("query")
|
||||
.rows;
|
||||
assert_eq!(rows.len(), 2, "both rows present");
|
||||
|
||||
@@ -63,7 +63,7 @@ async fn insert_named(db: &Database, name: &str) {
|
||||
}
|
||||
|
||||
async fn row_count(db: &Database) -> usize {
|
||||
db.query_data("Customers".to_string(), None, None, None)
|
||||
db.query_data("Customers".to_string(), None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.rows
|
||||
@@ -306,7 +306,7 @@ async fn sql_delete(db: &Database, input: &str) {
|
||||
}
|
||||
|
||||
async fn count_t(db: &Database) -> usize {
|
||||
db.query_data("T".to_string(), None, None, None)
|
||||
db.query_data("T".to_string(), None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.rows
|
||||
@@ -378,7 +378,7 @@ fn undo_restores_db_and_csv_consistently() {
|
||||
// Both the database read model and the on-disk CSV are
|
||||
// restored — the (db, csv) pair stays consistent.
|
||||
assert_eq!(
|
||||
db.query_data("T".to_string(), None, None, None)
|
||||
db.query_data("T".to_string(), None, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.rows
|
||||
|
||||
Reference in New Issue
Block a user