1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::convert::TryFrom;
use std::convert::TryInto;
use crate::proto::google::spanner::v1 as proto;
use crate::Error;
use crate::FromSpanner;
use crate::StructType;
use crate::Transaction;
use crate::Value;
pub struct Row<'a> {
row_type: &'a StructType,
columns: &'a Vec<Value>,
}
impl<'a> Row<'a> {
pub fn get<T>(&'a self, column: usize) -> Result<T, Error>
where
T: FromSpanner<'a>,
{
match self.row_type.0.get(column) {
None => Err(Error::Codec(format!("no such column {}", column))),
Some((_, tpe)) => {
let value = self.columns.get(column).unwrap();
<T as FromSpanner>::from_spanner_nullable(tpe, value)
}
}
}
pub fn get_unchecked<T>(&'a self, column: usize) -> T
where
T: FromSpanner<'a>,
{
self.get(column).unwrap()
}
pub fn get_by_name<T>(&'a self, column_name: &str) -> Result<T, Error>
where
T: FromSpanner<'a>,
{
self.row_type
.field_index(column_name)
.ok_or_else(|| Error::Codec(format!("no such column: {}", column_name)))
.and_then(|idx| self.get(idx))
}
pub fn get_by_name_unchecked<T>(&'a self, column_name: &str) -> T
where
T: FromSpanner<'a>,
{
self.get_by_name(column_name).unwrap()
}
}
#[derive(Debug)]
pub(crate) struct Stats {
pub(crate) row_count: Option<i64>,
}
impl TryFrom<proto::ResultSetStats> for Stats {
type Error = Error;
fn try_from(value: proto::ResultSetStats) -> Result<Self, Self::Error> {
let row_count = match value.row_count {
Some(proto::result_set_stats::RowCount::RowCountExact(exact)) => Ok(Some(exact)),
Some(proto::result_set_stats::RowCount::RowCountLowerBound(_)) => Err(Error::Client(
"lower bound row count is unsupported".to_string(),
)),
None => Ok(None),
}?;
Ok(Self { row_count })
}
}
#[derive(Debug)]
pub struct ResultSet {
row_type: StructType,
rows: Vec<Vec<Value>>,
pub(crate) transaction: Option<Transaction>,
pub(crate) stats: Stats,
}
impl ResultSet {
pub fn iter(&self) -> impl Iterator<Item = Row<'_>> {
self.rows.iter().map(move |columns| Row {
row_type: &self.row_type,
columns,
})
}
}
impl TryFrom<proto::ResultSet> for ResultSet {
type Error = crate::Error;
fn try_from(value: proto::ResultSet) -> Result<Self, Self::Error> {
let metadata = value
.metadata
.ok_or_else(|| Self::Error::Codec("missing result set metadata".to_string()))?;
let row_type = metadata
.row_type
.ok_or_else(|| Self::Error::Codec("missing row type metadata".to_string()))
.and_then(StructType::try_from)?;
let rows = value
.rows
.iter()
.map(|row| {
row.values
.iter()
.zip(row_type.types())
.map(|(value, tpe)| Value::try_from(tpe, value.clone()))
.collect()
})
.collect::<Result<Vec<Vec<Value>>, Error>>()?;
Ok(Self {
row_type,
rows,
transaction: metadata.transaction.map(Transaction::from),
stats: value.stats.unwrap_or_default().try_into()?,
})
}
}