66from decimal import Decimal
77from enum import Enum
88from io import StringIO
9- from typing import Any , Dict , List , Optional , Sequence , Union
9+ from typing import Any , Dict , List , Optional , Sequence , Tuple , Union
1010
1111from sqlparse import parse as parse_sql # type: ignore
1212from sqlparse .sql import ( # type: ignore
@@ -234,7 +234,7 @@ def python_type(self) -> type:
234234def split_struct_fields (raw_struct : str ) -> List [str ]:
235235 """Split raw struct inner fields string into a list of field definitions.
236236 >>> split_struct_fields("field1 int, field2 struct(field1 int, field2 text)")
237- [" field1 int", " field2 struct(field1 int, field2 text)" ]
237+ [' field1 int', ' field2 struct(field1 int, field2 text)' ]
238238 """
239239 balance = 0 # keep track of the level of nesting, and only split on level 0
240240 separator = ","
@@ -246,15 +246,38 @@ def split_struct_fields(raw_struct: str) -> List[str]:
246246 elif ch == ")" :
247247 balance -= 1
248248 elif ch == separator and balance == 0 :
249- res .append (current .getvalue ())
249+ res .append (current .getvalue (). strip () )
250250 current = StringIO ()
251251 continue
252252 current .write (ch )
253253
254- res .append (current .getvalue ())
254+ res .append (current .getvalue (). strip () )
255255 return res
256256
257257
258+ def split_struct_field (raw_field : str ) -> Tuple [str , str ]:
259+ """Split raw struct field into name and type.
260+ >>> split_struct_field("field int")
261+ ('field', 'int')
262+ >>> split_struct_field("`with space` text null")
263+ ('with space', 'text null')
264+ >>> split_struct_field("s struct(`a b` int)")
265+ ('s', 'struct(`a b` int)')
266+ """
267+ raw_field = raw_field .strip ()
268+ second_tick = (
269+ raw_field .find ("`" , raw_field .find ("`" ) + 1 )
270+ if raw_field .startswith ("`" )
271+ else - 1
272+ )
273+ name , type_ = (
274+ (raw_field [: second_tick + 1 ], raw_field [second_tick + 1 :])
275+ if second_tick != - 1
276+ else raw_field .split (" " , 1 )
277+ )
278+ return name .strip (" `" ), type_ .strip ()
279+
280+
258281def parse_type (raw_type : str ) -> Union [type , ExtendedType ]: # noqa: C901
259282 """Parse typename provided by query metadata into Python type."""
260283 if not isinstance (raw_type , str ):
@@ -276,7 +299,7 @@ def parse_type(raw_type: str) -> Union[type, ExtendedType]: # noqa: C901
276299 fields_raw = split_struct_fields (raw_type [len (STRUCT ._prefix ) : - 1 ])
277300 fields = {}
278301 for f in fields_raw :
279- name , type_ = f . strip (). split ( " " , 1 )
302+ name , type_ = split_struct_field ( f )
280303 fields [name .strip ()] = parse_type (type_ .strip ())
281304 return STRUCT (fields )
282305 except ValueError :
0 commit comments