1use crate::error::{ConvertError, RowError};
16use crate::query::from_sql::SqlValueInner;
17use crate::query::{FromSql, Schema};
18use google_cloud_bigquery_v2::model::TableFieldSchema;
19use std::sync::Arc;
20use wkt::{ListValue, Struct, Value};
21
22pub type Result<T> = std::result::Result<T, RowError>;
23
24#[derive(Clone, Debug)]
65pub struct Row {
66 pub(crate) values: Vec<SqlValueInner>,
67 pub(crate) schema: Arc<Schema>,
68}
69
70mod sealed {
71 use super::{Row, SqlValueInner};
72 use crate::error::ConvertError;
73 use crate::query::SqlValue;
74
75 pub trait ColumnIndex {
77 fn index(&self, row: &Row) -> Option<usize>;
79
80 fn take_sql_value(
82 &self,
83 value: &mut SqlValue,
84 ) -> std::result::Result<SqlValue, ConvertError>;
85 }
86
87 impl ColumnIndex for usize {
88 fn index(&self, row: &Row) -> Option<usize> {
89 row.schema.get_field_by_index(*self).map(|_| *self)
90 }
91
92 fn take_sql_value(
93 &self,
94 value: &mut SqlValue,
95 ) -> std::result::Result<SqlValue, ConvertError> {
96 match &mut value.inner {
97 SqlValueInner::Struct(entries) => {
98 let (_, slot) = entries
99 .get_mut(*self)
100 .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
101 Ok(SqlValue::from_inner(std::mem::replace(
102 slot,
103 SqlValueInner::Null,
104 )))
105 }
106 SqlValueInner::Array(arr) => {
107 let slot = arr
108 .get_mut(*self)
109 .ok_or_else(|| ConvertError::MissingField(self.to_string()))?;
110 Ok(SqlValue::from_inner(std::mem::replace(
111 slot,
112 SqlValueInner::Null,
113 )))
114 }
115 SqlValueInner::String(s) => {
116 let arr: Vec<wkt::Value> =
117 serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
118 value.inner = SqlValueInner::from_wkt(wkt::Value::Array(arr));
119 self.take_sql_value(value)
120 }
121 SqlValueInner::Null => Err(ConvertError::NotNull),
122 other => Err(ConvertError::type_mismatch(
123 "struct, array, or string",
124 other,
125 )),
126 }
127 }
128 }
129
130 impl ColumnIndex for &str {
131 fn index(&self, row: &Row) -> Option<usize> {
132 row.schema.get_field_index_by_name(self)
133 }
134
135 fn take_sql_value(
136 &self,
137 value: &mut SqlValue,
138 ) -> std::result::Result<SqlValue, ConvertError> {
139 match &mut value.inner {
140 SqlValueInner::Struct(entries) => {
141 let (_, slot) = entries
142 .iter_mut()
143 .find(|(name, _)| name == *self)
144 .ok_or_else(|| ConvertError::MissingField((*self).to_string()))?;
145 Ok(SqlValue::from_inner(std::mem::replace(
146 slot,
147 SqlValueInner::Null,
148 )))
149 }
150 SqlValueInner::String(s) => {
151 let obj: wkt::Struct =
152 serde_json::from_str(s).map_err(|e| ConvertError::Convert(Box::new(e)))?;
153 value.inner = SqlValueInner::from_wkt(wkt::Value::Object(obj));
154 self.take_sql_value(value)
155 }
156 SqlValueInner::Null => Err(ConvertError::NotNull),
157 other => Err(ConvertError::type_mismatch("object or string", other)),
158 }
159 }
160 }
161
162 impl ColumnIndex for String {
163 fn index(&self, row: &Row) -> Option<usize> {
164 <&str as ColumnIndex>::index(&self.as_str(), row)
165 }
166
167 fn take_sql_value(
168 &self,
169 value: &mut SqlValue,
170 ) -> std::result::Result<SqlValue, ConvertError> {
171 self.as_str().take_sql_value(value)
172 }
173 }
174}
175
176pub trait ColumnIndex: sealed::ColumnIndex + std::fmt::Display {}
180
181impl ColumnIndex for usize {}
182impl ColumnIndex for &str {}
183impl ColumnIndex for String {}
184
185impl Row {
186 pub(crate) fn try_new(row: Struct, schema: &Arc<Schema>) -> Result<Self> {
187 let values = convert_row(row, schema.fields())?;
188
189 Ok(Self {
190 values,
191 schema: schema.clone(),
192 })
193 }
194
195 fn resolve_index<I: ColumnIndex>(&self, col: &I) -> Result<usize> {
196 sealed::ColumnIndex::index(col, self)
197 .ok_or_else(|| RowError::ColumnNotFound(format!("{col}")))
198 }
199
200 fn convert_value_at<T: FromSql>(&self, idx: usize, val: SqlValueInner) -> Result<T> {
201 T::from_value(crate::query::SqlValue::from_inner(val)).map_err(|e| {
202 let (column, sql_type) = self
203 .schema
204 .get_field_by_index(idx)
205 .map(|f| (f.name.clone(), f.r#type.clone()))
206 .unwrap_or_else(|| (idx.to_string(), "UNKNOWN".to_string()));
207 RowError::TypeConversion {
208 column,
209 sql_type,
210 source: e,
211 }
212 })
213 }
214
215 pub fn get<T: FromSql, I: ColumnIndex>(&self, index: I) -> Result<T> {
234 let idx = self.resolve_index(&index)?;
235 let val = self
236 .values
237 .get(idx)
238 .ok_or_else(|| RowError::IndexOutOfRange {
239 index: idx,
240 len: self.schema.len(),
241 })?;
242
243 self.convert_value_at(idx, val.clone())
244 }
245
246 pub fn take<T: FromSql, I: ColumnIndex>(&mut self, index: I) -> Result<T> {
285 let idx = self.resolve_index(&index)?;
286
287 let val = self
288 .values
289 .get_mut(idx)
290 .ok_or_else(|| RowError::IndexOutOfRange {
291 index: idx,
292 len: self.schema.len(),
293 })?;
294
295 let owned_val = std::mem::replace(val, SqlValueInner::Null);
297 self.convert_value_at(idx, owned_val)
298 }
299}
300
301fn convert_row(row: Struct, fields: &[TableFieldSchema]) -> Result<Vec<SqlValueInner>> {
302 let field_list = get_field_list(row)?;
303
304 if field_list.len() != fields.len() {
305 return Err(RowError::InvalidRowFormat(format!(
306 "schema and row cell mismatch (expected {}, got {})",
307 fields.len(),
308 field_list.len()
309 )));
310 }
311
312 field_list
313 .into_iter()
314 .zip(fields)
315 .map(|(cell, field)| convert_value(get_field_value(cell)?, field))
316 .collect()
317}
318
319fn get_field_list(mut row: Struct) -> Result<Vec<Value>> {
320 match row.remove("f") {
321 Some(Value::Array(arr)) => Ok(arr),
322 Some(_) => Err(RowError::InvalidRowFormat("invalid field values".into())),
323 None => Err(RowError::InvalidRowFormat("missing field values".into())),
324 }
325}
326
327fn get_field_value(value: Value) -> Result<Value> {
328 match value {
329 Value::Object(mut obj) => match obj.remove("v") {
330 Some(val) => Ok(val),
331 None => Err(RowError::InvalidRowFormat("missing field value".into())),
332 },
333 _ => Err(RowError::InvalidRowFormat("invalid field value".into())),
334 }
335}
336
337fn convert_value(value: Value, field: &TableFieldSchema) -> Result<SqlValueInner> {
338 match value {
339 Value::Null => Ok(SqlValueInner::Null),
340 Value::String(v) => convert_basic_type(v, &field.name, &field.r#type),
341 Value::Object(v) => convert_nested(v, &field.fields),
342 Value::Array(v) => convert_repeated(v, field),
343 _ => Err(RowError::InvalidRowFormat(format!(
344 "cell value is not an object: value={:?}, field_type={:?}",
345 value, field.r#type
346 ))),
347 }
348}
349
350fn convert_repeated(value: ListValue, field: &TableFieldSchema) -> Result<SqlValueInner> {
351 let arr = value
352 .into_iter()
353 .map(|cell| {
354 let val = get_field_value(cell)?;
356 convert_value(val, field)
357 })
358 .collect::<Result<Vec<_>>>()?;
359 Ok(SqlValueInner::Array(arr))
360}
361
362fn convert_nested(value: Struct, fields: &[TableFieldSchema]) -> Result<SqlValueInner> {
363 let values = convert_row(value, fields)?;
364 let entries = fields
365 .iter()
366 .zip(values)
367 .map(|(field, value)| (field.name.clone(), value))
368 .collect();
369 Ok(SqlValueInner::Struct(entries))
370}
371
372fn convert_basic_type(value: String, field_name: &str, field_type: &str) -> Result<SqlValueInner> {
373 match field_type {
374 "STRING" | "BYTES" | "TIMESTAMP" | "DATE" | "TIME" | "DATETIME" | "NUMERIC"
375 | "BIGNUMERIC" | "BIGINT" | "GEOGRAPHY" | "JSON" | "INTERVAL" | "RANGE" => {
376 Ok(SqlValueInner::String(value))
377 }
378 "INTEGER" | "INT64" => {
379 let num = value.parse::<i64>().map_err(|e| RowError::TypeConversion {
380 column: field_name.to_string(),
381 sql_type: field_type.to_string(),
382 source: ConvertError::Convert(Box::new(e)),
383 })?;
384 Ok(SqlValueInner::Number(serde_json::Number::from(num)))
385 }
386 "FLOAT" | "FLOAT64" => {
387 let num = value.parse::<f64>().map_err(|e| RowError::TypeConversion {
388 column: field_name.to_string(),
389 sql_type: field_type.to_string(),
390 source: ConvertError::Convert(Box::new(e)),
391 })?;
392 match serde_json::Number::from_f64(num) {
393 Some(n) => Ok(SqlValueInner::Number(n)),
394 None => Ok(SqlValueInner::String(value)),
395 }
396 }
397 "BOOLEAN" | "BOOL" => {
398 let b = if value.eq_ignore_ascii_case("true") {
399 true
400 } else if value.eq_ignore_ascii_case("false") {
401 false
402 } else {
403 return Err(RowError::TypeConversion {
404 column: field_name.to_string(),
405 sql_type: field_type.to_string(),
406 source: ConvertError::Convert(
407 "provided string was not `true` or `false`".into(),
408 ),
409 });
410 };
411 Ok(SqlValueInner::Bool(b))
412 }
413 _ => Err(RowError::InvalidRowFormat(format!(
414 "unknown field type: {} at column {}",
415 field_type, field_name
416 ))),
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use crate as google_cloud_bigquery;
424 use crate::query::FromRow;
425 use google_cloud_bigquery_v2::model::{TableFieldSchema, TableSchema};
426 use google_cloud_type::model::Decimal;
427 use rust_decimal::Decimal as RustDecimal;
428 use serde_json::{Map, json};
429 use test_case::test_case;
430
431 type TestResult = anyhow::Result<()>;
432
433 #[tokio::test]
434 async fn convert_basic_types_from_row() -> TestResult {
435 let raw_row = Map::from_iter([(
436 "f".to_string(),
437 json!([
438 { "v": "James" },
439 { "v": "272793" },
440 { "v": "TRUE" },
441 { "v": null },
442 { "v": "64.0" },
443 ]),
444 )]);
445 let schema = TableSchema::new().set_fields([
446 TableFieldSchema::new()
447 .set_name("name")
448 .set_type("STRING")
449 .set_mode("NULLABLE"),
450 TableFieldSchema::new()
451 .set_name("some_int")
452 .set_type("INTEGER")
453 .set_mode("NULLABLE"),
454 TableFieldSchema::new()
455 .set_name("some_bool")
456 .set_type("BOOLEAN")
457 .set_mode("NULLABLE"),
458 TableFieldSchema::new()
459 .set_name("some_null")
460 .set_type("INTEGER")
461 .set_mode("NULLABLE"),
462 TableFieldSchema::new()
463 .set_name("some_float")
464 .set_type("FLOAT64")
465 .set_mode("NULLABLE"),
466 ]);
467 let schema = Arc::new(Schema::new(schema));
468 let mut row = Row::try_new(raw_row, &schema)?;
469
470 assert_eq!(row.get::<String, _>(0)?, "James");
471 assert_eq!(row.get::<String, _>("name")?, "James");
472
473 assert_eq!(row.get::<i32, _>(1)?, 272793);
474 assert_eq!(row.get::<i32, _>("some_int")?, 272793);
475 assert_eq!(row.get::<i64, _>(1)?, 272793);
476 assert_eq!(row.get::<i64, _>("some_int")?, 272793);
477
478 assert!(row.get::<bool, _>(2)?);
479 assert!(row.get::<bool, _>("some_bool")?);
480
481 assert_eq!(row.get::<Option<i64>, _>(3)?, None);
482 assert_eq!(row.get::<Option<i64>, _>("some_null")?, None);
483
484 assert_eq!(row.get::<f32, _>(4)?, 64.0);
485 assert_eq!(row.get::<f32, _>("some_float")?, 64.0);
486 assert_eq!(row.get::<f64, _>(4)?, 64.0);
487 assert_eq!(row.get::<f64, _>("some_float")?, 64.0);
488
489 assert_eq!(row.take::<String, _>(0)?, "James");
490 assert_eq!(row.get::<Option<String>, _>(0)?, None);
491
492 assert_eq!(row.take::<i32, _>(1)?, 272793);
493 assert_eq!(row.get::<Option<i32>, _>(1)?, None);
494
495 assert!(row.take::<bool, _>(2)?);
496 assert_eq!(row.get::<Option<bool>, _>(2)?, None);
497
498 assert_eq!(row.take::<Option<i64>, _>(3)?, None);
499 assert_eq!(row.get::<Option<i64>, _>(3)?, None);
500
501 assert_eq!(row.take::<f32, _>(4)?, 64.0);
502 assert_eq!(row.get::<Option<f32>, _>(4)?, None);
503
504 Ok(())
505 }
506
507 #[tokio::test]
508 async fn convert_numeric_from_row() -> TestResult {
509 let raw_row = Map::from_iter([(
510 "f".to_string(),
511 json!([
512 { "v": "123.456" },
513 { "v": "99999999999999999999.123456789" },
514 { "v": "99999999999999999999999999999999.123" },
515 ]),
516 )]);
517 let schema = TableSchema::new().set_fields([
518 TableFieldSchema::new()
519 .set_name("price")
520 .set_type("NUMERIC")
521 .set_mode("NULLABLE"),
522 TableFieldSchema::new()
523 .set_name("big_amount")
524 .set_type("BIGNUMERIC")
525 .set_mode("NULLABLE"),
526 TableFieldSchema::new()
527 .set_name("overflow_amount")
528 .set_type("BIGNUMERIC")
529 .set_mode("NULLABLE"),
530 ]);
531 let schema = Arc::new(Schema::new(schema));
532 let mut row = Row::try_new(raw_row, &schema)?;
533
534 assert_eq!(
535 row.get::<Decimal, _>(0)?,
536 Decimal::new().set_value("123.456")
537 );
538 assert_eq!(
539 row.get::<Decimal, _>("price")?,
540 Decimal::new().set_value("123.456")
541 );
542
543 assert_eq!(
544 row.get::<Decimal, _>(1)?,
545 Decimal::new().set_value("99999999999999999999.123456789")
546 );
547 assert_eq!(
548 row.get::<Decimal, _>("big_amount")?,
549 Decimal::new().set_value("99999999999999999999.123456789")
550 );
551
552 assert_eq!(
553 row.get::<RustDecimal, _>(0)?,
554 "123.456".parse().expect("valid decimal")
555 );
556 assert_eq!(
557 row.get::<RustDecimal, _>("price")?,
558 "123.456".parse().expect("valid decimal")
559 );
560
561 assert_eq!(
562 row.get::<RustDecimal, _>(1)?,
563 "99999999999999999999.123456789"
564 .parse()
565 .expect("valid decimal")
566 );
567 assert_eq!(
568 row.get::<RustDecimal, _>("big_amount")?,
569 "99999999999999999999.123456789"
570 .parse()
571 .expect("valid decimal")
572 );
573
574 assert!(row.get::<RustDecimal, _>(2).is_err());
575 assert!(row.get::<RustDecimal, _>("overflow_amount").is_err());
576
577 assert_eq!(
578 row.take::<Decimal, _>(0)?,
579 Decimal::new().set_value("123.456")
580 );
581 assert_eq!(row.get::<Option<Decimal>, _>(0)?, None);
582
583 assert_eq!(
584 row.take::<RustDecimal, _>(1)?,
585 "99999999999999999999.123456789".parse()?
586 );
587 assert_eq!(row.get::<Option<RustDecimal>, _>(1)?, None);
588
589 Ok(())
590 }
591
592 #[tokio::test]
593 async fn convert_bytes_from_row() -> TestResult {
594 let raw_row = Map::from_iter([(
595 "f".to_string(),
596 json!([
597 { "v": "AQIDBA==" },
598 { "v": "SGVsbG8=" },
599 { "v": null },
600 ]),
601 )]);
602 let schema = TableSchema::new().set_fields([
603 TableFieldSchema::new()
604 .set_name("payload_vec")
605 .set_type("BYTES")
606 .set_mode("NULLABLE"),
607 TableFieldSchema::new()
608 .set_name("payload_bytes")
609 .set_type("BYTES")
610 .set_mode("NULLABLE"),
611 TableFieldSchema::new()
612 .set_name("null_bytes")
613 .set_type("BYTES")
614 .set_mode("NULLABLE"),
615 ]);
616 let schema = Arc::new(Schema::new(schema));
617 let mut row = Row::try_new(raw_row, &schema)?;
618
619 assert_eq!(row.get::<Vec<u8>, _>(0)?, vec![1, 2, 3, 4]);
620 assert_eq!(row.get::<Vec<u8>, _>("payload_vec")?, vec![1, 2, 3, 4]);
621
622 assert_eq!(
623 row.get::<bytes::Bytes, _>(1)?,
624 bytes::Bytes::from_static(b"Hello")
625 );
626 assert_eq!(
627 row.get::<bytes::Bytes, _>("payload_bytes")?,
628 bytes::Bytes::from_static(b"Hello")
629 );
630
631 assert_eq!(row.get::<Option<Vec<u8>>, _>(2)?, None);
632 assert_eq!(row.get::<Option<bytes::Bytes>, _>("null_bytes")?, None);
633
634 assert_eq!(row.take::<Vec<u8>, _>(0)?, vec![1, 2, 3, 4]);
635 assert_eq!(row.get::<Option<Vec<u8>>, _>(0)?, None);
636
637 assert_eq!(
638 row.take::<bytes::Bytes, _>(1)?,
639 bytes::Bytes::from_static(b"Hello")
640 );
641 assert_eq!(row.get::<Option<bytes::Bytes>, _>(1)?, None);
642
643 Ok(())
644 }
645
646 #[tokio::test]
647 async fn convert_record_from_row() -> TestResult {
648 let raw_row = Map::from_iter([(
649 "f".to_string(),
650 json!([
651 {
652 "v": {
653 "f": [
654 { "v": "Alice" },
655 { "v": "25" }
656 ]
657 }
658 }
659 ]),
660 )]);
661 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
662 .set_name("user")
663 .set_type("RECORD")
664 .set_mode("NULLABLE")
665 .set_fields([
666 TableFieldSchema::new()
667 .set_name("name")
668 .set_type("STRING")
669 .set_mode("NULLABLE"),
670 TableFieldSchema::new()
671 .set_name("age")
672 .set_type("INTEGER")
673 .set_mode("NULLABLE"),
674 ])]);
675 let schema = Arc::new(Schema::new(schema));
676 let mut row = Row::try_new(raw_row, &schema)?;
677
678 let expected: Struct = serde_json::from_value(json!({
679 "name": "Alice",
680 "age": 25,
681 }))?;
682 assert_eq!(row.get::<Struct, _>(0)?, expected);
683 assert_eq!(row.get::<Struct, _>("user")?, expected);
684 assert_eq!(row.get::<Struct, _>("user".to_string())?, expected);
685 assert_eq!(row.take::<Struct, _>("user")?, expected);
686 assert_eq!(row.get::<Option<Struct>, _>("user")?, None);
687
688 Ok(())
689 }
690
691 #[derive(crate::query::FromSql, Debug, PartialEq)]
692 struct JsonPayload {
693 name: String,
694 age: i64,
695 }
696
697 #[tokio::test]
698 async fn convert_json_from_row() -> TestResult {
699 let json_str = json!({"name": "Alice", "age": 30}).to_string();
700 let raw_row = Map::from_iter([(
701 "f".to_string(),
702 json!([
703 { "v": json_str },
704 { "v": null },
705 ]),
706 )]);
707 let schema = TableSchema::new().set_fields([
708 TableFieldSchema::new()
709 .set_name("json_obj")
710 .set_type("JSON")
711 .set_mode("NULLABLE"),
712 TableFieldSchema::new()
713 .set_name("json_null")
714 .set_type("JSON")
715 .set_mode("NULLABLE"),
716 ]);
717 let schema = Arc::new(Schema::new(schema));
718 let row = Row::try_new(raw_row, &schema)?;
719
720 let expected_struct: Struct = serde_json::from_value(json!({
721 "name": "Alice",
722 "age": 30,
723 }))?;
724 assert_eq!(row.get::<String, _>("json_obj")?, json_str);
725 assert_eq!(row.get::<Struct, _>("json_obj")?, expected_struct);
726 assert_eq!(
727 row.get::<JsonPayload, _>("json_obj")?,
728 JsonPayload {
729 name: "Alice".to_string(),
730 age: 30,
731 }
732 );
733 assert_eq!(row.get::<Option<Struct>, _>("json_null")?, None);
734 assert_eq!(row.get::<Option<JsonPayload>, _>("json_null")?, None);
735
736 Ok(())
737 }
738
739 #[tokio::test]
740 async fn convert_repeated_from_row() -> TestResult {
741 let raw_row = Map::from_iter([(
742 "f".to_string(),
743 json!([
744 {
745 "v": [
746 { "v": "1" },
747 { "v": "2" },
748 { "v": "3" }
749 ]
750 }
751 ]),
752 )]);
753 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
754 .set_name("numbers")
755 .set_type("INTEGER")
756 .set_mode("REPEATED")]);
757 let schema = Arc::new(Schema::new(schema));
758 let mut row = Row::try_new(raw_row, &schema)?;
759
760 assert_eq!(row.get::<Vec<i64>, _>(0)?, vec![1, 2, 3]);
761 assert_eq!(row.get::<Vec<i64>, _>("numbers")?, vec![1, 2, 3]);
762 assert_eq!(row.take::<Vec<i64>, _>("numbers")?, vec![1, 2, 3]);
763 assert_eq!(row.get::<Option<Vec<i64>>, _>("numbers")?, None);
764
765 Ok(())
766 }
767
768 #[tokio::test]
769 async fn convert_repeated_record_from_row() -> TestResult {
770 let raw_row = Map::from_iter([(
771 "f".to_string(),
772 json!([
773 {
774 "v": [
775 {
776 "v": {
777 "f": [
778 { "v": "Bob" },
779 { "v": "28" }
780 ]
781 }
782 },
783 {
784 "v": {
785 "f": [
786 { "v": "Charlie" },
787 { "v": "31" }
788 ]
789 }
790 }
791 ]
792 }
793 ]),
794 )]);
795 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
796 .set_name("users")
797 .set_type("RECORD")
798 .set_mode("REPEATED")
799 .set_fields([
800 TableFieldSchema::new()
801 .set_name("name")
802 .set_type("STRING")
803 .set_mode("NULLABLE"),
804 TableFieldSchema::new()
805 .set_name("age")
806 .set_type("INTEGER")
807 .set_mode("NULLABLE"),
808 ])]);
809 let schema = Arc::new(Schema::new(schema));
810 let mut row = Row::try_new(raw_row, &schema)?;
811
812 let expected: Vec<Struct> = serde_json::from_value(json!([
813 {
814 "name": "Bob",
815 "age": 28,
816 },
817 {
818 "name": "Charlie",
819 "age": 31,
820 },
821 ]))?;
822 assert_eq!(row.get::<Vec<Struct>, _>(0)?, expected);
823 assert_eq!(row.get::<Vec<Struct>, _>("users")?, expected);
824 assert_eq!(row.take::<Vec<Struct>, _>("users")?, expected);
825 assert_eq!(row.get::<Option<Vec<Struct>>, _>("users")?, None);
826
827 Ok(())
828 }
829
830 #[test_case("INTEGER", "123", Value::Number(123.into()); "integer positive")]
831 #[test_case("INTEGER", "-456", Value::Number((-456).into()); "integer negative")]
832 #[test_case("INT64", "9223372036854775807", Value::Number(9223372036854775807_i64.into()); "int64 max")]
833 #[test_case("FLOAT", "123.45", Value::Number(serde_json::Number::from_f64(123.45).unwrap()); "float success")]
834 #[test_case("FLOAT64", "NaN", Value::String("NaN".to_string()); "float NaN")]
835 #[test_case("FLOAT64", "+inf", Value::String("+inf".to_string()); "float positive infinity")]
836 #[test_case("FLOAT64", "-inf", Value::String("-inf".to_string()); "float negative infinity")]
837 #[test_case("BOOLEAN", "true", Value::Bool(true); "boolean true lowercase")]
838 #[test_case("BOOLEAN", "TRUE", Value::Bool(true); "boolean true uppercase")]
839 #[test_case("BOOL", "false", Value::Bool(false); "bool false")]
840 #[test_case("JSON", r#"{"a":1}"#, Value::String(r#"{"a":1}"#.to_string()); "json string")]
841 fn convert_basic_type_cases_success(field_type: &str, value: &str, expected: Value) {
842 let res = convert_basic_type(value.to_string(), "test_col", field_type);
843 let value = res.expect("should succeed");
844 assert_eq!(
845 value,
846 crate::query::from_sql::SqlValueInner::from_wkt(expected)
847 );
848 }
849
850 #[test_case("INTEGER", "abc"; "integer invalid")]
851 #[test_case("INT64", "9223372036854775808"; "int64 overflow")]
852 #[test_case("FLOAT", "abc"; "float invalid")]
853 #[test_case("BOOL", "invalid"; "bool invalid")]
854 fn convert_basic_type_cases_conversion_fail(field_type: &str, value: &str) {
855 let res = convert_basic_type(value.to_string(), "test_col", field_type);
856 let err = res.unwrap_err();
857 assert!(matches!(err, RowError::TypeConversion { .. }));
858 }
859
860 #[test]
861 fn convert_basic_type_invalid_row_format() {
862 let res = convert_basic_type("value".to_string(), "test_col", "UNKNOWN");
863 let err = res.unwrap_err();
864 assert!(matches!(err, RowError::InvalidRowFormat(_)));
865 }
866
867 #[test]
868 fn convert_value_unsupported_value() {
869 let field = TableFieldSchema::new()
870 .set_name("test_col")
871 .set_type("BOOLEAN")
872 .set_mode("NULLABLE");
873 let res = convert_value(Value::Bool(true), &field);
874 let err = res.unwrap_err();
875 assert!(matches!(err, RowError::InvalidRowFormat(_)));
876 }
877
878 #[derive(FromRow, Debug, PartialEq)]
879 struct TestRow {
880 name: String,
881 #[bigquery(rename = "custom_int")]
882 some_int: i64,
883 some_bool: bool,
884 some_null: Option<i64>,
885 }
886
887 #[tokio::test]
888 async fn derive_from_row_success() -> TestResult {
889 let raw_row = Map::from_iter([(
890 "f".to_string(),
891 json!([
892 { "v": "James" },
893 { "v": "272793" },
894 { "v": "TRUE" },
895 { "v": null },
896 ]),
897 )]);
898 let schema = TableSchema::new().set_fields([
899 TableFieldSchema::new()
900 .set_name("name")
901 .set_type("STRING")
902 .set_mode("NULLABLE"),
903 TableFieldSchema::new()
904 .set_name("custom_int")
905 .set_type("INTEGER")
906 .set_mode("NULLABLE"),
907 TableFieldSchema::new()
908 .set_name("some_bool")
909 .set_type("BOOLEAN")
910 .set_mode("NULLABLE"),
911 TableFieldSchema::new()
912 .set_name("some_null")
913 .set_type("INTEGER")
914 .set_mode("NULLABLE"),
915 ]);
916 let schema = Arc::new(Schema::new(schema));
917 let row = Row::try_new(raw_row, &schema)?;
918
919 let converted_row = TestRow::try_from(row)?;
920 assert_eq!(
921 converted_row,
922 TestRow {
923 name: "James".to_string(),
924 some_int: 272793,
925 some_bool: true,
926 some_null: None,
927 }
928 );
929
930 Ok(())
931 }
932
933 #[derive(FromRow, Debug, PartialEq)]
934 struct RawIdentRow {
935 r#type: String,
936 r#match: i64,
937 }
938
939 #[tokio::test]
940 async fn derive_from_row_raw_identifier() -> TestResult {
941 let raw_row = Map::from_iter([(
942 "f".to_string(),
943 json!([
944 { "v": "click" },
945 { "v": "7" },
946 ]),
947 )]);
948 let schema = TableSchema::new().set_fields([
949 TableFieldSchema::new()
950 .set_name("type")
951 .set_type("STRING")
952 .set_mode("NULLABLE"),
953 TableFieldSchema::new()
954 .set_name("match")
955 .set_type("INTEGER")
956 .set_mode("NULLABLE"),
957 ]);
958 let schema = Arc::new(Schema::new(schema));
959 let row = Row::try_new(raw_row, &schema)?;
960
961 let converted = RawIdentRow::try_from(row)?;
962 assert_eq!(
963 converted,
964 RawIdentRow {
965 r#type: "click".to_string(),
966 r#match: 7,
967 }
968 );
969 Ok(())
970 }
971
972 #[tokio::test]
973 async fn derive_from_row_missing_column() -> TestResult {
974 let raw_row = Map::from_iter([(
975 "f".to_string(),
976 json!([
977 { "v": "James" },
978 { "v": "123" },
979 { "v": "TRUE" },
980 { "v": null },
981 ]),
982 )]);
983 let schema = TableSchema::new().set_fields([
984 TableFieldSchema::new()
985 .set_name("name")
986 .set_type("STRING")
987 .set_mode("NULLABLE"),
988 TableFieldSchema::new()
989 .set_name("wrong_col")
990 .set_type("INTEGER")
991 .set_mode("NULLABLE"),
992 TableFieldSchema::new()
993 .set_name("some_bool")
994 .set_type("BOOLEAN")
995 .set_mode("NULLABLE"),
996 TableFieldSchema::new()
997 .set_name("some_null")
998 .set_type("INTEGER")
999 .set_mode("NULLABLE"),
1000 ]);
1001 let schema = Arc::new(Schema::new(schema));
1002 let row = Row::try_new(raw_row, &schema)?;
1003
1004 let err = TestRow::try_from(row).unwrap_err();
1005 assert!(matches!(err, RowError::ColumnNotFound(col) if col == "custom_int"));
1006 Ok(())
1007 }
1008
1009 #[derive(FromRow, Debug, PartialEq)]
1010 struct ShadowedFieldNamesRow {
1011 row: i64,
1012 name: String,
1013 }
1014
1015 #[tokio::test]
1016 async fn derive_from_row_shadowing_field_names() -> TestResult {
1017 let raw_row = Map::from_iter([(
1018 "f".to_string(),
1019 json!([
1020 { "v": "42" },
1021 { "v": "Alice" },
1022 ]),
1023 )]);
1024 let schema = TableSchema::new().set_fields([
1025 TableFieldSchema::new()
1026 .set_name("row")
1027 .set_type("INTEGER")
1028 .set_mode("NULLABLE"),
1029 TableFieldSchema::new()
1030 .set_name("name")
1031 .set_type("STRING")
1032 .set_mode("NULLABLE"),
1033 ]);
1034 let schema = Arc::new(Schema::new(schema));
1035 let row = Row::try_new(raw_row, &schema)?;
1036
1037 let converted = ShadowedFieldNamesRow::try_from(row)?;
1038 assert_eq!(
1039 converted,
1040 ShadowedFieldNamesRow {
1041 row: 42,
1042 name: "Alice".to_string(),
1043 }
1044 );
1045 Ok(())
1046 }
1047
1048 #[derive(FromRow, Debug, PartialEq)]
1049 struct TupleRow(i64, String);
1050
1051 #[derive(crate::query::FromSql, Debug, PartialEq)]
1052 struct AnonTriple(i64, String, bool);
1053
1054 #[derive(crate::query::FromSql, Debug, PartialEq)]
1055 struct NamedZThenA {
1056 z: i64,
1057 a: i64,
1058 }
1059
1060 #[derive(crate::query::FromSql, Debug, PartialEq)]
1061 struct PositionalPair(i64, i64);
1062
1063 #[derive(crate::query::FromSql, Debug, PartialEq)]
1064 struct DupIdNamed {
1065 id: i64,
1066 }
1067
1068 #[tokio::test]
1069 async fn anonymous_struct_preserves_all_fields() -> TestResult {
1070 let raw_row = Map::from_iter([(
1073 "f".to_string(),
1074 json!([
1075 {
1076 "v": {
1077 "f": [
1078 { "v": "10" },
1079 { "v": "hello" },
1080 { "v": "true" }
1081 ]
1082 }
1083 }
1084 ]),
1085 )]);
1086 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1087 .set_name("anon")
1088 .set_type("RECORD")
1089 .set_mode("NULLABLE")
1090 .set_fields([
1091 TableFieldSchema::new()
1092 .set_name("")
1093 .set_type("INT64")
1094 .set_mode("NULLABLE"),
1095 TableFieldSchema::new()
1096 .set_name("")
1097 .set_type("STRING")
1098 .set_mode("NULLABLE"),
1099 TableFieldSchema::new()
1100 .set_name("")
1101 .set_type("BOOL")
1102 .set_mode("NULLABLE"),
1103 ])]);
1104 let schema = Arc::new(Schema::new(schema));
1105 let mut row = Row::try_new(raw_row, &schema)?;
1106
1107 let anon: AnonTriple = row.take("anon")?;
1108 assert_eq!(anon, AnonTriple(10, "hello".to_string(), true));
1109 Ok(())
1110 }
1111
1112 #[tokio::test]
1113 async fn struct_preserves_sql_declaration_order_over_alphabetical_order() -> TestResult {
1114 let raw_row = Map::from_iter([(
1117 "f".to_string(),
1118 json!([
1119 {
1120 "v": {
1121 "f": [
1122 { "v": "1" },
1123 { "v": "2" }
1124 ]
1125 }
1126 }
1127 ]),
1128 )]);
1129 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1130 .set_name("pair")
1131 .set_type("RECORD")
1132 .set_mode("NULLABLE")
1133 .set_fields([
1134 TableFieldSchema::new()
1135 .set_name("z")
1136 .set_type("INT64")
1137 .set_mode("NULLABLE"),
1138 TableFieldSchema::new()
1139 .set_name("a")
1140 .set_type("INT64")
1141 .set_mode("NULLABLE"),
1142 ])]);
1143 let schema = Arc::new(Schema::new(schema));
1144 let row = Row::try_new(raw_row, &schema)?;
1145
1146 let by_name: NamedZThenA = row.get("pair")?;
1148 assert_eq!(by_name, NamedZThenA { z: 1, a: 2 });
1149
1150 let by_pos: PositionalPair = row.get("pair")?;
1152 assert_eq!(by_pos, PositionalPair(1, 2));
1153 Ok(())
1154 }
1155
1156 #[tokio::test]
1157 async fn duplicate_struct_field_names_match_row_behavior() -> TestResult {
1158 let raw_row = Map::from_iter([(
1162 "f".to_string(),
1163 json!([
1164 {
1165 "v": {
1166 "f": [
1167 { "v": "100" },
1168 { "v": "200" }
1169 ]
1170 }
1171 }
1172 ]),
1173 )]);
1174 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1175 .set_name("dup")
1176 .set_type("RECORD")
1177 .set_mode("NULLABLE")
1178 .set_fields([
1179 TableFieldSchema::new()
1180 .set_name("id")
1181 .set_type("INT64")
1182 .set_mode("NULLABLE"),
1183 TableFieldSchema::new()
1184 .set_name("id")
1185 .set_type("INT64")
1186 .set_mode("NULLABLE"),
1187 ])]);
1188 let schema = Arc::new(Schema::new(schema));
1189 let row = Row::try_new(raw_row, &schema)?;
1190
1191 let first_id: DupIdNamed = row.get("dup")?;
1192 assert_eq!(first_id, DupIdNamed { id: 100 });
1193
1194 let both_ids: PositionalPair = row.get("dup")?;
1195 assert_eq!(both_ids, PositionalPair(100, 200));
1196 Ok(())
1197 }
1198
1199 #[tokio::test]
1200 async fn derive_from_row_tuple_struct() -> TestResult {
1201 let raw_row = Map::from_iter([(
1203 "f".to_string(),
1204 json!([
1205 { "v": "42" },
1206 { "v": "world" },
1207 ]),
1208 )]);
1209 let schema = TableSchema::new().set_fields([
1210 TableFieldSchema::new()
1211 .set_name("_f0")
1212 .set_type("INT64")
1213 .set_mode("NULLABLE"),
1214 TableFieldSchema::new()
1215 .set_name("_f1")
1216 .set_type("STRING")
1217 .set_mode("NULLABLE"),
1218 ]);
1219 let schema = Arc::new(Schema::new(schema));
1220 let row = Row::try_new(raw_row, &schema)?;
1221
1222 let converted = TupleRow::try_from(row)?;
1223 assert_eq!(converted, TupleRow(42, "world".to_string()));
1224
1225 Ok(())
1227 }
1228
1229 #[derive(FromSql, Debug, PartialEq)]
1230 struct NestedGeneric<U> {
1231 inner_val: U,
1232 }
1233
1234 #[derive(FromRow, Debug, PartialEq)]
1235 struct GenericRow<T: Clone + Default, U: std::fmt::Debug> {
1236 #[bigquery(rename = "custom_val")]
1237 single: T,
1238 optional: Option<T>,
1239 list: Vec<T>,
1240 nested: NestedGeneric<U>,
1241 common: i64,
1242 }
1243
1244 #[tokio::test]
1245 async fn derive_from_row_generic() -> TestResult {
1246 let raw_row = Map::from_iter([(
1247 "f".to_string(),
1248 json!([
1249 { "v": "100" },
1250 { "v": null },
1251 { "v": [{ "v": "1" }, { "v": "2" }, { "v": "3" }] },
1252 {
1253 "v": {
1254 "f": [
1255 { "v": "nested_value" }
1256 ]
1257 }
1258 },
1259 { "v": "1" },
1260 ]),
1261 )]);
1262 let schema = TableSchema::new().set_fields([
1263 TableFieldSchema::new()
1264 .set_name("custom_val")
1265 .set_type("INTEGER")
1266 .set_mode("NULLABLE"),
1267 TableFieldSchema::new()
1268 .set_name("optional")
1269 .set_type("INTEGER")
1270 .set_mode("NULLABLE"),
1271 TableFieldSchema::new()
1272 .set_name("list")
1273 .set_type("INTEGER")
1274 .set_mode("REPEATED"),
1275 TableFieldSchema::new()
1276 .set_name("nested")
1277 .set_type("RECORD")
1278 .set_mode("NULLABLE")
1279 .set_fields([TableFieldSchema::new()
1280 .set_name("inner_val")
1281 .set_type("STRING")
1282 .set_mode("NULLABLE")]),
1283 TableFieldSchema::new()
1284 .set_name("common")
1285 .set_type("INTEGER")
1286 .set_mode("NULLABLE"),
1287 ]);
1288 let schema = Arc::new(Schema::new(schema));
1289 let row = Row::try_new(raw_row, &schema)?;
1290
1291 let converted = GenericRow::<i64, String>::try_from(row)?;
1292 assert_eq!(
1293 converted,
1294 GenericRow {
1295 single: 100,
1296 optional: None,
1297 list: vec![1, 2, 3],
1298 nested: NestedGeneric {
1299 inner_val: "nested_value".to_string(),
1300 },
1301 common: 1,
1302 }
1303 );
1304 Ok(())
1305 }
1306
1307 #[derive(FromRow, Debug, PartialEq)]
1308 struct GenericRowWhere<T>
1309 where
1310 T: std::fmt::Debug + Clone,
1311 {
1312 val: T,
1313 }
1314
1315 #[tokio::test]
1316 async fn derive_from_row_generic_where_clause() -> TestResult {
1317 let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "hello" }]))]);
1318 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1319 .set_name("val")
1320 .set_type("STRING")
1321 .set_mode("NULLABLE")]);
1322 let schema = Arc::new(Schema::new(schema));
1323 let row = Row::try_new(raw_row, &schema)?;
1324
1325 let converted = GenericRowWhere::<String>::try_from(row)?;
1326 assert_eq!(
1327 converted,
1328 GenericRowWhere {
1329 val: "hello".to_string(),
1330 }
1331 );
1332 Ok(())
1333 }
1334
1335 #[derive(FromRow, Debug, PartialEq)]
1336 struct GenericRowDefault<T = i64> {
1337 val: T,
1338 }
1339
1340 #[tokio::test]
1341 async fn derive_from_row_generic_default_param() -> TestResult {
1342 let raw_row = Map::from_iter([("f".to_string(), json!([{ "v": "42" }]))]);
1343 let schema = TableSchema::new().set_fields([TableFieldSchema::new()
1344 .set_name("val")
1345 .set_type("INTEGER")
1346 .set_mode("NULLABLE")]);
1347 let schema = Arc::new(Schema::new(schema));
1348 let row = Row::try_new(raw_row, &schema)?;
1349
1350 let converted = GenericRowDefault::try_from(row)?;
1351 assert_eq!(converted, GenericRowDefault { val: 42 });
1352 Ok(())
1353 }
1354
1355 #[derive(FromRow, Debug, PartialEq)]
1356 struct GenericTupleRow<T, U>(T, Option<T>, U);
1357
1358 #[tokio::test]
1359 async fn derive_from_row_generic_tuple_struct() -> TestResult {
1360 let raw_row = Map::from_iter([(
1361 "f".to_string(),
1362 json!([
1363 { "v": "42" },
1364 { "v": null },
1365 { "v": "hello" },
1366 ]),
1367 )]);
1368 let schema = TableSchema::new().set_fields([
1369 TableFieldSchema::new()
1370 .set_name("_f0")
1371 .set_type("INT64")
1372 .set_mode("NULLABLE"),
1373 TableFieldSchema::new()
1374 .set_name("_f1")
1375 .set_type("INT64")
1376 .set_mode("NULLABLE"),
1377 TableFieldSchema::new()
1378 .set_name("_f2")
1379 .set_type("STRING")
1380 .set_mode("NULLABLE"),
1381 ]);
1382 let schema = Arc::new(Schema::new(schema));
1383 let row = Row::try_new(raw_row, &schema)?;
1384
1385 let converted = GenericTupleRow::<i64, String>::try_from(row)?;
1386 assert_eq!(converted, GenericTupleRow(42, None, "hello".to_string()));
1387 Ok(())
1388 }
1389}