ydb 0.13.4

Crate contains generated low-level grpc code from YDB API protobuf, used as base for ydb crate
Documentation
use std::collections::HashMap;
use std::time::Duration;

use crate::grpc_wrapper::raw_errors::RawResult;
use crate::grpc_wrapper::raw_query_service::status::check_status;
use crate::grpc_wrapper::raw_table_service::value::{
    RawColumn, RawResultSet, RawTypedValue, RawValue,
};
use crate::types::Value;
use ydb_grpc::ydb_proto::query::{
    execute_query_request, ExecMode, ExecuteQueryRequest, ExecuteQueryResponsePart, QueryContent,
    SchemaInclusionMode, StatsMode, Syntax,
};
use ydb_grpc::ydb_proto::result_set::Format;

#[derive(Clone, Debug)]
pub(crate) struct RawExecuteQueryRequest {
    pub session_id: String,
    pub yql_text: String,
    pub parameters: HashMap<String, Value>,
    pub tx_control: Option<ydb_grpc::ydb_proto::query::TransactionControl>,
    pub collect_stats: bool,
}

impl RawExecuteQueryRequest {
    pub(crate) fn new(
        session_id: impl Into<String>,
        yql_text: impl Into<String>,
        parameters: HashMap<String, Value>,
        tx_control: Option<ydb_grpc::ydb_proto::query::TransactionControl>,
        collect_stats: bool,
    ) -> Self {
        Self {
            session_id: session_id.into(),
            yql_text: yql_text.into(),
            parameters,
            tx_control,
            collect_stats,
        }
    }

    pub fn into_proto(self) -> RawResult<ExecuteQueryRequest> {
        let mut parameters = HashMap::with_capacity(self.parameters.len());
        for (name, val) in self.parameters {
            let raw: RawTypedValue = val.try_into()?;
            parameters.insert(name, raw.into());
        }

        Ok(ExecuteQueryRequest {
            session_id: self.session_id,
            exec_mode: ExecMode::Execute as i32,
            tx_control: self.tx_control,
            query: Some(execute_query_request::Query::QueryContent(QueryContent {
                syntax: Syntax::YqlV1 as i32,
                text: self.yql_text,
            })),
            parameters,
            stats_mode: if self.collect_stats {
                StatsMode::Basic as i32
            } else {
                StatsMode::None as i32
            },
            concurrent_result_sets: false,
            response_part_limit_bytes: 0,
            pool_id: String::new(),
            stats_period_ms: 0,
            schema_inclusion_mode: SchemaInclusionMode::Unspecified as i32,
            result_set_format: Format::Unspecified as i32,
            arrow_format_settings: None,
        })
    }
}

pub(crate) fn check_part(part: &ExecuteQueryResponsePart) -> RawResult<()> {
    check_status(part.status, &part.issues)
}

pub(crate) fn tx_id_from_part(part: &ExecuteQueryResponsePart) -> Option<String> {
    part.tx_meta
        .as_ref()
        .map(|m| m.id.clone())
        .filter(|id| !id.is_empty())
}

pub(crate) fn stats_from_part(part: &ExecuteQueryResponsePart) -> Option<Duration> {
    part.exec_stats
        .as_ref()
        .map(|stats| Duration::from_micros(stats.total_duration_us))
}

pub(crate) fn append_rows_from_part(
    columns: &mut Vec<RawColumn>,
    rows: &mut Vec<Vec<RawValue>>,
    truncated: &mut bool,
    part: ExecuteQueryResponsePart,
) -> RawResult<()> {
    let Some(proto_set) = part.result_set else {
        return Ok(());
    };
    let part_set = RawResultSet::try_from(proto_set)?;
    *truncated |= part_set.truncated;
    if !columns.is_empty()
        && !part_set.columns.is_empty()
        && !columns_compatible(columns, &part_set.columns)
    {
        return Err(crate::grpc_wrapper::raw_errors::RawError::custom(
            "column metadata mismatch between stream parts".to_string(),
        ));
    }
    if columns.is_empty() {
        *columns = part_set.columns;
    }
    rows.extend(part_set.rows);
    Ok(())
}

fn columns_compatible(existing: &[RawColumn], new_cols: &[RawColumn]) -> bool {
    existing.len() == new_cols.len()
        && existing
            .iter()
            .zip(new_cols.iter())
            .all(|(left, right)| left.name == right.name && left.column_type == right.column_type)
}