holochain_data 0.7.0-dev.21

Database abstraction layer for Holochain using sqlx
Documentation
use crate::models::wasm::{CoordinatorZomeModel, DnaDefModel, EntryDefModel, IntegrityZomeModel};
use holo_hash::{AgentPubKey, HashableContentExtSync, WasmHash};
use holochain_types::prelude::{DnaDef, DnaWasmHashed, EntryDef, WasmZomeDef, ZomeDef};
use holochain_zome_types::zome::InlineZomeDef;
use sqlx::{Acquire, Executor, Sqlite};

use super::inner_writes;

/// Store WASM bytecode.
pub(super) async fn put_wasm<'e, E>(executor: E, wasm: DnaWasmHashed) -> sqlx::Result<()>
where
    E: Executor<'e, Database = Sqlite>,
{
    let (wasm, hash) = wasm.into_inner();
    inner_writes::put_wasm(executor, hash.get_raw_39(), &wasm.code).await
}

/// Store a compiled, serialized WASM module.
pub(super) async fn put_compiled_wasm<'e, E>(
    executor: E,
    wasm_hash: WasmHash,
    wasm_serialized: &[u8],
) -> sqlx::Result<()>
where
    E: Executor<'e, Database = Sqlite>,
{
    inner_writes::put_compiled_wasm(executor, wasm_hash.get_raw_39(), wasm_serialized).await
}

/// Store a DNA definition and its associated zomes.
///
/// This operation is transactional — either all data is stored or none is.
/// When called with `&Pool`, a new transaction is started. When called with
/// `&mut Transaction`, a savepoint is used so the outer transaction remains
/// in control.
pub(super) async fn put_dna_def<'c, A>(
    conn: A,
    agent: &AgentPubKey,
    dna_def: &DnaDef,
) -> sqlx::Result<()>
where
    A: Acquire<'c, Database = Sqlite>,
{
    let mut tx = conn.begin().await?;

    let hash = dna_def.to_hash();
    let hash_bytes = hash.get_raw_32();
    let agent_bytes = agent.get_raw_32();

    #[cfg(feature = "unstable-migration")]
    let lineage = Some(serde_json::to_value(&dna_def.lineage).map_err(|e| {
        sqlx::Error::Encode(Box::new(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            e.to_string(),
        )))
    })?);
    #[cfg(not(feature = "unstable-migration"))]
    let lineage: Option<serde_json::Value> = None;

    let dna_model = DnaDefModel {
        hash: hash_bytes.to_vec(),
        agent: agent_bytes.to_vec(),
        name: dna_def.name.clone(),
        network_seed: dna_def.modifiers.network_seed.clone(),
        properties: dna_def.modifiers.properties.bytes().to_vec(),
        lineage,
    };

    inner_writes::insert_dna_def(&mut *tx, &dna_model).await?;
    inner_writes::delete_integrity_zomes(&mut *tx, hash_bytes, agent_bytes).await?;
    inner_writes::delete_coordinator_zomes(&mut *tx, hash_bytes, agent_bytes).await?;

    for (zome_index, (zome_name, zome_def)) in dna_def.integrity_zomes.iter().enumerate() {
        let zome_hash = zome_def.zome_hash();
        let dependencies = extract_dependencies(zome_def.as_any_zome_def());

        let model = IntegrityZomeModel {
            dna_hash: hash_bytes.to_vec(),
            agent: agent_bytes.to_vec(),
            zome_index: zome_index as i64,
            zome_name: zome_name.0.as_ref().to_string(),
            zome_hash: zome_hash.get_raw_39().to_vec(),
            dependencies: sqlx::types::Json(dependencies),
        };
        inner_writes::insert_integrity_zome(&mut *tx, &model).await?;
    }

    for (zome_index, (zome_name, zome_def)) in dna_def.coordinator_zomes.iter().enumerate() {
        let zome_hash = zome_def.zome_hash();
        let dependencies = extract_dependencies(zome_def.as_any_zome_def());

        let model = CoordinatorZomeModel {
            dna_hash: hash_bytes.to_vec(),
            agent: agent_bytes.to_vec(),
            zome_index: zome_index as i64,
            zome_name: zome_name.0.as_ref().to_string(),
            zome_hash: zome_hash.get_raw_39().to_vec(),
            dependencies: sqlx::types::Json(dependencies),
        };
        inner_writes::insert_coordinator_zome(&mut *tx, &model).await?;
    }

    tx.commit().await
}

/// Store an entry definition.
pub(super) async fn put_entry_def<'e, E>(
    executor: E,
    key: Vec<u8>,
    entry_def: &EntryDef,
) -> sqlx::Result<()>
where
    E: Executor<'e, Database = Sqlite>,
{
    let model = EntryDefModel::from_entry_def(key, entry_def);
    inner_writes::put_entry_def(executor, &model).await
}

fn extract_dependencies(zome_def: &ZomeDef) -> Vec<String> {
    match zome_def {
        ZomeDef::Wasm(WasmZomeDef { dependencies, .. }) => dependencies
            .iter()
            .map(|n| n.0.as_ref().to_string())
            .collect(),
        ZomeDef::Inline(InlineZomeDef { dependencies, .. }) => dependencies
            .iter()
            .map(|n| n.0.as_ref().to_string())
            .collect(),
    }
}