google_cloud_bigquery/
error.rs1use google_cloud_bigquery_v2::model::ErrorProto;
16use google_cloud_gax::error::Error;
17
18#[derive(thiserror::Error, Debug)]
20#[non_exhaustive]
21pub enum QueryError {
22 #[error("only query jobs are supported")]
24 UnsupportedJobType,
25
26 #[error("cannot convert dry run query to complete query")]
28 DryRun,
29
30 #[non_exhaustive]
33 #[error("query job failed: {errors:?}")]
34 JobFailed {
35 errors: Vec<ErrorProto>,
37 },
38
39 #[non_exhaustive]
41 #[error("the operation failed. RPC error: {source}")]
42 Rpc {
43 #[from]
45 #[source]
46 source: Error,
47 },
48}
49
50#[derive(thiserror::Error, Debug)]
52#[non_exhaustive]
53pub enum RowError {
54 #[error("could not find column: {0}")]
56 ColumnNotFound(String),
57
58 #[non_exhaustive]
60 #[error("column index out of range: {index} (expected < {len})")]
61 IndexOutOfRange {
62 index: usize,
64 len: usize,
66 },
67
68 #[error("type conversion error for column '{column}' (SQL type {sql_type}): {source}")]
70 #[non_exhaustive]
71 TypeConversion {
72 column: String,
74 sql_type: String,
76 #[source]
78 source: ConvertError,
79 },
80
81 #[error("internal service JSON layout is invalid: {0}")]
83 InvalidRowFormat(String),
84
85 #[non_exhaustive]
87 #[error("the operation failed. RPC error: {source}")]
88 Rpc {
89 #[from]
91 #[source]
92 source: Error,
93 },
94}
95
96#[derive(thiserror::Error, Debug)]
98#[non_exhaustive]
99pub enum ConvertError {
100 #[error("type mismatch, expected {expected}, got {got}")]
102 #[non_exhaustive]
103 TypeMismatch {
104 expected: String,
106 got: String,
108 },
109
110 #[error("expected non-null value, got null")]
112 NotNull,
113
114 #[error("missing field: {0}")]
116 MissingField(String),
117
118 #[error("cannot convert value: {0}")]
120 Convert(
121 #[from]
122 #[source]
123 Box<dyn std::error::Error + Send + Sync + 'static>,
124 ),
125}
126
127impl ConvertError {
128 pub(crate) fn type_mismatch(
129 expected: impl Into<String>,
130 got: &crate::query::from_sql::SqlValueInner,
131 ) -> Self {
132 Self::TypeMismatch {
133 expected: expected.into(),
134 got: got.type_name().to_string(),
135 }
136 }
137}
138
139pub use crate::write::error::AppendError;
140pub use crate::write::error::CommitError;
141pub use crate::write::error::WriterBuilderError;
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use google_cloud_gax::error::rpc::{Code, Status};
147
148 #[test]
149 fn test_dry_run_display() {
150 let err = QueryError::DryRun;
151 assert_eq!(
152 err.to_string(),
153 "cannot convert dry run query to complete query"
154 );
155 }
156
157 #[test]
158 fn test_job_failed_display() {
159 let err = QueryError::JobFailed {
160 errors: vec![
161 ErrorProto::new()
162 .set_reason("invalidQuery")
163 .set_message("Syntax error: Unexpected end of input"),
164 ],
165 };
166 assert!(err.to_string().contains("query job failed:"));
167 assert!(err.to_string().contains("invalidQuery"));
168 assert!(
169 err.to_string()
170 .contains("Syntax error: Unexpected end of input")
171 );
172 }
173
174 #[test]
175 fn test_rpc_display() {
176 let status = Status::default()
177 .set_code(Code::InvalidArgument)
178 .set_message("simulated bad request");
179 let err = QueryError::Rpc {
180 source: Error::service(status),
181 };
182 assert_eq!(
183 err.to_string(),
184 "the operation failed. RPC error: the service reports an error with code INVALID_ARGUMENT described as: simulated bad request"
185 );
186 }
187
188 #[test]
189 fn test_row_error_display() {
190 let err = RowError::ColumnNotFound("name".to_string());
191 assert_eq!(err.to_string(), "could not find column: name");
192
193 let err = RowError::IndexOutOfRange { index: 5, len: 3 };
194 assert_eq!(
195 err.to_string(),
196 "column index out of range: 5 (expected < 3)"
197 );
198
199 let err = RowError::TypeConversion {
200 column: "age".to_string(),
201 sql_type: "INTEGER".to_string(),
202 source: ConvertError::NotNull,
203 };
204 assert_eq!(
205 err.to_string(),
206 "type conversion error for column 'age' (SQL type INTEGER): expected non-null value, got null"
207 );
208
209 let err = RowError::InvalidRowFormat("missing f field".to_string());
210 assert_eq!(
211 err.to_string(),
212 "internal service JSON layout is invalid: missing f field"
213 );
214
215 let status = Status::default()
216 .set_code(Code::Internal)
217 .set_message("internal error");
218 let err = RowError::Rpc {
219 source: Error::service(status),
220 };
221 assert!(err.to_string().contains("the operation failed. RPC error:"));
222 }
223
224 #[test]
225 fn test_convert_error_display() {
226 let val = crate::query::SqlValue::new(wkt::Value::String("hello".to_string()));
227 let err = ConvertError::type_mismatch("i64", &val.inner);
228 assert_eq!(err.to_string(), "type mismatch, expected i64, got string");
229
230 let err = ConvertError::NotNull;
231 assert_eq!(err.to_string(), "expected non-null value, got null");
232
233 let err = ConvertError::MissingField("custom_col".to_string());
234 assert_eq!(err.to_string(), "missing field: custom_col");
235
236 let inner_err: Box<dyn std::error::Error + Send + Sync> = "invalid integer".into();
237 let err = ConvertError::Convert(inner_err);
238 assert_eq!(err.to_string(), "cannot convert value: invalid integer");
239 }
240}