55MySQL, BigQuery, Snowflake).
66"""
77
8- from typing import Any
8+ from functools import lru_cache
9+ from typing import Any , Final
910
1011from sqlspec .core .type_conversion import BaseTypeConverter
1112from sqlspec .utils .serializers import to_json
1213
14+ ADBC_SPECIAL_CHARS : Final [frozenset [str ]] = frozenset ({"{" , "[" , "-" , ":" , "T" , "." })
15+
1316
1417class ADBCTypeConverter (BaseTypeConverter ):
1518 """ADBC-specific type converter with dialect awareness.
1619
1720 Extends the base BaseTypeConverter with ADBC multi-backend functionality
1821 including dialect-specific type handling for different database systems.
22+ Includes per-instance LRU cache for improved performance.
1923 """
2024
21- __slots__ = ("dialect" , )
25+ __slots__ = ("_convert_cache" , "dialect" )
2226
23- def __init__ (self , dialect : str ) -> None :
24- """Initialize with dialect-specific configuration.
27+ def __init__ (self , dialect : str , cache_size : int = 5000 ) -> None :
28+ """Initialize with dialect-specific configuration and conversion cache .
2529
2630 Args:
2731 dialect: Target database dialect (postgres, sqlite, duckdb, etc.)
32+ cache_size: Maximum number of string values to cache (default: 5000)
2833 """
2934 super ().__init__ ()
3035 self .dialect = dialect .lower ()
3136
37+ @lru_cache (maxsize = cache_size )
38+ def _cached_convert (value : str ) -> Any :
39+ if not value or not any (c in value for c in ADBC_SPECIAL_CHARS ):
40+ return value
41+ detected_type = self .detect_type (value )
42+ if detected_type :
43+ try :
44+ if self .dialect in {"postgres" , "postgresql" }:
45+ if detected_type in {"uuid" , "interval" }:
46+ return self .convert_value (value , detected_type )
47+ elif self .dialect == "duckdb" :
48+ if detected_type == "uuid" :
49+ return self .convert_value (value , detected_type )
50+ elif self .dialect == "sqlite" :
51+ if detected_type == "uuid" :
52+ return str (value )
53+ elif self .dialect == "bigquery" :
54+ if detected_type == "uuid" :
55+ return self .convert_value (value , detected_type )
56+ elif self .dialect in {"mysql" , "snowflake" } and detected_type in {"uuid" , "json" }:
57+ return self .convert_value (value , detected_type )
58+ return self .convert_value (value , detected_type )
59+ except Exception :
60+ return value
61+ return value
62+
63+ self ._convert_cache = _cached_convert
64+
3265 def convert_if_detected (self , value : Any ) -> Any :
33- """Convert value with dialect-specific handling.
66+ """Convert value with dialect-specific handling (cached) .
3467
3568 Args:
3669 value: Value to potentially convert.
@@ -40,37 +73,7 @@ def convert_if_detected(self, value: Any) -> Any:
4073 """
4174 if not isinstance (value , str ):
4275 return value
43-
44- if not any (c in value for c in ["{" , "[" , "-" , ":" , "T" ]):
45- return value
46-
47- detected_type = self .detect_type (value )
48- if detected_type :
49- try :
50- if self .dialect in {"postgres" , "postgresql" }:
51- if detected_type in {"uuid" , "interval" }:
52- return self .convert_value (value , detected_type )
53-
54- elif self .dialect == "duckdb" :
55- if detected_type == "uuid" :
56- return self .convert_value (value , detected_type )
57-
58- elif self .dialect == "sqlite" :
59- if detected_type == "uuid" :
60- return str (value )
61-
62- elif self .dialect == "bigquery" :
63- if detected_type == "uuid" :
64- return self .convert_value (value , detected_type )
65-
66- elif self .dialect in {"mysql" , "snowflake" } and detected_type in {"uuid" , "json" }:
67- return self .convert_value (value , detected_type )
68-
69- return self .convert_value (value , detected_type )
70- except Exception :
71- return value
72-
73- return value
76+ return self ._convert_cache (value )
7477
7578 def convert_dict (self , value : dict [str , Any ]) -> Any :
7679 """Convert dictionary values with dialect-specific handling.
@@ -81,13 +84,8 @@ def convert_dict(self, value: dict[str, Any]) -> Any:
8184 Returns:
8285 Converted value appropriate for the dialect.
8386 """
84-
85- # For dialects that cannot handle raw dicts (like ADBC PostgreSQL),
86- # convert to JSON strings
8787 if self .dialect in {"postgres" , "postgresql" , "bigquery" }:
8888 return to_json (value )
89-
90- # For other dialects, pass through unchanged
9189 return value
9290
9391 def supports_native_type (self , type_name : str ) -> bool :
@@ -104,11 +102,10 @@ def supports_native_type(self, type_name: str) -> bool:
104102 "postgresql" : ["uuid" , "json" , "interval" , "pg_array" ],
105103 "duckdb" : ["uuid" , "json" ],
106104 "bigquery" : ["json" ],
107- "sqlite" : [], # Limited native type support
105+ "sqlite" : [],
108106 "mysql" : ["json" ],
109107 "snowflake" : ["json" ],
110108 }
111-
112109 return type_name in native_support .get (self .dialect , [])
113110
114111 def get_dialect_specific_converter (self , value : Any , target_type : str ) -> Any :
@@ -124,36 +121,33 @@ def get_dialect_specific_converter(self, value: Any, target_type: str) -> Any:
124121 if self .dialect in {"postgres" , "postgresql" }:
125122 if target_type in {"uuid" , "json" , "interval" }:
126123 return self .convert_value (value , target_type )
127-
128124 elif self .dialect == "duckdb" :
129125 if target_type in {"uuid" , "json" }:
130126 return self .convert_value (value , target_type )
131-
132127 elif self .dialect == "sqlite" :
133128 if target_type == "uuid" :
134129 return str (value )
135130 if target_type == "json" :
136131 return self .convert_value (value , target_type )
137-
138132 elif self .dialect == "bigquery" :
139133 if target_type == "uuid" :
140134 return str (self .convert_value (value , target_type ))
141135 if target_type == "json" :
142136 return self .convert_value (value , target_type )
143-
144137 return self .convert_value (value , target_type ) if hasattr (self , "convert_value" ) else value
145138
146139
147- def get_adbc_type_converter (dialect : str ) -> ADBCTypeConverter :
140+ def get_adbc_type_converter (dialect : str , cache_size : int = 5000 ) -> ADBCTypeConverter :
148141 """Factory function to create dialect-specific ADBC type converter.
149142
150143 Args:
151144 dialect: Database dialect name.
145+ cache_size: Maximum number of string values to cache (default: 5000)
152146
153147 Returns:
154148 Configured ADBCTypeConverter instance.
155149 """
156- return ADBCTypeConverter (dialect )
150+ return ADBCTypeConverter (dialect , cache_size )
157151
158152
159- __all__ = ("ADBCTypeConverter" , "get_adbc_type_converter" )
153+ __all__ = ("ADBC_SPECIAL_CHARS" , " ADBCTypeConverter" , "get_adbc_type_converter" )
0 commit comments