Skip to main content

google_cloud_bigquery/write/
append_response.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 super::error::{AppendError, AppendResult};
16use crate::Error;
17use crate::model::append_rows_response::Response;
18use crate::model::{AppendRowsResponse, TableSchema};
19
20/// The return type of an `append()` operation.
21#[derive(Clone, Debug, Default, PartialEq)]
22#[non_exhaustive]
23pub struct AppendResponse {
24    /// The row offset at which the last append occurred. The offset will not be
25    /// set if appending using default streams.
26    pub offset: Option<i64>,
27
28    /// If set, the service reports that the table schema has changed.
29    ///
30    /// Note that this notification is best effort. Changing a table schema can
31    /// take several minutes to propagate on the server side.
32    ///
33    /// The client library does not use this information to modify any internal
34    /// state. It only forwards the notification to the application, which
35    /// should react accordingly (if necessary).
36    pub updated_schema: Option<TableSchema>,
37}
38
39pub(crate) fn to_result(resp: AppendRowsResponse) -> AppendResult<AppendResponse> {
40    if !resp.row_errors.is_empty() {
41        return Err(AppendError::RowErrors(resp.row_errors));
42    }
43
44    let offset = match resp.response {
45        None => None,
46        Some(Response::AppendResult(r)) => r.offset,
47        Some(Response::Error(s)) => {
48            return Err(Error::service((*s).into()).into());
49        }
50    };
51    Ok(AppendResponse {
52        offset,
53        updated_schema: resp.updated_schema,
54    })
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::model::RowError;
61    use crate::model::append_rows_response::AppendResult;
62    use crate::model::row_error::RowErrorCode;
63    use google_cloud_gax::error::rpc::Code;
64    use google_cloud_rpc::model::Status as RpcStatus;
65
66    fn schema() -> TableSchema {
67        TableSchema::new()
68    }
69
70    fn row_error(index: i64) -> RowError {
71        RowError::new()
72            .set_index(index)
73            .set_code(RowErrorCode::FieldsError)
74            .set_message("fail")
75    }
76
77    #[test]
78    fn success() -> anyhow::Result<()> {
79        let resp = AppendRowsResponse::new()
80            .set_append_result(AppendResult::new().set_offset(42))
81            .set_updated_schema(schema());
82
83        let res = to_result(resp)?;
84        assert_eq!(res.offset, Some(42));
85        assert_eq!(res.updated_schema, Some(schema()));
86        Ok(())
87    }
88
89    #[test]
90    fn rpc_error() {
91        let resp = AppendRowsResponse::new().set_error(
92            RpcStatus::new()
93                .set_code(Code::InvalidArgument as i32)
94                .set_message("fail"),
95        );
96
97        let err = to_result(resp).expect_err("should error");
98        let AppendError::Rpc { source } = err else {
99            panic!("Expected AppendError::Rpc, got {:?}", err);
100        };
101        let status = source.status().expect("status should be set");
102        assert_eq!(status.code, Code::InvalidArgument);
103        assert_eq!(status.message, "fail");
104    }
105
106    #[test]
107    fn row_errors() {
108        let resp = AppendRowsResponse::new().set_row_errors(vec![row_error(1), row_error(2)]);
109
110        let err = to_result(resp).expect_err("should error");
111        let AppendError::RowErrors(errors) = err else {
112            panic!("Expected AppendError::RowErrors, got {:?}", err);
113        };
114        assert_eq!(errors, vec![row_error(1), row_error(2)]);
115    }
116
117    #[test]
118    fn unset_response() -> anyhow::Result<()> {
119        let resp = AppendRowsResponse::new().set_updated_schema(schema());
120        let res = to_result(resp)?;
121        assert_eq!(res.offset, None);
122        assert_eq!(res.updated_schema, Some(schema()));
123        Ok(())
124    }
125}