1use crate::error::{ConvertError, RowError};
16#[cfg(any(
17 google_cloud_unstable_bigquery_arrow,
18 google_cloud_unstable_bigquery_storage_read
19))]
20use crate::query::from_sql::ArrowCell;
21use crate::query::from_sql::SqlValueInner;
22use crate::query::{FromSql, Schema};
23#[cfg(any(
24 google_cloud_unstable_bigquery_arrow,
25 google_cloud_unstable_bigquery_storage_read
26))]
27use arrow::record_batch::RecordBatch;
28use google_cloud_bigquery_v2::model::TableFieldSchema;
29use std::sync::Arc;
30use wkt::{ListValue, Struct, Value};
31
32pub type Result<T> = std::result::Result<T, RowError>;
33
34#[derive(Clone, Debug)]
75pub struct Row {
76 pub(crate) inner: RowInner,
77 pub(crate) schema: Arc<Schema>,
78}
79
80#[derive(Clone, Debug)]
81pub(crate) enum RowInner {
82 Json(Vec<SqlValueInner>),
83 #[cfg(any(
84 google_cloud_unstable_bigquery_arrow,
85 google_cloud_unstable_bigquery_storage_read
86 ))]
87 Arrow {
88 batch: Arc<RecordBatch>,
89 row_idx: usize,
90 },
91}
92
93mod sealed {
94 use super::{Row, SqlValueInner};
95 use crate::error::ConvertError;
96 use crate::query::SqlValue;
97
98 pub trait ColumnIndex {
100 fn index(&self, row: &Row) -> Option<usize>;
102
103 #[cfg(any(
105 google_cloud_unstable_bigquery_arrow,
106 google_cloud_unstable_bigquery_storage_read
107 ))]
108 fn arrow_index(&self, struct_arr: &arrow::array::StructArray) -> Option<usize>;
109
110 fn take_sql_value(
112 &self,
113 value: &mut SqlValue,
114 ) -> std::result::Result<SqlValue, ConvertError>;
115 }
116
117 impl ColumnIndex for usize {
118 fn index(&self, row: &Row) -> Option<usize> {
119 row.schema.get_field_by_index(*self).map(|_| *self)
120 }
121
122 #[cfg(any(
123 google_cloud_unstable_bigquery_arrow,
124 google_cloud_unstable_bigquery_storage_read
125 ))]
126 fn arrow_index(&self, struct_arr: &arrow::array::StructArray) -> Option<usize> {
127 (*self < struct_arr.num_columns()).then_some(*self)
128 }
129
130 fn take_sql_value(
131 &self,
132 value: &mut SqlValue,
133 ) -> std::result::Result<SqlValue, ConvertError> {
134 match &mut value.inner {
135 SqlValueInner::Struct(entries) => {
136 let (_, slot) = entries
137 .get_mut(*self)
138 .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
139 Ok(SqlValue::from_inner(std::mem::replace(
140 slot,
141 SqlValueInner::Null,
142 )))
143 }
144 SqlValueInner::Array(arr) => {
145 let slot = arr
146 .get_mut(*self)
147 .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
148 Ok(SqlValue::from_inner(std::mem::replace(
149 slot,
150 SqlValueInner::Null,
151 )))
152 }
153 SqlValueInner::String(s) => {
154 let arr: Vec<wkt::Value> =
155 serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
156 value.inner = SqlValueInner::from_wkt(wkt::Value::Array(arr));
157 self.take_sql_value(value)
158 }
159 SqlValueInner::Null => Err(ConvertError::NotNull),
160 #[cfg(any(
161 google_cloud_unstable_bigquery_arrow,
162 google_cloud_unstable_bigquery_storage_read
163 ))]
164 SqlValueInner::Arrow(cell) => match cell.data_type() {
165 arrow::datatypes::DataType::Struct(_) => cell
166 .struct_field_cell(self)
167 .map(|val| SqlValue::from_inner(SqlValueInner::Arrow(val))),
168 arrow::datatypes::DataType::List(_)
169 | arrow::datatypes::DataType::LargeList(_) => cell
170 .list_element_cell(*self)
171 .map(|val| SqlValue::from_inner(SqlValueInner::Arrow(val))),
172 arrow::datatypes::DataType::Utf8 | arrow::datatypes::DataType::LargeUtf8 => {
173 let s = cell.as_str()?.to_string();
174 value.inner = SqlValueInner::String(s);
175 self.take_sql_value(value)
176 }
177 _ => Err(ConvertError::TypeMismatch {
178 expected: "struct, array, or string".to_string(),
179 got: cell.data_type_str(),
180 }),
181 },
182 other => Err(ConvertError::type_mismatch(
183 "struct, array, or string",
184 other,
185 )),
186 }
187 }
188 }
189
190 impl ColumnIndex for &str {
191 fn index(&self, row: &Row) -> Option<usize> {
192 row.schema.get_field_index_by_name(self)
193 }
194
195 #[cfg(any(
196 google_cloud_unstable_bigquery_arrow,
197 google_cloud_unstable_bigquery_storage_read
198 ))]
199 fn arrow_index(&self, struct_arr: &arrow::array::StructArray) -> Option<usize> {
200 struct_arr.fields().iter().position(|f| f.name() == *self)
201 }
202
203 fn take_sql_value(
204 &self,
205 value: &mut SqlValue,
206 ) -> std::result::Result<SqlValue, ConvertError> {
207 match &mut value.inner {
208 SqlValueInner::Struct(entries) => {
209 let (_, slot) = entries
210 .iter_mut()
211 .find(|(name, _)| name == *self)
212 .ok_or_else(|| ConvertError::MissingField((*self).to_string()))?;
213 Ok(SqlValue::from_inner(std::mem::replace(
214 slot,
215 SqlValueInner::Null,
216 )))
217 }
218 SqlValueInner::String(s) => {
219 let obj: wkt::Struct =
220 serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
221 value.inner = SqlValueInner::from_wkt(wkt::Value::Object(obj));
222 self.take_sql_value(value)
223 }
224 SqlValueInner::Null => Err(ConvertError::NotNull),
225 #[cfg(any(
226 google_cloud_unstable_bigquery_arrow,
227 google_cloud_unstable_bigquery_storage_read
228 ))]
229 SqlValueInner::Arrow(cell) => match cell.data_type() {
230 arrow::datatypes::DataType::Struct(_) => cell
231 .struct_field_cell(self)
232 .map(|val| SqlValue::from_inner(SqlValueInner::Arrow(val))),
233 arrow::datatypes::DataType::Utf8 | arrow::datatypes::DataType::LargeUtf8 => {
234 let s = cell.as_str()?.to_string();
235 value.inner = SqlValueInner::String(s);
236 self.take_sql_value(value)
237 }
238 _ => Err(ConvertError::TypeMismatch {
239 expected: "object or string".to_string(),
240 got: cell.data_type_str(),
241 }),
242 },
243 other => Err(ConvertError::type_mismatch("object or string", other)),
244 }
245 }
246 }
247
248 impl ColumnIndex for String {
249 fn index(&self, row: &Row) -> Option<usize> {
250 <&str as ColumnIndex>::index(&self.as_str(), row)
251 }
252
253 #[cfg(any(
254 google_cloud_unstable_bigquery_arrow,
255 google_cloud_unstable_bigquery_storage_read
256 ))]
257 fn arrow_index(&self, struct_arr: &arrow::array::StructArray) -> Option<usize> {
258 self.as_str().arrow_index(struct_arr)
259 }
260
261 fn take_sql_value(
262 &self,
263 value: &mut SqlValue,
264 ) -> std::result::Result<SqlValue, ConvertError> {
265 self.as_str().take_sql_value(value)
266 }
267 }
268}
269
270pub trait ColumnIndex: sealed::ColumnIndex + std::fmt::Display {}
274
275impl ColumnIndex for usize {}
276impl ColumnIndex for &str {}
277impl ColumnIndex for String {}
278
279impl Row {
280 pub(crate) fn try_new(row: Struct, schema: &Arc<Schema>) -> Result<Self> {
281 let values = convert_row(row, schema.fields())?;
282
283 Ok(Self {
284 inner: RowInner::Json(values),
285 schema: schema.clone(),
286 })
287 }
288
289 #[cfg(any(
290 google_cloud_unstable_bigquery_arrow,
291 google_cloud_unstable_bigquery_storage_read
292 ))]
293 pub(crate) fn try_new_from_arrow(
294 batch: &Arc<RecordBatch>,
295 row_idx: usize,
296 schema: &Arc<Schema>,
297 ) -> Result<Self> {
298 if batch.num_columns() != schema.len() {
299 return Err(RowError::InvalidRowFormat(format!(
300 "schema and row cell mismatch (expected {}, got {})",
301 schema.len(),
302 batch.num_columns()
303 )));
304 }
305 if row_idx >= batch.num_rows() {
306 return Err(RowError::InvalidRowFormat(format!(
307 "row index out of bounds (row_idx {}, num_rows {})",
308 row_idx,
309 batch.num_rows()
310 )));
311 }
312
313 Ok(Self {
314 inner: RowInner::Arrow {
315 batch: Arc::clone(batch),
316 row_idx,
317 },
318 schema: schema.clone(),
319 })
320 }
321
322 fn resolve_index<I: ColumnIndex>(&self, col: &I) -> Result<usize> {
323 sealed::ColumnIndex::index(col, self)
324 .ok_or_else(|| RowError::ColumnNotFound(format!("{col}")))
325 }
326
327 fn convert_value_at<T: FromSql>(&self, idx: usize, val: SqlValueInner) -> Result<T> {
328 T::from_value(crate::query::SqlValue::from_inner(val)).map_err(|e| {
329 let (column, sql_type) = self
330 .schema
331 .get_field_by_index(idx)
332 .map(|f| (f.name.clone(), f.r#type.clone()))
333 .unwrap_or_else(|| (idx.to_string(), "UNKNOWN".to_string()));
334 RowError::TypeConversion {
335 column,
336 sql_type,
337 source: e,
338 }
339 })
340 }
341
342 pub fn get<T: FromSql>(&self, index: impl ColumnIndex) -> Result<T> {
361 let idx = self.resolve_index(&index)?;
362 match &self.inner {
363 RowInner::Json(values) => {
364 let val = values.get(idx).ok_or_else(|| RowError::IndexOutOfRange {
365 index: idx,
366 len: self.schema.len(),
367 })?;
368 self.convert_value_at(idx, val.clone())
369 }
370 #[cfg(any(
371 google_cloud_unstable_bigquery_arrow,
372 google_cloud_unstable_bigquery_storage_read
373 ))]
374 RowInner::Arrow { batch, row_idx } => {
375 let col = batch
376 .columns()
377 .get(idx)
378 .ok_or_else(|| RowError::IndexOutOfRange {
379 index: idx,
380 len: self.schema.len(),
381 })?;
382 let val = SqlValueInner::Arrow(ArrowCell::new(col.clone(), *row_idx));
383 self.convert_value_at(idx, val)
384 }
385 }
386 }
387
388 pub fn take<T: FromSql>(&mut self, index: impl ColumnIndex) -> Result<T> {
427 let idx = self.resolve_index(&index)?;
428 match &mut self.inner {
429 RowInner::Json(values) => {
430 let val = values
431 .get_mut(idx)
432 .ok_or_else(|| RowError::IndexOutOfRange {
433 index: idx,
434 len: self.schema.len(),
435 })?;
436
437 let owned_val = std::mem::replace(val, SqlValueInner::Null);
439 self.convert_value_at(idx, owned_val)
440 }
441 #[cfg(any(
442 google_cloud_unstable_bigquery_arrow,
443 google_cloud_unstable_bigquery_storage_read
444 ))]
445 RowInner::Arrow { batch, row_idx } => {
446 let col = batch
447 .columns()
448 .get(idx)
449 .ok_or_else(|| RowError::IndexOutOfRange {
450 index: idx,
451 len: self.schema.len(),
452 })?;
453 let val = SqlValueInner::Arrow(ArrowCell::new(col.clone(), *row_idx));
454 self.convert_value_at(idx, val)
455 }
456 }
457 }
458}
459
460fn convert_row(row: Struct, fields: &[TableFieldSchema]) -> Result<Vec<SqlValueInner>> {
461 let field_list = get_field_list(row)?;
462
463 if field_list.len() != fields.len() {
464 return Err(RowError::InvalidRowFormat(format!(
465 "schema and row cell mismatch (expected {}, got {})",
466 fields.len(),
467 field_list.len()
468 )));
469 }
470
471 field_list
472 .into_iter()
473 .zip(fields)
474 .map(|(cell, field)| convert_value(get_field_value(cell)?, field))
475 .collect()
476}
477
478fn get_field_list(mut row: Struct) -> Result<Vec<Value>> {
479 match row.remove("f") {
480 Some(Value::Array(arr)) => Ok(arr),
481 Some(_) => Err(RowError::InvalidRowFormat("invalid field values".into())),
482 None => Err(RowError::InvalidRowFormat("missing field values".into())),
483 }
484}
485
486fn get_field_value(value: Value) -> Result<Value> {
487 match value {
488 Value::Object(mut obj) => match obj.remove("v") {
489 Some(val) => Ok(val),
490 None => Err(RowError::InvalidRowFormat("missing field value".into())),
491 },
492 _ => Err(RowError::InvalidRowFormat("invalid field value".into())),
493 }
494}
495
496fn convert_value(value: Value, field: &TableFieldSchema) -> Result<SqlValueInner> {
497 match value {
498 Value::Null => Ok(SqlValueInner::Null),
499 Value::String(v) => convert_basic_type(v, &field.name, &field.r#type),
500 Value::Object(v) => convert_nested(v, &field.fields),
501 Value::Array(v) => convert_repeated(v, field),
502 _ => Err(RowError::InvalidRowFormat(format!(
503 "cell value is not an object: value={:?}, field_type={:?}",
504 value, field.r#type
505 ))),
506 }
507}
508
509fn convert_repeated(value: ListValue, field: &TableFieldSchema) -> Result<SqlValueInner> {
510 let arr = value
511 .into_iter()
512 .map(|cell| {
513 let val = get_field_value(cell)?;
515 convert_value(val, field)
516 })
517 .collect::<Result<Vec<_>>>()?;
518 Ok(SqlValueInner::Array(arr))
519}
520
521fn convert_nested(value: Struct, fields: &[TableFieldSchema]) -> Result<SqlValueInner> {
522 let values = convert_row(value, fields)?;
523 let entries = fields
524 .iter()
525 .zip(values)
526 .map(|(field, value)| (field.name.clone(), value))
527 .collect();
528 Ok(SqlValueInner::Struct(entries))
529}
530
531fn convert_basic_type(value: String, field_name: &str, field_type: &str) -> Result<SqlValueInner> {
532 match field_type {
533 "STRING" | "BYTES" | "TIMESTAMP" | "DATE" | "TIME" | "DATETIME" | "NUMERIC"
534 | "BIGNUMERIC" | "BIGINT" | "GEOGRAPHY" | "JSON" | "INTERVAL" | "RANGE" => {
535 Ok(SqlValueInner::String(value))
536 }
537 "INTEGER" | "INT64" => {
538 let num = value.parse::<i64>().map_err(|e| RowError::TypeConversion {
539 column: field_name.to_string(),
540 sql_type: field_type.to_string(),
541 source: ConvertError::Convert(Box::new(e)),
542 })?;
543 Ok(SqlValueInner::Number(serde_json::Number::from(num)))
544 }
545 "FLOAT" | "FLOAT64" => {
546 let num = value.parse::<f64>().map_err(|e| RowError::TypeConversion {
547 column: field_name.to_string(),
548 sql_type: field_type.to_string(),
549 source: ConvertError::Convert(Box::new(e)),
550 })?;
551 match serde_json::Number::from_f64(num) {
552 Some(n) => Ok(SqlValueInner::Number(n)),
553 None => Ok(SqlValueInner::String(value)),
554 }
555 }
556 "BOOLEAN" | "BOOL" => {
557 let b = if value.eq_ignore_ascii_case("true") {
558 true
559 } else if value.eq_ignore_ascii_case("false") {
560 false
561 } else {
562 return Err(RowError::TypeConversion {
563 column: field_name.to_string(),
564 sql_type: field_type.to_string(),
565 source: ConvertError::Convert(
566 "provided string was not `true` or `false`".into(),
567 ),
568 });
569 };
570 Ok(SqlValueInner::Bool(b))
571 }
572 _ => Err(RowError::InvalidRowFormat(format!(
573 "unknown field type: {} at column {}",
574 field_type, field_name
575 ))),
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582 use crate as google_cloud_bigquery;
583 use crate::query::FromRow;
584 use google_cloud_bigquery_v2::model::{TableFieldSchema, TableSchema};
585 use google_cloud_type::model::Decimal;
586 use rust_decimal::Decimal as RustDecimal;
587 use serde_json::{Map, json};
588 use test_case::test_case;
589
590 type TestResult = anyhow::Result<()>;
591
592 #[tokio::test]
593 async fn convert_basic_types_from_row() -> TestResult {
594 let raw_row = Map::from_iter([(
595 "f".to_string(),
596 json!([
597 { "v": "James" },
598 { "v": "272793" },
599 { "v": "TRUE" },
600 { "v": null },
601 { "v": "64.0" },
602 ]),
603 )]);
604 let schema = TableSchema::new().set_fields([
605 TableFieldSchema::new()
606 .set_name("name")
607 .set_type("STRING")
608 .set_mode("NULLABLE"),
609 TableFieldSchema::new()
610 .set_name("some_int")
611 .set_type("INTEGER")
612 .set_mode("NULLABLE"),
613 TableFieldSchema::new()
614 .set_name("some_bool")
615 .set_type("BOOLEAN")
616 .set_mode("NULLABLE"),
617 TableFieldSchema::new()
618 .set_name("some_null")
619 .set_type("INTEGER")
620 .set_mode("NULLABLE"),
621 TableFieldSchema::new()
622 .set_name("some_float")
623 .set_type("FLOAT64")
624 .set_mode("NULLABLE"),
625 ]);
626 let schema = Arc::new(Schema::new(schema));
627 let mut row = Row::try_new(raw_row, &schema)?;
628
629 assert_eq!(row.get::<String>(0)?, "James");
630 assert_eq!(row.get::<String>("name")?, "James");
631
632 assert_eq!(row.get::<i32>(1)?, 272793);
633 assert_eq!(row.get::<i32>("some_int")?, 272793);
634 assert_eq!(row.get::<i64>(1)?, 272793);
635 assert_eq!(row.get::<i64>("some_int")?, 272793);
636
637 assert!(row.get::<bool>(2)?);
638 assert!(row.get::<bool>("some_bool")?);
639
640 assert_eq!(row.get::<Option<i64>>(3)?, None);
641 assert_eq!(row.get::<Option<i64>>("some_null")?, None);
642
643 assert_eq!(row.get::<f32>(4)?, 64.0);
644 assert_eq!(row.get::<f32>("some_float")?, 64.0);
645 assert_eq!(row.get::<f64>(4)?, 64.0);
646 assert_eq!(row.get::<f64>("some_float")?, 64.0);
647
648 assert_eq!(row.take::<String>(0)?, "James");
649 assert_eq!(row.get::<Option<String>>(0)?, None);
650
651 assert_eq!(row.take::<i32>(1)?, 272793);
652 assert_eq!(row.get::<Option<i32>>(1)?, None);
653
654 assert!(row.take::<bool>(2)?);
655 assert_eq!(row.get::<Option<bool>>(2)?, None);
656
657 assert_eq!(row.take::<Option<i64>>(3)?, None);
658 assert_eq!(row.get::<Option<i64>>(3)?, None);
659
660 assert_eq!(row.take::<f32>(4)?, 64.0);
661 assert_eq!(row.get::<Option<f32>>(4)?, None);
662
663 Ok(())
664 }
665
666 #[tokio::test]
667 async fn convert_numeric_from_row() -> TestResult {
668 let raw_row = Map::from_iter([(
669 "f".to_string(),
670 json!([
671 { "v": "123.456" },
672 { "v": "99999999999999999999.123456789" },
673 { "v": "99999999999999999999999999999999.123" },
674 ]),
675 )]);
676 let schema = TableSchema::new().set_fields([
677 TableFieldSchema::new()
678 .set_name("price")
679 .set_type("NUMERIC")
680 .set_mode("NULLABLE"),
681 TableFieldSchema::new()
682 .set_name("big_amount")
683 .set_type("BIGNUMERIC")
684 .set_mode("NULLABLE"),
685 TableFieldSchema::new()
686 .set_name("overflow_amount")
687 .set_type("BIGNUMERIC")
688 .set_mode("NULLABLE"),
689 ]);
690 let schema = Arc::new(Schema::new(schema));
691 let mut row = Row::try_new(raw_row, &schema)?;
692
693 assert_eq!(row.get::<Decimal>(0)?, Decimal::new().set_value("123.456"));
694 assert_eq!(
695 row.get::<Decimal>("price")?,
696 Decimal::new().set_value("123.456")
697 );
698
699 assert_eq!(
700 row.get::<Decimal>(1)?,
701 Decimal::new().set_value("99999999999999999999.123456789")
702 );
703 assert_eq!(
704 row.get::<Decimal>("big_amount")?,
705 Decimal::new().set_value("99999999999999999999.123456789")
706 );
707
708 assert_eq!(
709 row.get::<RustDecimal>(0)?,
710 "123.456".parse().expect("valid decimal")
711 );
712 assert_eq!(
713 row.get::<RustDecimal>("price")?,
714 "123.456".parse().expect("valid decimal")
715 );
716
717 assert_eq!(
718 row.get::<RustDecimal>(1)?,
719 "99999999999999999999.123456789"
720 .parse()
721 .expect("valid decimal")
722 );
723 assert_eq!(
724 row.get::<RustDecimal>("big_amount")?,
725 "99999999999999999999.123456789"
726 .parse()
727 .expect("valid decimal")
728 );
729
730 assert!(row.get::<RustDecimal>(2).is_err());
731 assert!(row.get::<RustDecimal>("overflow_amount").is_err());
732
733 assert_eq!(row.take::<Decimal>(0)?, Decimal::new().set_value("123.456"));
734 assert_eq!(row.get::<Option<Decimal>>(0)?, None);
735
736 assert_eq!(
737 row.take::<RustDecimal>(1)?,
738 "99999999999999999999.123456789".parse()?
739 );
740 assert_eq!(row.get::<Option<RustDecimal>>(1)?, None);
741
742 Ok(())
743 }
744
745 #[tokio::test]
746 async fn convert_bytes_from_row() -> TestResult {
747 let raw_row = Map::from_iter([(
748 "f".to_string(),
749 json!([
750 { "v": "AQIDBA==" },
751 { "v": "SGVsbG8=" },
752 { "v": null },
753 ]),
754 )]);
755 let schema = TableSchema::new().set_fields([
756 TableFieldSchema::new()
757 .set_name("payload_vec")
758 .set_type("BYTES")
759 .set_mode("NULLABLE"),
760 TableFieldSchema::new()
761 .set_name("payload_bytes")
762 .set_type("BYTES")
763 .set_mode("NULLABLE"),
764 TableFieldSchema::new()
765 .set_name("null_bytes")
766 .set_type("BYTES")
767 .set_mode("NULLABLE"),
768 ]);
769 let schema = Arc::new(Schema::new(schema));
770 let mut row = Row::try_new(raw_row, &schema)?;
771
772 assert_eq!(row.get::<Vec<u8>>(0)?, vec![1, 2, 3, 4]);
773 assert_eq!(row.get::<Vec<u8>>("payload_vec")?, vec![1, 2, 3, 4]);
774
775 assert_eq!(
776 row.get::<bytes::Bytes>(1)?,
777 bytes::Bytes::from_static(b"Hello")
778 );
779 assert_eq!(
780 row.get::<bytes::Bytes>("payload_bytes")?,
781 bytes::Bytes::from_static(b"Hello")
782 );
783
784 assert_eq!(row.get::<Option<Vec<u8>>>(2)?, None);
785 assert_eq!(row.get::<Option<bytes::Bytes>>("null_bytes")?, None);
786
787 assert_eq!(row.take::<Vec<u8>>(0)?, vec![1, 2, 3, 4]);
788 assert_eq!(row.get::<Option<Vec<u8>>>(0)?, None);
789
790 assert_eq!(
791 row.take::<bytes::Bytes>(1)?,
792 bytes::Bytes::from_static(b"Hello")
793 );
794 assert_eq!(row.get::<Option<bytes::Bytes>>(1)?, None);
795
796 Ok(())
797 }
798
799 #[tokio::test]
800 async fn convert_record_from_row() -> TestResult {
801 let raw_row = Map::from_iter([(
802 "f".to_string(),
803 json!([
804 {
805 "v": {
806 "f": [
807 { "v": "Alice" },
808 { "v": "25" }
809 ]
810 }
811 }
812 ]),
813 )]);
814 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
815 .set_name("user")
816 .set_type("RECORD")
817 .set_mode("NULLABLE")
818 .set_fields([
819 TableFieldSchema::new()
820 .set_name("name")
821 .set_type("STRING")
822 .set_mode("NULLABLE"),
823 TableFieldSchema::new()
824 .set_name("age")
825 .set_type("INTEGER")
826 .set_mode("NULLABLE"),
827 ])]);
828 let schema = Arc::new(Schema::new(schema));
829 let mut row = Row::try_new(raw_row, &schema)?;
830
831 let expected: Struct = serde_json::from_value(json!({
832 "name": "Alice",
833 "age": 25,
834 }))?;
835 assert_eq!(row.get::<Struct>(0)?, expected);
836 assert_eq!(row.get::<Struct>("user")?, expected);
837 assert_eq!(row.get::<Struct>("user".to_string())?, expected);
838 assert_eq!(row.take::<Struct>("user")?, expected);
839 assert_eq!(row.get::<Option<Struct>>("user")?, None);
840
841 Ok(())
842 }
843
844 #[derive(crate::query::FromSql, Debug, PartialEq)]
845 struct JsonPayload {
846 name: String,
847 age: i64,
848 }
849
850 #[tokio::test]
851 async fn convert_json_from_row() -> TestResult {
852 let json_str = json!({"name": "Alice", "age": 30}).to_string();
853 let raw_row = Map::from_iter([(
854 "f".to_string(),
855 json!([
856 { "v": json_str },
857 { "v": null },
858 ]),
859 )]);
860 let schema = TableSchema::new().set_fields([
861 TableFieldSchema::new()
862 .set_name("json_obj")
863 .set_type("JSON")
864 .set_mode("NULLABLE"),
865 TableFieldSchema::new()
866 .set_name("json_null")
867 .set_type("JSON")
868 .set_mode("NULLABLE"),
869 ]);
870 let schema = Arc::new(Schema::new(schema));
871 let row = Row::try_new(raw_row, &schema)?;
872
873 let expected_struct: Struct = serde_json::from_value(json!({
874 "name": "Alice",
875 "age": 30,
876 }))?;
877 assert_eq!(row.get::<String>("json_obj")?, json_str);
878 assert_eq!(row.get::<Struct>("json_obj")?, expected_struct);
879 assert_eq!(
880 row.get::<JsonPayload>("json_obj")?,
881 JsonPayload {
882 name: "Alice".to_string(),
883 age: 30,
884 }
885 );
886 assert_eq!(row.get::<Option<Struct>>("json_null")?, None);
887 assert_eq!(row.get::<Option<JsonPayload>>("json_null")?, None);
888
889 Ok(())
890 }
891
892 #[tokio::test]
893 async fn convert_repeated_from_row() -> TestResult {
894 let raw_row = Map::from_iter([(
895 "f".to_string(),
896 json!([
897 {
898 "v": [
899 { "v": "1" },
900 { "v": "2" },
901 { "v": "3" }
902 ]
903 }
904 ]),
905 )]);
906 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
907 .set_name("numbers")
908 .set_type("INTEGER")
909 .set_mode("REPEATED")]);
910 let schema = Arc::new(Schema::new(schema));
911 let mut row = Row::try_new(raw_row, &schema)?;
912
913 assert_eq!(row.get::<Vec<i64>>(0)?, vec![1, 2, 3]);
914 assert_eq!(row.get::<Vec<i64>>("numbers")?, vec![1, 2, 3]);
915 assert_eq!(row.take::<Vec<i64>>("numbers")?, vec![1, 2, 3]);
916 assert_eq!(row.get::<Option<Vec<i64>>>("numbers")?, None);
917
918 Ok(())
919 }
920
921 #[tokio::test]
922 async fn convert_repeated_record_from_row() -> TestResult {
923 let raw_row = Map::from_iter([(
924 "f".to_string(),
925 json!([
926 {
927 "v": [
928 {
929 "v": {
930 "f": [
931 { "v": "Bob" },
932 { "v": "28" }
933 ]
934 }
935 },
936 {
937 "v": {
938 "f": [
939 { "v": "Charlie" },
940 { "v": "31" }
941 ]
942 }
943 }
944 ]
945 }
946 ]),
947 )]);
948 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
949 .set_name("users")
950 .set_type("RECORD")
951 .set_mode("REPEATED")
952 .set_fields([
953 TableFieldSchema::new()
954 .set_name("name")
955 .set_type("STRING")
956 .set_mode("NULLABLE"),
957 TableFieldSchema::new()
958 .set_name("age")
959 .set_type("INTEGER")
960 .set_mode("NULLABLE"),
961 ])]);
962 let schema = Arc::new(Schema::new(schema));
963 let mut row = Row::try_new(raw_row, &schema)?;
964
965 let expected: Vec<Struct> = serde_json::from_value(json!([
966 {
967 "name": "Bob",
968 "age": 28,
969 },
970 {
971 "name": "Charlie",
972 "age": 31,
973 },
974 ]))?;
975 assert_eq!(row.get::<Vec<Struct>>(0)?, expected);
976 assert_eq!(row.get::<Vec<Struct>>("users")?, expected);
977 assert_eq!(row.take::<Vec<Struct>>("users")?, expected);
978 assert_eq!(row.get::<Option<Vec<Struct>>>("users")?, None);
979
980 Ok(())
981 }
982
983 #[test_case("INTEGER", "123", Value::Number(123.into()); "integer positive")]
984 #[test_case("INTEGER", "-456", Value::Number((-456).into()); "integer negative")]
985 #[test_case("INT64", "9223372036854775807", Value::Number(9223372036854775807_i64.into()); "int64 max")]
986 #[test_case("FLOAT", "123.45", Value::Number(serde_json::Number::from_f64(123.45).unwrap()); "float success")]
987 #[test_case("FLOAT64", "NaN", Value::String("NaN".to_string()); "float NaN")]
988 #[test_case("FLOAT64", "+inf", Value::String("+inf".to_string()); "float positive infinity")]
989 #[test_case("FLOAT64", "-inf", Value::String("-inf".to_string()); "float negative infinity")]
990 #[test_case("BOOLEAN", "true", Value::Bool(true); "boolean true lowercase")]
991 #[test_case("BOOLEAN", "TRUE", Value::Bool(true); "boolean true uppercase")]
992 #[test_case("BOOL", "false", Value::Bool(false); "bool false")]
993 #[test_case("JSON", r#"{"a":1}"#, Value::String(r#"{"a":1}"#.to_string()); "json string")]
994 fn convert_basic_type_cases_success(field_type: &str, value: &str, expected: Value) {
995 let res = convert_basic_type(value.to_string(), "test_col", field_type);
996 let value = res.expect("should succeed");
997 assert_eq!(
998 value,
999 crate::query::from_sql::SqlValueInner::from_wkt(expected)
1000 );
1001 }
1002
1003 #[test_case("INTEGER", "abc"; "integer invalid")]
1004 #[test_case("INT64", "9223372036854775808"; "int64 overflow")]
1005 #[test_case("FLOAT", "abc"; "float invalid")]
1006 #[test_case("BOOL", "invalid"; "bool invalid")]
1007 fn convert_basic_type_cases_conversion_fail(field_type: &str, value: &str) {
1008 let res = convert_basic_type(value.to_string(), "test_col", field_type);
1009 let err = res.unwrap_err();
1010 assert!(matches!(err, RowError::TypeConversion { .. }));
1011 }
1012
1013 #[test]
1014 fn convert_basic_type_invalid_row_format() {
1015 let res = convert_basic_type("value".to_string(), "test_col", "UNKNOWN");
1016 let err = res.unwrap_err();
1017 assert!(matches!(err, RowError::InvalidRowFormat(_)));
1018 }
1019
1020 #[test]
1021 fn convert_value_unsupported_value() {
1022 let field = TableFieldSchema::new()
1023 .set_name("test_col")
1024 .set_type("BOOLEAN")
1025 .set_mode("NULLABLE");
1026 let res = convert_value(Value::Bool(true), &field);
1027 let err = res.unwrap_err();
1028 assert!(matches!(err, RowError::InvalidRowFormat(_)));
1029 }
1030
1031 #[derive(FromRow, Debug, PartialEq)]
1032 struct TestRow {
1033 name: String,
1034 #[bigquery(rename = "custom_int")]
1035 some_int: i64,
1036 some_bool: bool,
1037 some_null: Option<i64>,
1038 }
1039
1040 #[tokio::test]
1041 async fn derive_from_row_success() -> TestResult {
1042 let raw_row = Map::from_iter([(
1043 "f".to_string(),
1044 json!([
1045 { "v": "James" },
1046 { "v": "272793" },
1047 { "v": "TRUE" },
1048 { "v": null },
1049 ]),
1050 )]);
1051 let schema = TableSchema::new().set_fields([
1052 TableFieldSchema::new()
1053 .set_name("name")
1054 .set_type("STRING")
1055 .set_mode("NULLABLE"),
1056 TableFieldSchema::new()
1057 .set_name("custom_int")
1058 .set_type("INTEGER")
1059 .set_mode("NULLABLE"),
1060 TableFieldSchema::new()
1061 .set_name("some_bool")
1062 .set_type("BOOLEAN")
1063 .set_mode("NULLABLE"),
1064 TableFieldSchema::new()
1065 .set_name("some_null")
1066 .set_type("INTEGER")
1067 .set_mode("NULLABLE"),
1068 ]);
1069 let schema = Arc::new(Schema::new(schema));
1070 let row = Row::try_new(raw_row, &schema)?;
1071
1072 let converted_row = TestRow::try_from(row)?;
1073 assert_eq!(
1074 converted_row,
1075 TestRow {
1076 name: "James".to_string(),
1077 some_int: 272793,
1078 some_bool: true,
1079 some_null: None,
1080 }
1081 );
1082
1083 Ok(())
1084 }
1085
1086 #[derive(FromRow, Debug, PartialEq)]
1087 struct RawIdentRow {
1088 r#type: String,
1089 r#match: i64,
1090 }
1091
1092 #[tokio::test]
1093 async fn derive_from_row_raw_identifier() -> TestResult {
1094 let raw_row = Map::from_iter([(
1095 "f".to_string(),
1096 json!([
1097 { "v": "click" },
1098 { "v": "7" },
1099 ]),
1100 )]);
1101 let schema = TableSchema::new().set_fields([
1102 TableFieldSchema::new()
1103 .set_name("type")
1104 .set_type("STRING")
1105 .set_mode("NULLABLE"),
1106 TableFieldSchema::new()
1107 .set_name("match")
1108 .set_type("INTEGER")
1109 .set_mode("NULLABLE"),
1110 ]);
1111 let schema = Arc::new(Schema::new(schema));
1112 let row = Row::try_new(raw_row, &schema)?;
1113
1114 let converted = RawIdentRow::try_from(row)?;
1115 assert_eq!(
1116 converted,
1117 RawIdentRow {
1118 r#type: "click".to_string(),
1119 r#match: 7,
1120 }
1121 );
1122 Ok(())
1123 }
1124
1125 #[tokio::test]
1126 async fn derive_from_row_missing_column() -> TestResult {
1127 let raw_row = Map::from_iter([(
1128 "f".to_string(),
1129 json!([
1130 { "v": "James" },
1131 { "v": "123" },
1132 { "v": "TRUE" },
1133 { "v": null },
1134 ]),
1135 )]);
1136 let schema = TableSchema::new().set_fields([
1137 TableFieldSchema::new()
1138 .set_name("name")
1139 .set_type("STRING")
1140 .set_mode("NULLABLE"),
1141 TableFieldSchema::new()
1142 .set_name("wrong_col")
1143 .set_type("INTEGER")
1144 .set_mode("NULLABLE"),
1145 TableFieldSchema::new()
1146 .set_name("some_bool")
1147 .set_type("BOOLEAN")
1148 .set_mode("NULLABLE"),
1149 TableFieldSchema::new()
1150 .set_name("some_null")
1151 .set_type("INTEGER")
1152 .set_mode("NULLABLE"),
1153 ]);
1154 let schema = Arc::new(Schema::new(schema));
1155 let row = Row::try_new(raw_row, &schema)?;
1156
1157 let err = TestRow::try_from(row).unwrap_err();
1158 assert!(matches!(err, RowError::ColumnNotFound(col) if col == "custom_int"));
1159 Ok(())
1160 }
1161
1162 #[derive(FromRow, Debug, PartialEq)]
1163 struct ShadowedFieldNamesRow {
1164 row: i64,
1165 name: String,
1166 }
1167
1168 #[tokio::test]
1169 async fn derive_from_row_shadowing_field_names() -> TestResult {
1170 let raw_row = Map::from_iter([(
1171 "f".to_string(),
1172 json!([
1173 { "v": "42" },
1174 { "v": "Alice" },
1175 ]),
1176 )]);
1177 let schema = TableSchema::new().set_fields([
1178 TableFieldSchema::new()
1179 .set_name("row")
1180 .set_type("INTEGER")
1181 .set_mode("NULLABLE"),
1182 TableFieldSchema::new()
1183 .set_name("name")
1184 .set_type("STRING")
1185 .set_mode("NULLABLE"),
1186 ]);
1187 let schema = Arc::new(Schema::new(schema));
1188 let row = Row::try_new(raw_row, &schema)?;
1189
1190 let converted = ShadowedFieldNamesRow::try_from(row)?;
1191 assert_eq!(
1192 converted,
1193 ShadowedFieldNamesRow {
1194 row: 42,
1195 name: "Alice".to_string(),
1196 }
1197 );
1198 Ok(())
1199 }
1200
1201 #[derive(FromRow, Debug, PartialEq)]
1202 struct TupleRow(i64, String);
1203
1204 #[derive(crate::query::FromSql, Debug, PartialEq)]
1205 struct AnonTriple(i64, String, bool);
1206
1207 #[derive(crate::query::FromSql, Debug, PartialEq)]
1208 struct NamedZThenA {
1209 z: i64,
1210 a: i64,
1211 }
1212
1213 #[derive(crate::query::FromSql, Debug, PartialEq)]
1214 struct PositionalPair(i64, i64);
1215
1216 #[derive(crate::query::FromSql, Debug, PartialEq)]
1217 struct DupIdNamed {
1218 id: i64,
1219 }
1220
1221 #[tokio::test]
1222 async fn anonymous_struct_preserves_all_fields() -> TestResult {
1223 let raw_row = Map::from_iter([(
1226 "f".to_string(),
1227 json!([
1228 {
1229 "v": {
1230 "f": [
1231 { "v": "10" },
1232 { "v": "hello" },
1233 { "v": "true" }
1234 ]
1235 }
1236 }
1237 ]),
1238 )]);
1239 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1240 .set_name("anon")
1241 .set_type("RECORD")
1242 .set_mode("NULLABLE")
1243 .set_fields([
1244 TableFieldSchema::new()
1245 .set_name("")
1246 .set_type("INT64")
1247 .set_mode("NULLABLE"),
1248 TableFieldSchema::new()
1249 .set_name("")
1250 .set_type("STRING")
1251 .set_mode("NULLABLE"),
1252 TableFieldSchema::new()
1253 .set_name("")
1254 .set_type("BOOL")
1255 .set_mode("NULLABLE"),
1256 ])]);
1257 let schema = Arc::new(Schema::new(schema));
1258 let mut row = Row::try_new(raw_row, &schema)?;
1259
1260 let anon: AnonTriple = row.take("anon")?;
1261 assert_eq!(anon, AnonTriple(10, "hello".to_string(), true));
1262 Ok(())
1263 }
1264
1265 #[tokio::test]
1266 async fn struct_preserves_sql_declaration_order_over_alphabetical_order() -> TestResult {
1267 let raw_row = Map::from_iter([(
1270 "f".to_string(),
1271 json!([
1272 {
1273 "v": {
1274 "f": [
1275 { "v": "1" },
1276 { "v": "2" }
1277 ]
1278 }
1279 }
1280 ]),
1281 )]);
1282 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1283 .set_name("pair")
1284 .set_type("RECORD")
1285 .set_mode("NULLABLE")
1286 .set_fields([
1287 TableFieldSchema::new()
1288 .set_name("z")
1289 .set_type("INT64")
1290 .set_mode("NULLABLE"),
1291 TableFieldSchema::new()
1292 .set_name("a")
1293 .set_type("INT64")
1294 .set_mode("NULLABLE"),
1295 ])]);
1296 let schema = Arc::new(Schema::new(schema));
1297 let row = Row::try_new(raw_row, &schema)?;
1298
1299 let by_name: NamedZThenA = row.get("pair")?;
1301 assert_eq!(by_name, NamedZThenA { z: 1, a: 2 });
1302
1303 let by_pos: PositionalPair = row.get("pair")?;
1305 assert_eq!(by_pos, PositionalPair(1, 2));
1306 Ok(())
1307 }
1308
1309 #[tokio::test]
1310 async fn duplicate_struct_field_names_match_row_behavior() -> TestResult {
1311 let raw_row = Map::from_iter([(
1315 "f".to_string(),
1316 json!([
1317 {
1318 "v": {
1319 "f": [
1320 { "v": "100" },
1321 { "v": "200" }
1322 ]
1323 }
1324 }
1325 ]),
1326 )]);
1327 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1328 .set_name("dup")
1329 .set_type("RECORD")
1330 .set_mode("NULLABLE")
1331 .set_fields([
1332 TableFieldSchema::new()
1333 .set_name("id")
1334 .set_type("INT64")
1335 .set_mode("NULLABLE"),
1336 TableFieldSchema::new()
1337 .set_name("id")
1338 .set_type("INT64")
1339 .set_mode("NULLABLE"),
1340 ])]);
1341 let schema = Arc::new(Schema::new(schema));
1342 let row = Row::try_new(raw_row, &schema)?;
1343
1344 let first_id: DupIdNamed = row.get("dup")?;
1345 assert_eq!(first_id, DupIdNamed { id: 100 });
1346
1347 let both_ids: PositionalPair = row.get("dup")?;
1348 assert_eq!(both_ids, PositionalPair(100, 200));
1349 Ok(())
1350 }
1351
1352 #[tokio::test]
1353 async fn derive_from_row_tuple_struct() -> TestResult {
1354 let raw_row = Map::from_iter([(
1356 "f".to_string(),
1357 json!([
1358 { "v": "42" },
1359 { "v": "world" },
1360 ]),
1361 )]);
1362 let schema = TableSchema::new().set_fields([
1363 TableFieldSchema::new()
1364 .set_name("_f0")
1365 .set_type("INT64")
1366 .set_mode("NULLABLE"),
1367 TableFieldSchema::new()
1368 .set_name("_f1")
1369 .set_type("STRING")
1370 .set_mode("NULLABLE"),
1371 ]);
1372 let schema = Arc::new(Schema::new(schema));
1373 let row = Row::try_new(raw_row, &schema)?;
1374
1375 let converted = TupleRow::try_from(row)?;
1376 assert_eq!(converted, TupleRow(42, "world".to_string()));
1377
1378 Ok(())
1380 }
1381
1382 #[derive(FromSql, Debug, PartialEq)]
1383 struct NestedGeneric<U> {
1384 inner_val: U,
1385 }
1386
1387 #[derive(FromRow, Debug, PartialEq)]
1388 struct GenericRow<T: Clone + Default, U: std::fmt::Debug> {
1389 #[bigquery(rename = "custom_val")]
1390 single: T,
1391 optional: Option<T>,
1392 list: Vec<T>,
1393 nested: NestedGeneric<U>,
1394 common: i64,
1395 }
1396
1397 #[tokio::test]
1398 async fn derive_from_row_generic() -> TestResult {
1399 let raw_row = Map::from_iter([(
1400 "f".to_string(),
1401 json!([
1402 { "v": "100" },
1403 { "v": null },
1404 { "v": [{ "v": "1" }, { "v": "2" }, { "v": "3" }] },
1405 {
1406 "v": {
1407 "f": [
1408 { "v": "nested_value" }
1409 ]
1410 }
1411 },
1412 { "v": "1" },
1413 ]),
1414 )]);
1415 let schema = TableSchema::new().set_fields([
1416 TableFieldSchema::new()
1417 .set_name("custom_val")
1418 .set_type("INTEGER")
1419 .set_mode("NULLABLE"),
1420 TableFieldSchema::new()
1421 .set_name("optional")
1422 .set_type("INTEGER")
1423 .set_mode("NULLABLE"),
1424 TableFieldSchema::new()
1425 .set_name("list")
1426 .set_type("INTEGER")
1427 .set_mode("REPEATED"),
1428 TableFieldSchema::new()
1429 .set_name("nested")
1430 .set_type("RECORD")
1431 .set_mode("NULLABLE")
1432 .set_fields([TableFieldSchema::new()
1433 .set_name("inner_val")
1434 .set_type("STRING")
1435 .set_mode("NULLABLE")]),
1436 TableFieldSchema::new()
1437 .set_name("common")
1438 .set_type("INTEGER")
1439 .set_mode("NULLABLE"),
1440 ]);
1441 let schema = Arc::new(Schema::new(schema));
1442 let row = Row::try_new(raw_row, &schema)?;
1443
1444 let converted = GenericRow::<i64, String>::try_from(row)?;
1445 assert_eq!(
1446 converted,
1447 GenericRow {
1448 single: 100,
1449 optional: None,
1450 list: vec![1, 2, 3],
1451 nested: NestedGeneric {
1452 inner_val: "nested_value".to_string(),
1453 },
1454 common: 1,
1455 }
1456 );
1457 Ok(())
1458 }
1459
1460 #[derive(FromRow, Debug, PartialEq)]
1461 struct GenericRowWhere<T>
1462 where
1463 T: std::fmt::Debug + Clone,
1464 {
1465 val: T,
1466 }
1467
1468 #[tokio::test]
1469 async fn derive_from_row_generic_where_clause() -> TestResult {
1470 let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "hello" }]))]);
1471 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1472 .set_name("val")
1473 .set_type("STRING")
1474 .set_mode("NULLABLE")]);
1475 let schema = Arc::new(Schema::new(schema));
1476 let row = Row::try_new(raw_row, &schema)?;
1477
1478 let converted = GenericRowWhere::<String>::try_from(row)?;
1479 assert_eq!(
1480 converted,
1481 GenericRowWhere {
1482 val: "hello".to_string(),
1483 }
1484 );
1485 Ok(())
1486 }
1487
1488 #[derive(FromRow, Debug, PartialEq)]
1489 struct GenericRowDefault<T = i64> {
1490 val: T,
1491 }
1492
1493 #[tokio::test]
1494 async fn derive_from_row_generic_default_param() -> TestResult {
1495 let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "42" }]))]);
1496 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1497 .set_name("val")
1498 .set_type("INTEGER")
1499 .set_mode("NULLABLE")]);
1500 let schema = Arc::new(Schema::new(schema));
1501 let row = Row::try_new(raw_row, &schema)?;
1502
1503 let converted = GenericRowDefault::try_from(row)?;
1504 assert_eq!(converted, GenericRowDefault { val: 42 });
1505 Ok(())
1506 }
1507
1508 #[derive(FromRow, Debug, PartialEq)]
1509 struct GenericTupleRow<T, U>(T, Option<T>, U);
1510
1511 #[tokio::test]
1512 async fn derive_from_row_generic_tuple_struct() -> TestResult {
1513 let raw_row = Map::from_iter([(
1514 "f".to_string(),
1515 json!([
1516 { "v": "42" },
1517 { "v": null },
1518 { "v": "hello" },
1519 ]),
1520 )]);
1521 let schema = TableSchema::new().set_fields([
1522 TableFieldSchema::new()
1523 .set_name("_f0")
1524 .set_type("INT64")
1525 .set_mode("NULLABLE"),
1526 TableFieldSchema::new()
1527 .set_name("_f1")
1528 .set_type("INT64")
1529 .set_mode("NULLABLE"),
1530 TableFieldSchema::new()
1531 .set_name("_f2")
1532 .set_type("STRING")
1533 .set_mode("NULLABLE"),
1534 ]);
1535 let schema = Arc::new(Schema::new(schema));
1536 let row = Row::try_new(raw_row, &schema)?;
1537
1538 let converted = GenericTupleRow::<i64, String>::try_from(row)?;
1539 assert_eq!(converted, GenericTupleRow(42, None, "hello".to_string()));
1540 Ok(())
1541 }
1542
1543 #[cfg(any(
1544 google_cloud_unstable_bigquery_arrow,
1545 google_cloud_unstable_bigquery_storage_read
1546 ))]
1547 mod arrow_tests {
1548 use super::*;
1549 use crate::query::SqlValue;
1550 use arrow::array::{Int64Array, StringArray};
1551 use arrow::datatypes::{DataType, Field, Schema as ArrowSchema};
1552
1553 #[derive(Debug, PartialEq)]
1554 struct ArrowI64(i64);
1555
1556 impl FromSql for ArrowI64 {
1557 fn from_value(value: SqlValue) -> std::result::Result<Self, ConvertError> {
1558 match value.inner {
1559 SqlValueInner::Arrow(cell) => cell.as_i64().map(Self),
1560 other => Err(ConvertError::type_mismatch("Int64Array", &other)),
1561 }
1562 }
1563 }
1564
1565 #[derive(Debug, PartialEq)]
1566 struct ArrowString(String);
1567
1568 impl FromSql for ArrowString {
1569 fn from_value(value: SqlValue) -> std::result::Result<Self, ConvertError> {
1570 match value.inner {
1571 SqlValueInner::Arrow(cell) => cell.as_str().map(|s| Self(s.to_owned())),
1572 other => Err(ConvertError::type_mismatch("StringArray", &other)),
1573 }
1574 }
1575 }
1576
1577 #[derive(FromRow, Debug, PartialEq)]
1578 struct ArrowUserRow {
1579 name: ArrowString,
1580 age: ArrowI64,
1581 }
1582
1583 fn sample_batch_and_schema() -> anyhow::Result<(Arc<RecordBatch>, Arc<Schema>)> {
1584 let arrow_schema = Arc::new(ArrowSchema::new(vec![
1585 Field::new("name", DataType::Utf8, false),
1586 Field::new("age", DataType::Int64, true),
1587 ]));
1588 let batch = Arc::new(RecordBatch::try_new(
1589 arrow_schema.clone(),
1590 vec![
1591 Arc::new(StringArray::from(vec!["Alice", "Bob"])),
1592 Arc::new(Int64Array::from(vec![Some(30), None])),
1593 ],
1594 )?);
1595 let schema = Arc::new(Schema::from_arrow_schema(&arrow_schema));
1596 Ok((batch, schema))
1597 }
1598
1599 #[test]
1600 fn try_new_from_arrow_validation() -> TestResult {
1601 let (batch, schema) = sample_batch_and_schema()?;
1602
1603 let mismatched_schema = Arc::new(Schema::new(
1604 TableSchema::new().set_fields([TableFieldSchema::new()
1605 .set_name("only_one")
1606 .set_type("STRING")]),
1607 ));
1608 let err = Row::try_new_from_arrow(&batch, 0, &mismatched_schema).unwrap_err();
1609 assert!(
1610 matches!(&err, RowError::InvalidRowFormat(msg) if msg.contains("expected 1, got 2")),
1611 "unexpected error: {err:?}"
1612 );
1613
1614 let err = Row::try_new_from_arrow(&batch, 2, &schema).unwrap_err();
1615 assert!(
1616 matches!(&err, RowError::InvalidRowFormat(msg) if msg.contains("row_idx 2, num_rows 2")),
1617 "unexpected error: {err:?}"
1618 );
1619
1620 Ok(())
1621 }
1622
1623 #[test]
1624 fn arrow_row_get_take_and_from_row() -> TestResult {
1625 let (batch, schema) = sample_batch_and_schema()?;
1626
1627 let mut row0 = Row::try_new_from_arrow(&batch, 0, &schema)?;
1628 assert_eq!(
1629 row0.get::<ArrowString>("name")?,
1630 ArrowString("Alice".to_string())
1631 );
1632 assert_eq!(row0.get::<ArrowI64>(1)?, ArrowI64(30));
1633 assert_eq!(
1634 row0.take::<ArrowString>(0)?,
1635 ArrowString("Alice".to_string())
1636 );
1637 assert_eq!(row0.take::<ArrowI64>("age")?, ArrowI64(30));
1638
1639 let derived = ArrowUserRow::try_from(Row::try_new_from_arrow(&batch, 0, &schema)?)?;
1640 assert_eq!(
1641 derived,
1642 ArrowUserRow {
1643 name: ArrowString("Alice".to_string()),
1644 age: ArrowI64(30),
1645 }
1646 );
1647
1648 let mut row1 = Row::try_new_from_arrow(&batch, 1, &schema)?;
1649 assert_eq!(
1650 row1.get::<ArrowString>("name")?,
1651 ArrowString("Bob".to_string())
1652 );
1653
1654 let err = row1.get::<ArrowI64>("age").unwrap_err();
1655 assert!(
1656 matches!(
1657 &err,
1658 RowError::TypeConversion {
1659 column,
1660 sql_type,
1661 source: ConvertError::NotNull,
1662 } if column == "age" && sql_type == "INTEGER"
1663 ),
1664 "unexpected error: {err:?}"
1665 );
1666
1667 let err = row1.take::<ArrowI64>("missing").unwrap_err();
1668 assert!(
1669 matches!(&err, RowError::ColumnNotFound(col) if col == "missing"),
1670 "unexpected error: {err:?}"
1671 );
1672
1673 let err = row1.get::<ArrowI64>(99).unwrap_err();
1674 assert!(
1675 matches!(&err, RowError::ColumnNotFound(col) if col == "99"),
1676 "unexpected error: {err:?}"
1677 );
1678
1679 Ok(())
1680 }
1681
1682 #[test]
1683 fn try_new_from_arrow_anonymous_and_named_structs() -> TestResult {
1684 use arrow::array::{ArrayRef, BooleanArray, StructArray};
1685 use arrow::datatypes::Fields;
1686
1687 let anon_fields = Fields::from(vec![
1689 Field::new("", DataType::Int64, false),
1690 Field::new("", DataType::Utf8, false),
1691 Field::new("", DataType::Boolean, false),
1692 ]);
1693 let anon_arr = Arc::new(StructArray::new(
1694 anon_fields.clone(),
1695 vec![
1696 Arc::new(Int64Array::from(vec![10])) as ArrayRef,
1697 Arc::new(StringArray::from(vec!["hello"])) as ArrayRef,
1698 Arc::new(BooleanArray::from(vec![true])) as ArrayRef,
1699 ],
1700 None,
1701 ));
1702
1703 let pair_fields = Fields::from(vec![
1705 Field::new("z", DataType::Int64, false),
1706 Field::new("a", DataType::Int64, false),
1707 ]);
1708 let pair_arr = Arc::new(StructArray::new(
1709 pair_fields.clone(),
1710 vec![
1711 Arc::new(Int64Array::from(vec![1])) as ArrayRef,
1712 Arc::new(Int64Array::from(vec![2])) as ArrayRef,
1713 ],
1714 None,
1715 ));
1716
1717 let dup_fields = Fields::from(vec![
1719 Field::new("id", DataType::Int64, false),
1720 Field::new("id", DataType::Int64, false),
1721 ]);
1722 let dup_arr = Arc::new(StructArray::new(
1723 dup_fields.clone(),
1724 vec![
1725 Arc::new(Int64Array::from(vec![100])) as ArrayRef,
1726 Arc::new(Int64Array::from(vec![200])) as ArrayRef,
1727 ],
1728 None,
1729 ));
1730
1731 let arrow_schema = Arc::new(ArrowSchema::new(vec![
1732 Field::new("anon", DataType::Struct(anon_fields), false),
1733 Field::new("pair", DataType::Struct(pair_fields), false),
1734 Field::new("dup", DataType::Struct(dup_fields), false),
1735 ]));
1736
1737 let batch = Arc::new(RecordBatch::try_new(
1738 arrow_schema,
1739 vec![anon_arr, pair_arr, dup_arr],
1740 )?);
1741
1742 let table_schema = TableSchema::new().set_fields([
1743 TableFieldSchema::new().set_name("anon").set_type("RECORD"),
1744 TableFieldSchema::new().set_name("pair").set_type("RECORD"),
1745 TableFieldSchema::new().set_name("dup").set_type("RECORD"),
1746 ]);
1747 let schema = Arc::new(Schema::new(table_schema));
1748
1749 let mut row = Row::try_new_from_arrow(&batch, 0, &schema)?;
1750
1751 let anon: AnonTriple = row.get("anon")?;
1753 assert_eq!(anon, AnonTriple(10, "hello".to_string(), true));
1754
1755 let by_name: NamedZThenA = row.get("pair")?;
1757 assert_eq!(by_name, NamedZThenA { z: 1, a: 2 });
1758 let by_pos: PositionalPair = row.get("pair")?;
1759 assert_eq!(by_pos, PositionalPair(1, 2));
1760
1761 let first_id: DupIdNamed = row.get("dup")?;
1763 assert_eq!(first_id, DupIdNamed { id: 100 });
1764 let both_ids: PositionalPair = row.get("dup")?;
1765 assert_eq!(both_ids, PositionalPair(100, 200));
1766
1767 let anon_taken: AnonTriple = row.take("anon")?;
1769 assert_eq!(anon_taken, AnonTriple(10, "hello".to_string(), true));
1770
1771 Ok(())
1772 }
1773
1774 #[derive(FromRow, Debug, PartialEq)]
1775 struct ArrowNestedRow {
1776 numbers: Vec<i64>,
1777 strings: Vec<String>,
1778 pairs: Vec<PositionalPair>,
1779 }
1780
1781 #[test]
1782 fn try_new_from_arrow_list_arrays() -> TestResult {
1783 use arrow::array::{ArrayRef, LargeListArray, ListArray, StructArray};
1784 use arrow::buffer::OffsetBuffer;
1785 use arrow::datatypes::Fields;
1786
1787 let int_values = Arc::new(Int64Array::from(vec![1, 2, 3, 42])) as ArrayRef;
1789 let int_offsets = OffsetBuffer::from_lengths(vec![3, 1]);
1790 let int_list = Arc::new(ListArray::new(
1791 Arc::new(Field::new("element", DataType::Int64, false)),
1792 int_offsets,
1793 int_values,
1794 None,
1795 ));
1796
1797 let str_values = Arc::new(StringArray::from(vec!["a", "b", "c"])) as ArrayRef;
1799 let str_offsets = OffsetBuffer::from_lengths(vec![2, 1]);
1800 let str_large_list = Arc::new(LargeListArray::new(
1801 Arc::new(Field::new("element", DataType::Utf8, false)),
1802 str_offsets,
1803 str_values,
1804 None,
1805 ));
1806
1807 let pair_fields = Fields::from(vec![
1809 Field::new("z", DataType::Int64, false),
1810 Field::new("a", DataType::Int64, false),
1811 ]);
1812 let pair_structs = Arc::new(StructArray::new(
1813 pair_fields.clone(),
1814 vec![
1815 Arc::new(Int64Array::from(vec![1, 10])) as ArrayRef,
1816 Arc::new(Int64Array::from(vec![2, 20])) as ArrayRef,
1817 ],
1818 None,
1819 )) as ArrayRef;
1820 let struct_offsets = OffsetBuffer::from_lengths(vec![1, 1]);
1821 let struct_list = Arc::new(ListArray::new(
1822 Arc::new(Field::new("element", DataType::Struct(pair_fields), false)),
1823 struct_offsets,
1824 pair_structs,
1825 None,
1826 ));
1827
1828 let arrow_schema = Arc::new(ArrowSchema::new(vec![
1829 Field::new(
1830 "numbers",
1831 DataType::List(Arc::new(Field::new("element", DataType::Int64, false))),
1832 false,
1833 ),
1834 Field::new(
1835 "strings",
1836 DataType::LargeList(Arc::new(Field::new("element", DataType::Utf8, false))),
1837 false,
1838 ),
1839 Field::new(
1840 "pairs",
1841 DataType::List(Arc::new(Field::new(
1842 "element",
1843 DataType::Struct(Fields::from(vec![
1844 Field::new("z", DataType::Int64, false),
1845 Field::new("a", DataType::Int64, false),
1846 ])),
1847 false,
1848 ))),
1849 false,
1850 ),
1851 ]));
1852
1853 let batch = Arc::new(RecordBatch::try_new(
1854 arrow_schema,
1855 vec![int_list, str_large_list, struct_list],
1856 )?);
1857
1858 let table_schema = TableSchema::new().set_fields([
1859 TableFieldSchema::new()
1860 .set_name("numbers")
1861 .set_type("INTEGER")
1862 .set_mode("REPEATED"),
1863 TableFieldSchema::new()
1864 .set_name("strings")
1865 .set_type("STRING")
1866 .set_mode("REPEATED"),
1867 TableFieldSchema::new()
1868 .set_name("pairs")
1869 .set_type("RECORD")
1870 .set_mode("REPEATED"),
1871 ]);
1872 let schema = Arc::new(Schema::new(table_schema));
1873
1874 let row0 = Row::try_new_from_arrow(&batch, 0, &schema)?;
1876 let numbers0: Vec<i64> = row0.get("numbers")?;
1877 assert_eq!(numbers0, vec![1, 2, 3]);
1878
1879 let strings0: Vec<String> = row0.get("strings")?;
1880 assert_eq!(strings0, vec!["a".to_string(), "b".to_string()]);
1881
1882 let pairs0_named: Vec<NamedZThenA> = row0.get("pairs")?;
1883 assert_eq!(pairs0_named, vec![NamedZThenA { z: 1, a: 2 }]);
1884
1885 let pairs0_pos: Vec<PositionalPair> = row0.get("pairs")?;
1886 assert_eq!(pairs0_pos, vec![PositionalPair(1, 2)]);
1887
1888 let row0_derived = ArrowNestedRow::try_from(row0)?;
1889 assert_eq!(
1890 row0_derived,
1891 ArrowNestedRow {
1892 numbers: vec![1, 2, 3],
1893 strings: vec!["a".to_string(), "b".to_string()],
1894 pairs: vec![PositionalPair(1, 2)],
1895 }
1896 );
1897
1898 let row1 = Row::try_new_from_arrow(&batch, 1, &schema)?;
1900 let numbers1: Vec<i64> = row1.get("numbers")?;
1901 assert_eq!(numbers1, vec![42]);
1902
1903 let strings1: Vec<String> = row1.get("strings")?;
1904 assert_eq!(strings1, vec!["c".to_string()]);
1905
1906 let pairs1_pos: Vec<PositionalPair> = row1.get("pairs")?;
1907 assert_eq!(pairs1_pos, vec![PositionalPair(10, 20)]);
1908
1909 let row1_derived = ArrowNestedRow::try_from(row1)?;
1910 assert_eq!(
1911 row1_derived,
1912 ArrowNestedRow {
1913 numbers: vec![42],
1914 strings: vec!["c".to_string()],
1915 pairs: vec![PositionalPair(10, 20)],
1916 }
1917 );
1918
1919 Ok(())
1920 }
1921 }
1922}