import sys
from dataclasses import dataclass
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union

import pandas as pd
import pyarrow as pa

import datasets
import datasets.config
from datasets.features.features import require_storage_cast
from datasets.table import table_cast


if TYPE_CHECKING:
     import sqlitea

     import sqlalchemy


__snake_case         =     datasets.utils.logging.get_logger(__name__)







@dataclass
class       __snake_case (  datasets.BuilderConfig ):
 __lowerCamelCase     : Union[str, "sqlalchemy.sql.Selectable"] 	=   None
 __lowerCamelCase     : Union[str, "sqlalchemy.engine.Connection", "sqlalchemy.engine.Engine", "sqlite3.Connection"] 	=   None
 __lowerCamelCase     : Optional[Union[str, List[str]]] 	=   None
 __lowerCamelCase     : bool 	=   True
 __lowerCamelCase     : Optional[Union[List, Tuple, Dict]] 	=   None
 __lowerCamelCase     : Optional[Union[List, Dict]] 	=   None
 __lowerCamelCase     : Optional[List[str]] 	=   None
 __lowerCamelCase     : Optional[int] 	=   10000
 __lowerCamelCase     : Optional[datasets.Features] 	=   None
 def 						UpperCAmelCase__							(	self    )						->				Tuple:
       '''simple docstring'''





       if self.sql is None:
             raise ValueError('''sql must be specified'''    )
       if self.con is None:
             raise ValueError('''con must be specified'''    )
 def 						UpperCAmelCase__							(	self		, snake_case__		, snake_case__ = None		, )						->				str:
       '''simple docstring'''





       UpperCAmelCase     :			List[str]     	=config_kwargs.copy()
       # We need to stringify the Selectable object to make its hash deterministic

       # The process of stringifying is explained here: http://docs.sqlalchemy.org/en/latest/faq/sqlexpressions.html
       UpperCAmelCase     :			Optional[int]     	=config_kwargs['''sql''']
       if not isinstance(snake_case__		, snake_case__    ):
             if datasets.config.SQLALCHEMY_AVAILABLE and "sqlalchemy" in sys.modules:
                   import sqlalchemy

                   if isinstance(snake_case__		, sqlalchemy.sql.Selectable    ):
                         UpperCAmelCase     :			Optional[Any]     	=sqlalchemy.create_engine(config_kwargs['''con'''].split('''://'''    )[0] + '''://'''    )
                         UpperCAmelCase     :			Optional[Any]     	=str(sql.compile(dialect=engine.dialect    )    )
                         UpperCAmelCase     :			Optional[int]     	=sql_str
                   else:
                         raise TypeError(
                             f'''Supported types for \'sql\' are string and sqlalchemy.sql.Selectable but got {type(snake_case__    )}: {sql}'''    )
             else:
                   raise TypeError(
                       f'''Supported types for \'sql\' are string and sqlalchemy.sql.Selectable but got {type(snake_case__    )}: {sql}'''    )
       UpperCAmelCase     :			Tuple     	=config_kwargs['''con''']
       if not isinstance(snake_case__		, snake_case__    ):
             UpperCAmelCase     :			int     	=id(snake_case__    )
             logger.info(
                 f'''SQL connection \'con\' of type {type(snake_case__    )} couldn\'t be hashed properly. To enable hashing, specify \'con\' as URI string instead.'''    )

       return super().create_config_id(snake_case__		, custom_features=snake_case__    )






 @property
 def 						UpperCAmelCase__							(	self    )						->				Any:
       '''simple docstring'''





       UpperCAmelCase     :			Union[str, Any]     	={
           '''index_col''': self.index_col,
           '''columns''': self.columns,
           '''params''': self.params,
           '''coerce_float''': self.coerce_float,
           '''parse_dates''': self.parse_dates,
       }
       return pd_read_sql_kwargs







class       __snake_case (  datasets.ArrowBasedBuilder ):
 __lowerCamelCase     : str 	=   SqlConfig
 def 						UpperCAmelCase__							(	self    )						->				List[str]:
       '''simple docstring'''





       return datasets.DatasetInfo(features=self.config.features    )
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				int:
       '''simple docstring'''





       return [datasets.SplitGenerator(name=datasets.Split.TRAIN		, gen_kwargs={}    )]
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				pa.Table:
       '''simple docstring'''





       if self.config.features is not None:
             UpperCAmelCase     :			int     	=self.config.features.arrow_schema
             if all(not require_storage_cast(snake_case__    ) for feature in self.config.features.values()    ):
                   # cheaper cast
                   UpperCAmelCase     :			List[Any]     	=pa.Table.from_arrays([pa_table[field.name] for field in schema]		, schema=snake_case__    )
             else:
                   # more expensive cast; allows str <-> int/float or str to Audio for example
                   UpperCAmelCase     :			Tuple     	=table_cast(snake_case__		, snake_case__    )
       return pa_table






 def 						UpperCAmelCase__							(	self    )						->				Any:
       '''simple docstring'''





       UpperCAmelCase     :			Any     	=self.config.chunksize
       UpperCAmelCase     :			Optional[int]     	=pd.read_sql(
           self.config.sql		, self.config.con		, chunksize=snake_case__		, **self.config.pd_read_sql_kwargs    )
       UpperCAmelCase     :			Optional[Any]     	=[sql_reader] if chunksize is None else sql_reader
       for chunk_idx, df in enumerate(snake_case__    ):
             UpperCAmelCase     :			int     	=pa.Table.from_pandas(snake_case__    )
             yield chunk_idx, self._cast_table(snake_case__    )

from hashlib import mda, shaaaa

__snake_case         =     (shaaaa, mda)







class       __snake_case :
 def __init__(	self		, snake_case__ = 8    )						->				None:
       '''simple docstring'''





       UpperCAmelCase     :			str     	=0b0
       UpperCAmelCase     :			str     	=size
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				None:
       '''simple docstring'''





       UpperCAmelCase     :			Union[str, Any]     	=self.hash_(snake_case__    )
       self.bitarray |= h
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				bool:
       '''simple docstring'''





       UpperCAmelCase     :			List[Any]     	=self.hash_(snake_case__    )
       return (h & self.bitarray) == h
 def __contains__(	self		, snake_case__    )						->				bool:
       '''simple docstring'''





       return self.exists(snake_case__    )
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				str:
       '''simple docstring'''





       UpperCAmelCase     :			Dict     	=bin(snake_case__    )[2:]
       return res.zfill(self.size    )
 @property
 def 						UpperCAmelCase__							(	self    )						->				str:
       '''simple docstring'''





       return self.format_bin(self.bitarray    )
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				int:
       '''simple docstring'''





       UpperCAmelCase     :			Union[str, Any]     	=0b0
       for func in HASH_FUNCTIONS:
             UpperCAmelCase     :			Optional[Any]     	=(
                 int.from_bytes(func(value.encode()    ).digest()		, '''little'''    ) % self.size
             )
             res |= 2**position
       return res
 def 						UpperCAmelCase__							(	self		, snake_case__    )						->				str:
       '''simple docstring'''





       return self.format_bin(self.hash_(snake_case__    )    )






 @property
 def 						UpperCAmelCase__							(	self    )						->				float:
       '''simple docstring'''





       UpperCAmelCase     :			int     	=bin(self.bitarray    ).count('''1'''    )
       return (n_ones / self.size) ** len(snake_case__    )
