use crate::models::dht::ChainOpRow;
use holo_hash::{ActionHash, AnyDhtHash, AnyLinkableHash, DhtOpHash};
use holochain_integrity_types::action::OpValidity;
use holochain_timestamp::Timestamp;
use sqlx::{Executor, Sqlite};
pub struct InsertChainOp<'a> {
pub op_hash: &'a DhtOpHash,
pub action_hash: &'a ActionHash,
pub op_type: i64,
pub basis_hash: &'a AnyLinkableHash,
pub storage_center_loc: u32,
pub validation_status: OpValidity,
pub locally_validated: bool,
pub require_receipt: bool,
pub when_received: Timestamp,
pub when_integrated: Timestamp,
pub serialized_size: u32,
}
pub(crate) async fn insert_chain_op<'a, 'e, E>(
executor: E,
op: InsertChainOp<'a>,
) -> sqlx::Result<()>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query(
"INSERT INTO ChainOp
(hash, op_type, action_hash, basis_hash, storage_center_loc,
validation_status, locally_validated, require_receipt,
when_received, when_integrated, serialized_size)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
)
.bind(op.op_hash.get_raw_36())
.bind(op.op_type)
.bind(op.action_hash.get_raw_36())
.bind(op.basis_hash.get_raw_36())
.bind(op.storage_center_loc as i64)
.bind(i64::from(op.validation_status))
.bind(op.locally_validated as i64)
.bind(op.require_receipt as i64)
.bind(op.when_received.as_micros())
.bind(op.when_integrated.as_micros())
.bind(op.serialized_size as i64)
.execute(executor)
.await?;
Ok(())
}
pub(crate) async fn clear_require_receipt<'e, E>(
executor: E,
op_hash: &DhtOpHash,
) -> sqlx::Result<u64>
where
E: Executor<'e, Database = Sqlite>,
{
let result = sqlx::query("UPDATE ChainOp SET require_receipt = 0 WHERE hash = ?")
.bind(op_hash.get_raw_36())
.execute(executor)
.await?;
Ok(result.rows_affected())
}
pub(crate) async fn get_chain_op<'e, E>(
executor: E,
hash: DhtOpHash,
) -> sqlx::Result<Option<ChainOpRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as(
"SELECT hash, op_type, action_hash, basis_hash, storage_center_loc,
validation_status, locally_validated, require_receipt,
when_received, when_integrated, serialized_size
FROM ChainOp WHERE hash = ?",
)
.bind(hash.get_raw_36())
.fetch_optional(executor)
.await
}
pub(crate) async fn get_chain_ops_by_basis<'e, E>(
executor: E,
basis: AnyDhtHash,
) -> sqlx::Result<Vec<ChainOpRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as("SELECT * FROM ChainOp WHERE basis_hash = ? ORDER BY when_integrated")
.bind(basis.get_raw_36())
.fetch_all(executor)
.await
}
#[derive(Debug, sqlx::FromRow)]
pub struct OpLocationRow {
pub hash: Vec<u8>,
pub basis_hash: Vec<u8>,
pub storage_center_loc: i64,
}
pub(crate) async fn integrated_op_locations<'e, E>(executor: E) -> sqlx::Result<Vec<OpLocationRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as(
"SELECT hash, basis_hash, storage_center_loc FROM ChainOp WHERE locally_validated = 1",
)
.fetch_all(executor)
.await
}
pub(crate) async fn set_validation_status<'e, E>(
executor: E,
op_hash: &DhtOpHash,
validation_status: OpValidity,
) -> sqlx::Result<u64>
where
E: Executor<'e, Database = Sqlite>,
{
let accepted = i64::from(OpValidity::Accepted);
let result = sqlx::query(
"UPDATE ChainOp SET validation_status = ?
WHERE hash = ? AND validation_status = ? AND locally_validated = 0",
)
.bind(i64::from(validation_status))
.bind(op_hash.get_raw_36())
.bind(accepted)
.execute(executor)
.await?;
Ok(result.rows_affected())
}
pub(crate) async fn get_chain_ops_for_action<'e, E>(
executor: E,
action_hash: ActionHash,
) -> sqlx::Result<Vec<ChainOpRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as("SELECT * FROM ChainOp WHERE action_hash = ? ORDER BY op_type")
.bind(action_hash.get_raw_36())
.fetch_all(executor)
.await
}
pub(crate) async fn op_validation_outcome<'e, E>(
executor: E,
action_hash: &ActionHash,
op_type: i64,
) -> sqlx::Result<Option<i64>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_scalar(
"SELECT outcome FROM (
SELECT validation_status AS outcome, 0 AS pri
FROM ChainOp
WHERE action_hash = ? AND op_type = ? AND locally_validated = 1
UNION ALL
SELECT
(CASE WHEN sys_validation_status = 2 OR app_validation_status = 2
THEN 2 ELSE 1 END) AS outcome,
1 AS pri
FROM LimboChainOp
WHERE action_hash = ? AND op_type = ?
AND (sys_validation_status = 2
OR (sys_validation_status = 1 AND app_validation_status IN (1, 2)))
)
ORDER BY pri
LIMIT 1",
)
.bind(action_hash.get_raw_36())
.bind(op_type)
.bind(action_hash.get_raw_36())
.bind(op_type)
.fetch_optional(executor)
.await
}
#[derive(Debug, sqlx::FromRow)]
pub struct PendingReceiptRow {
pub op_hash: Vec<u8>,
pub validation_status: i64,
pub when_integrated: i64,
pub action_author: Vec<u8>,
}
pub(crate) async fn pending_validation_receipts<'e, E>(
executor: E,
) -> sqlx::Result<Vec<PendingReceiptRow>>
where
E: Executor<'e, Database = Sqlite>,
{
sqlx::query_as(
"SELECT
ChainOp.hash AS op_hash,
ChainOp.validation_status AS validation_status,
ChainOp.when_integrated AS when_integrated,
Action.author AS action_author
FROM ChainOp
JOIN Action ON ChainOp.action_hash = Action.hash
WHERE ChainOp.require_receipt = 1
AND ChainOp.when_integrated IS NOT NULL
AND ChainOp.validation_status IS NOT NULL",
)
.fetch_all(executor)
.await
}