44# https://peps.python.org/pep-0249/
55#
66import logging
7+ import re
78from datetime import date , datetime
89from typing import (
910 Any ,
2425 SQLiteCloudConfig ,
2526 SQLiteCloudConnect ,
2627 SQLiteCloudException ,
28+ SQLiteDataTypes ,
2729)
2830from sqlitecloud .driver import Driver
2931from sqlitecloud .resultset import (
5153PARSE_COLNAMES = 2
5254
5355# Adapters registry to convert Python types to SQLite types
54- _adapters = {}
56+ adapters : Dict [ Type [ Any ], Callable [[ Any ], SQLiteDataTypes ]] = {}
5557# Converters registry to convert SQLite types to Python types
56- _converters = {}
58+ converters : Dict [ str , Callable [[ bytes ], Any ]] = {}
5759
5860
5961@overload
@@ -148,8 +150,8 @@ def register_adapter(
148150 callable (Callable): The callable that converts the type into a supported
149151 SQLite supported type.
150152 """
151- global _adapters
152- _adapters [pytype ] = adapter_callable
153+ registry = _get_adapters_registry ()
154+ registry [pytype ] = adapter_callable
153155
154156
155157def register_converter (type_name : str , converter : Callable [[bytes ], Any ]) -> None :
@@ -161,8 +163,16 @@ def register_converter(type_name: str, converter: Callable[[bytes], Any]) -> Non
161163 The match with the name of the type in the query is case-insensitive.
162164 converter (Callable): The callable that converts the bytestring into the custom Python type.
163165 """
164- global _converters
165- _converters [type_name .lower ()] = converter
166+ registry = _get_converters_registry ()
167+ registry [type_name .lower ()] = converter
168+
169+
170+ def _get_adapters_registry () -> dict :
171+ return adapters
172+
173+
174+ def _get_converters_registry () -> dict :
175+ return converters
166176
167177
168178class Connection :
@@ -307,8 +317,8 @@ def _apply_adapter(self, value: Any) -> SQLiteTypes:
307317 Returns:
308318 SQLiteTypes: The SQLite supported type or the given value when no adapter is found.
309319 """
310- if type (value ) in _adapters :
311- return _adapters [type (value )](value )
320+ if type (value ) in adapters :
321+ return adapters [type (value )](value )
312322
313323 if hasattr (value , "__conform__" ):
314324 # we don't support sqlite3.PrepareProtocol
@@ -365,7 +375,7 @@ def description(
365375 for i in range (self ._resultset .ncols ):
366376 description += (
367377 (
368- self ._resultset .colname [i ],
378+ self ._parse_colname ( self . _resultset .colname [i ])[ 0 ],
369379 None ,
370380 None ,
371381 None ,
@@ -541,6 +551,28 @@ def setinputsizes(self, sizes) -> None:
541551 def setoutputsize (self , size , column = None ) -> None :
542552 pass
543553
554+ def _parse_colname (self , colname : str ) -> Tuple [str , str ]:
555+ """
556+ Parse the column name to extract the column name and the
557+ declared type if present when it follows the syntax `colname [decltype]`.
558+
559+ Args:
560+ colname (str): The column name with optional declared type.
561+ Eg: "mycol [mytype]"
562+
563+ Returns:
564+ Tuple[str, str]: The column name and the declared type.
565+ Eg: ("mycol", "mytype")
566+ """
567+ # search for `[mytype]` in `mycol [mytype]`
568+ pattern = r"\[(.*?)\]"
569+
570+ matches = re .findall (pattern , colname )
571+ if not matches or len (matches ) == 0 :
572+ return colname , None
573+
574+ return colname .replace (f"[{ matches [0 ]} ]" , "" ).strip (), matches [0 ]
575+
544576 def _call_row_factory (self , row : Tuple ) -> object :
545577 if self .row_factory is None :
546578 return row
@@ -572,11 +604,26 @@ def _adapt_parameters(self, parameters: Union[Dict, Tuple]) -> Union[Dict, Tuple
572604
573605 return tuple (self ._connection ._apply_adapter (p ) for p in parameters )
574606
575- def _convert_value (self , value : Any , decltype : Optional [str ]) -> Any :
576- # todo: parse columns first
607+ def _convert_value (
608+ self , value : Any , colname : Optional [str ], decltype : Optional [str ]
609+ ) -> Any :
610+ if (
611+ colname
612+ and (self .connection .detect_types & PARSE_COLNAMES ) == PARSE_COLNAMES
613+ ):
614+ try :
615+ return self ._parse_colnames (value , colname )
616+ except MissingDecltypeException :
617+ pass
577618
578- if (self .connection .detect_types & PARSE_DECLTYPES ) == PARSE_DECLTYPES :
579- return self ._parse_decltypes (value , decltype )
619+ if (
620+ decltype
621+ and (self .connection .detect_types & PARSE_DECLTYPES ) == PARSE_DECLTYPES
622+ ):
623+ try :
624+ return self ._parse_decltypes (value , decltype )
625+ except MissingDecltypeException :
626+ pass
580627
581628 if decltype == SQLITECLOUD_VALUE_TYPE .TEXT .value or (
582629 decltype is None and isinstance (value , str )
@@ -585,16 +632,27 @@ def _convert_value(self, value: Any, decltype: Optional[str]) -> Any:
585632
586633 return value
587634
588- def _parse_decltypes (self , value : Any , decltype : str ) -> Any :
635+ def _parse_colnames (self , value : Any , colname : str ) -> Optional [Any ]:
636+ """Convert the value using the explicit type in the column name."""
637+ _ , decltype = self ._parse_colname (colname )
638+
639+ if decltype :
640+ return self ._parse_decltypes (value , decltype )
641+
642+ raise MissingDecltypeException (f"No decltype declared for: { decltype } " )
643+
644+ def _parse_decltypes (self , value : Any , decltype : str ) -> Optional [Any ]:
645+ """Convert the value by calling the registered converter for the given decltype."""
589646 decltype = decltype .lower ()
590- if decltype in _converters :
647+ registry = _get_converters_registry ()
648+ if decltype in registry :
591649 # sqlite3 always passes value as bytes
592650 value = (
593651 str (value ).encode ("utf-8" ) if not isinstance (value , bytes ) else value
594652 )
595- return _converters [decltype ](value )
653+ return registry [decltype ](value )
596654
597- return value
655+ raise MissingDecltypeException ( f"No decltype registered for: { decltype } " )
598656
599657 def _apply_text_factory (self , value : Any ) -> Any :
600658 """Use Connection.text_factory to convert value with TEXT column or
@@ -614,9 +672,10 @@ def _get_value(self, row: int, col: int) -> Optional[Any]:
614672 return None
615673
616674 value = self ._resultset .get_value (row , col )
675+ colname = self ._resultset .get_name (col )
617676 decltype = self ._resultset .get_decltype (col )
618677
619- return self ._convert_value (value , decltype )
678+ return self ._convert_value (value , colname , decltype )
620679
621680 def __iter__ (self ) -> "Cursor" :
622681 return self
@@ -638,6 +697,12 @@ def __next__(self) -> Optional[Tuple[Any]]:
638697 raise StopIteration
639698
640699
700+ class MissingDecltypeException (Exception ):
701+ def __init__ (self , message : str ) -> None :
702+ super ().__init__ (message )
703+ self .message = message
704+
705+
641706def register_adapters_and_converters ():
642707 """
643708 sqlite3 default adapters and converters.
0 commit comments