2121
2222from google .cloud import bigquery
2323import numpy as np
24+ import pandas as pd
2425import pyarrow as pa
2526import sqlglot as sg
2627import sqlglot .dialects .bigquery
2728import sqlglot .expressions as sge
2829
2930from bigframes import dtypes
3031from bigframes .core import guid , local_data , schema , utils
31- from bigframes .core .compile .sqlglot .expressions import typed_expr
32+ from bigframes .core .compile .sqlglot .expressions import constants , typed_expr
3233import bigframes .core .compile .sqlglot .sqlglot_types as sgt
3334
3435# shapely.wkt.dumps was moved to shapely.io.to_wkt in 2.0.
@@ -639,12 +640,30 @@ def _select_to_cte(expr: sge.Select, cte_name: sge.Identifier) -> sge.Select:
639640def _literal (value : typing .Any , dtype : dtypes .Dtype ) -> sge .Expression :
640641 sqlglot_type = sgt .from_bigframes_dtype (dtype ) if dtype else None
641642 if sqlglot_type is None :
642- if value is not None :
643- raise ValueError ("Cannot infer SQLGlot type from None dtype. " )
643+ if not pd . isna ( value ) :
644+ raise ValueError (f "Cannot infer SQLGlot type from None dtype: { value } " )
644645 return sge .Null ()
645646
646647 if value is None :
647648 return _cast (sge .Null (), sqlglot_type )
649+ if dtypes .is_struct_like (dtype ):
650+ items = [
651+ _literal (value = value [field_name ], dtype = field_dtype ).as_ (
652+ field_name , quoted = True
653+ )
654+ for field_name , field_dtype in dtypes .get_struct_fields (dtype ).items ()
655+ ]
656+ return sge .Struct .from_arg_list (items )
657+ elif dtypes .is_array_like (dtype ):
658+ value_type = dtypes .get_array_inner_type (dtype )
659+ values = sge .Array (
660+ expressions = [_literal (value = v , dtype = value_type ) for v in value ]
661+ )
662+ return values if len (value ) > 0 else _cast (values , sqlglot_type )
663+ elif pd .isna (value ):
664+ return _cast (sge .Null (), sqlglot_type )
665+ elif dtype == dtypes .JSON_DTYPE :
666+ return sge .ParseJSON (this = sge .convert (str (value )))
648667 elif dtype == dtypes .BYTES_DTYPE :
649668 return _cast (str (value ), sqlglot_type )
650669 elif dtypes .is_time_like (dtype ):
@@ -658,24 +677,12 @@ def _literal(value: typing.Any, dtype: dtypes.Dtype) -> sge.Expression:
658677 elif dtypes .is_geo_like (dtype ):
659678 wkt = value if isinstance (value , str ) else to_wkt (value )
660679 return sge .func ("ST_GEOGFROMTEXT" , sge .convert (wkt ))
661- elif dtype == dtypes .JSON_DTYPE :
662- return sge .ParseJSON (this = sge .convert (str (value )))
663680 elif dtype == dtypes .TIMEDELTA_DTYPE :
664681 return sge .convert (utils .timedelta_to_micros (value ))
665- elif dtypes .is_struct_like (dtype ):
666- items = [
667- _literal (value = value [field_name ], dtype = field_dtype ).as_ (
668- field_name , quoted = True
669- )
670- for field_name , field_dtype in dtypes .get_struct_fields (dtype ).items ()
671- ]
672- return sge .Struct .from_arg_list (items )
673- elif dtypes .is_array_like (dtype ):
674- value_type = dtypes .get_array_inner_type (dtype )
675- values = sge .Array (
676- expressions = [_literal (value = v , dtype = value_type ) for v in value ]
677- )
678- return values if len (value ) > 0 else _cast (values , sqlglot_type )
682+ elif dtype == dtypes .FLOAT_DTYPE :
683+ if np .isinf (value ):
684+ return constants ._INF if value > 0 else constants ._NEG_INF
685+ return sge .convert (value )
679686 else :
680687 if isinstance (value , np .generic ):
681688 value = value .item ()
0 commit comments