Skip to main content

google_cloud_spanner/
result_set_metadata.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::Arc;
16
17/// Metadata about a [`ResultSet`](crate::result::ResultSet).
18///
19/// # Example
20///
21/// ```
22/// # use google_cloud_spanner::client::Spanner;
23/// # use google_cloud_spanner::statement::Statement;
24/// # use google_cloud_spanner::value::TypeCode;
25/// # async fn test_doc() -> Result<(), Box<dyn std::error::Error>> {
26/// let client = Spanner::builder().build().await?;
27/// let db = client.database_client("projects/p/instances/i/databases/d").build().await?;
28/// let tx = db.single_use().build();
29/// let mut rs = tx.execute_query(Statement::builder("SELECT 1 AS Number").build()).await?;
30///
31/// let metadata = rs.metadata().expect("metadata available");
32///
33/// for (name, type_) in metadata.column_names().iter().zip(metadata.column_types().iter()) {
34///     println!("Column: {} has type: {:?}", name, type_.code());
35/// }
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Clone, Debug, PartialEq)]
40pub struct ResultSetMetadata {
41    pub(crate) column_names: Arc<Vec<String>>,
42    pub(crate) column_types: Arc<Vec<crate::types::Type>>,
43    pub(crate) undeclared_parameters: Arc<std::collections::BTreeMap<String, crate::types::Type>>,
44}
45
46impl ResultSetMetadata {
47    pub(crate) fn new(metadata: Option<crate::google::spanner::v1::ResultSetMetadata>) -> Self {
48        let mut column_names = Vec::new();
49        let mut column_types = Vec::new();
50        let mut undeclared_parameters = std::collections::BTreeMap::new();
51
52        if let Some(m) = &metadata
53            && let Some(undeclared) = &m.undeclared_parameters
54        {
55            for field in &undeclared.fields {
56                let param_type = field.r#type.clone().map(Into::into).unwrap_or_default();
57                undeclared_parameters.insert(field.name.clone(), param_type);
58            }
59        }
60
61        let fields = metadata
62            .and_then(|m| m.row_type)
63            .into_iter()
64            .flat_map(|r| r.fields.into_iter());
65        for field in fields {
66            column_names.push(field.name);
67            let column_type = field.r#type.map(Into::into).unwrap_or_default();
68            column_types.push(column_type);
69        }
70
71        Self {
72            column_names: Arc::new(column_names),
73            column_types: Arc::new(column_types),
74            undeclared_parameters: Arc::new(undeclared_parameters),
75        }
76    }
77
78    /// Returns the names of the columns in the result set.
79    pub fn column_names(&self) -> &[String] {
80        &self.column_names
81    }
82
83    /// Returns the types of the columns in the result set.
84    pub fn column_types(&self) -> &[crate::types::Type] {
85        &self.column_types
86    }
87    /// Returns the types of the undeclared parameters in the result set.
88    pub fn undeclared_parameters(&self) -> &std::collections::BTreeMap<String, crate::types::Type> {
89        &self.undeclared_parameters
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn auto_traits() {
99        static_assertions::assert_impl_all!(
100            ResultSetMetadata: Clone,
101            std::fmt::Debug,
102            PartialEq,
103            Send,
104            Sync
105        );
106    }
107
108    #[test]
109    fn new_and_accessors() {
110        use crate::google::spanner::v1 as spanner_v1;
111
112        let proto = spanner_v1::ResultSetMetadata {
113            row_type: Some(spanner_v1::StructType {
114                fields: vec![
115                    spanner_v1::struct_type::Field {
116                        name: "col1".to_string(),
117                        r#type: Some(spanner_v1::Type {
118                            code: spanner_v1::TypeCode::String.into(),
119                            ..Default::default()
120                        }),
121                    },
122                    spanner_v1::struct_type::Field {
123                        name: "col2".to_string(),
124                        r#type: Some(spanner_v1::Type {
125                            code: spanner_v1::TypeCode::Int64.into(),
126                            ..Default::default()
127                        }),
128                    },
129                ],
130            }),
131            ..Default::default()
132        };
133
134        let metadata = ResultSetMetadata::new(Some(proto));
135
136        assert_eq!(
137            metadata.column_names(),
138            &["col1".to_string(), "col2".to_string()]
139        );
140        assert_eq!(metadata.column_types().len(), 2);
141        assert_eq!(
142            metadata.column_types()[0].code(),
143            crate::types::TypeCode::String
144        );
145        assert_eq!(
146            metadata.column_types()[1].code(),
147            crate::types::TypeCode::Int64
148        );
149    }
150}