1+ """Dialect capability declarations for the strict wide-table contract.
2+
3+ A declaration is deliberately not a claim that a live target has been tested.
4+ Importers use this information for preflight checks before creating a dataset.
5+ """
6+
7+ from dataclasses import dataclass
8+ from urllib .parse import urlparse
9+
10+ from ..core import UnsupportedOperationError
11+
12+
13+ @dataclass (frozen = True )
14+ class SqlProfile :
15+ name : str
16+ url_schemes : tuple [str , ...]
17+ max_physical_variables : int
18+ identifier_limit : int
19+ binary64_numeric : bool
20+ lossless_text : bool
21+ tested_reference : bool = False
22+
23+ def as_dict (self ) -> dict [str , object ]:
24+ return {
25+ "max_physical_variables" : self .max_physical_variables ,
26+ "identifier_limit" : self .identifier_limit ,
27+ "binary64_numeric" : self .binary64_numeric ,
28+ "lossless_text" : self .lossless_text ,
29+ "tested_reference" : self .tested_reference ,
30+ }
31+
32+
33+ SQLITE = SqlProfile ("sqlite" , ("sqlite" ,), 1_999 , 255 , True , True , True )
34+ POSTGRESQL = SqlProfile ("postgresql" , ("postgresql" , "postgres" ), 1_599 , 63 , True , True )
35+ MYSQL = SqlProfile ("mysql" , ("mysql" , "mariadb" ), 1_016 , 64 , True , True )
36+ PROFILES = (SQLITE , POSTGRESQL , MYSQL )
37+
38+
39+ def profile_for_url (database_url : str ) -> SqlProfile :
40+ scheme = urlparse (database_url ).scheme .split ("+" , 1 )[0 ].lower ()
41+ for profile in PROFILES :
42+ if scheme in profile .url_schemes :
43+ return profile
44+ raise UnsupportedOperationError (
45+ f"No OpenStatSpec SQL profile is declared for database URL scheme { scheme !r} ."
46+ )
47+
48+
49+ def preflight (profile : SqlProfile , variable_count : int ) -> None :
50+ if variable_count > profile .max_physical_variables :
51+ raise UnsupportedOperationError (
52+ f"Target capability exceeded: { profile .name } supports at most "
53+ f"{ profile .max_physical_variables } source variables in one strict wide table."
54+ )
0 commit comments