Skip to content

Commit

Permalink
Merge pull request #56 from ginkgobioworks/reset-command
Browse files Browse the repository at this point in the history
Reset command
  • Loading branch information
Chris7 authored Sep 28, 2024
2 parents c5e2da6 + c626b65 commit 2dbb2d4
Show file tree
Hide file tree
Showing 9 changed files with 736 additions and 91 deletions.
7 changes: 7 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ To list all available branches, `gen --db db_name.db branch --list`. The current
To checkout a branch, `gen --db db_name.db branch --checkout branch_name`. This will migrate the database to the last change
applied in a given branch.

# Reset

This will revert a branch to a given operation id and detach operations made beneath this operation id. This should be
done when work after a given point is no longer desired and you wish to start at a fresh point in the branch.

To reset the database to a given operation, run the command `gen --db db_name.db reset operation_id`.

# Operations

Operations are changes that have been made to the database. Commands such as `import` and `update` create a new operation.
Expand Down
9 changes: 9 additions & 0 deletions migrations/operations/01-initial/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,12 @@ CREATE TABLE branch (
FOREIGN KEY(current_operation_id) REFERENCES operation(id)
) STRICT;
CREATE UNIQUE INDEX branch_uidx ON branch(db_uuid, name);

CREATE TABLE branch_masked_operations (
id INTEGER PRIMARY KEY NOT NULL,
branch_id INTEGER NOT NULL,
operation_id INTEGER NOT NULL,
FOREIGN KEY(branch_id) REFERENCES branch(id),
FOREIGN KEY(operation_id) REFERENCES operation(id)
) STRICT;
CREATE UNIQUE INDEX branch_mask_op_uidx ON branch_masked_operations(branch_id, operation_id);
122 changes: 63 additions & 59 deletions src/imports/fasta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,68 +34,72 @@ pub fn import_fasta(
change.id,
);

if !Collection::exists(conn, name) {
let collection = Collection::create(conn, name);
let mut summary: HashMap<String, i32> = HashMap::new();
let mut collection;

for result in reader.records() {
let record = result.expect("Error during fasta record parsing");
let sequence = str::from_utf8(record.sequence().as_ref())
.unwrap()
.to_string();
let name = String::from_utf8(record.name().to_vec()).unwrap();
let sequence_length = record.sequence().len() as i32;
let seq = if shallow {
Sequence::new()
.sequence_type("DNA")
.name(&name)
.file_path(fasta)
.save(conn)
} else {
Sequence::new()
.sequence_type("DNA")
.sequence(&sequence)
.save(conn)
};
let block_group = BlockGroup::create(conn, &collection.name, None, &name);
let edge_into = Edge::create(
conn,
Sequence::PATH_START_HASH.to_string(),
0,
Strand::Forward,
seq.hash.to_string(),
0,
Strand::Forward,
0,
0,
);
let edge_out_of = Edge::create(
conn,
seq.hash.to_string(),
sequence_length,
Strand::Forward,
Sequence::PATH_END_HASH.to_string(),
0,
Strand::Forward,
0,
0,
);
BlockGroupEdge::bulk_create(conn, block_group.id, &[edge_into.id, edge_out_of.id]);
let path = Path::create(conn, &name, block_group.id, &[edge_into.id, edge_out_of.id]);
summary.entry(path.name).or_insert(sequence_length);
}
let mut summary_str = "".to_string();
for (path_name, change_count) in summary.iter() {
summary_str.push_str(&format!(" {path_name}: {change_count} changes.\n"));
}
OperationSummary::create(operation_conn, operation.id, &summary_str);
println!("Created it");
let mut output = Vec::new();
session.changeset_strm(&mut output).unwrap();
operation_management::write_changeset(conn, &operation, &output);
if !Collection::exists(conn, name) {
collection = Collection::create(conn, name);
} else {
println!("Collection {:1} already exists", name);
collection = Collection {
name: name.to_string(),
};
}
let mut summary: HashMap<String, i32> = HashMap::new();

for result in reader.records() {
let record = result.expect("Error during fasta record parsing");
let sequence = str::from_utf8(record.sequence().as_ref())
.unwrap()
.to_string();
let name = String::from_utf8(record.name().to_vec()).unwrap();
let sequence_length = record.sequence().len() as i32;
let seq = if shallow {
Sequence::new()
.sequence_type("DNA")
.name(&name)
.file_path(fasta)
.save(conn)
} else {
Sequence::new()
.sequence_type("DNA")
.sequence(&sequence)
.save(conn)
};
let block_group = BlockGroup::create(conn, &collection.name, None, &name);
let edge_into = Edge::create(
conn,
Sequence::PATH_START_HASH.to_string(),
0,
Strand::Forward,
seq.hash.to_string(),
0,
Strand::Forward,
0,
0,
);
let edge_out_of = Edge::create(
conn,
seq.hash.to_string(),
sequence_length,
Strand::Forward,
Sequence::PATH_END_HASH.to_string(),
0,
Strand::Forward,
0,
0,
);
BlockGroupEdge::bulk_create(conn, block_group.id, &[edge_into.id, edge_out_of.id]);
let path = Path::create(conn, &name, block_group.id, &[edge_into.id, edge_out_of.id]);
summary.entry(path.name).or_insert(sequence_length);
}
let mut summary_str = "".to_string();
for (path_name, change_count) in summary.iter() {
summary_str.push_str(&format!(" {path_name}: {change_count} changes.\n"));
}
OperationSummary::create(operation_conn, operation.id, &summary_str);
println!("Created it");
let mut output = Vec::new();
session.changeset_strm(&mut output).unwrap();
operation_management::write_changeset(conn, &operation, &output);
}

#[cfg(test)]
Expand Down
8 changes: 8 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ enum Commands {
#[clap(index = 1)]
id: Option<i32>,
},
Reset {
/// The operation id to reset to
#[clap(index = 1)]
id: i32,
},
/// View operations carried out against a database
Operations {
/// The branch to list operations for
Expand Down Expand Up @@ -287,6 +292,9 @@ fn main() {
Some(Commands::Checkout { branch, id }) => {
operation_management::checkout(&conn, &operation_conn, &db_uuid, branch, *id);
}
Some(Commands::Reset { id }) => {
operation_management::reset(&conn, &operation_conn, &db_uuid, *id);
}
Some(Commands::Export { name, gfa }) => {
conn.execute("BEGIN TRANSACTION", []).unwrap();
export_gfa(&conn, name, &PathBuf::from(gfa));
Expand Down
2 changes: 1 addition & 1 deletion src/models/block_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl BlockGroup {
None => {
conn
.query_row(
"select id from block_group where collection_name = ?1 and sample_name is null and name = ?3",
"select id from block_group where collection_name = ?1 and sample_name is null and name = ?2",
(collection_name, name),
|row| row.get(0),
)
Expand Down
27 changes: 22 additions & 5 deletions src/models/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,29 @@ impl Collection {

pub fn create(conn: &Connection, name: &str) -> Collection {
let mut stmt = conn
.prepare("INSERT INTO collection (name) VALUES (?1) RETURNING *")
.prepare("INSERT INTO collection (name) VALUES (?1) RETURNING *;")
.unwrap();
let mut rows = stmt
.query_map((name,), |row| Ok(Collection { name: row.get(0)? }))
.unwrap();
rows.next().unwrap().unwrap()

match stmt.query_row((name,), |row| {
Ok(Collection {
name: name.to_string(),
})
}) {
Ok(res) => res,
Err(rusqlite::Error::SqliteFailure(err, details)) => {
if err.code == rusqlite::ErrorCode::ConstraintViolation {
Collection {
name: name.to_string(),
}
} else {
panic!("something bad happened querying the database")
}
}
Err(err) => {
println!("{err:?}");
panic!("something bad happened querying the database")
}
}
}

pub fn bulk_create(conn: &Connection, names: &Vec<String>) -> Vec<Collection> {
Expand Down
Loading

0 comments on commit 2dbb2d4

Please sign in to comment.