o
    KDiY                     @   s   d dl mZ d dlZd dlmZ d dlmZ d dlmZ d dl	Z	ddl
mZmZ ddlmZmZmZmZmZ dd	lmZ dd
lmZ ddlmZmZ edZG dd dZG dd dZG dd deZG dd dedZG dd deZ G dd deZ!G dd deZ"ee# eeefZ$dS )    )unicode_literalsN)OrderedDict)chain)	getLogger   )FieldStringField)	parse_tsvNO_VALUEget_subclass_names
arg_to_sqlunescapeQuerySet)F)MergeDistributedclickhouse_ormc                   @   s(   e Zd ZdZdZdZdd Zdd ZdS )
Constraintz%
    Defines a model constraint.
    Nc                 C   s
   || _ dS )ze
        Initializer. Expects an expression that ClickHouse will verify when inserting data.
        N)expr)selfr    r   S/var/www/Datamplify/venv/lib/python3.10/site-packages/infi/clickhouse_orm/models.py__init__   s   
zConstraint.__init__c                 C   s   d| j t| jf S )z_
        Returns the SQL statement for defining this constraint during table creation.
        zCONSTRAINT `%s` CHECK %s)namer   r   r   r   r   r   create_table_sql!   s   zConstraint.create_table_sql)__name__
__module____qualname____doc__r   parentr   r   r   r   r   r   r      s    r   c                   @   sf   e Zd ZdZdZdZdd Zdd Zedd Z	ed	d
 Z
edd Zedd ZedddZdS )Indexz(
    Defines a data-skipping index.
    Nc                 C   s   || _ || _|| _dS )a  
        Initializer.

        - `expr` - a column, expression, or tuple of columns and expressions to index.
        - `type` - the index type. Use one of the following methods to specify the type:
          `Index.minmax`, `Index.set`, `Index.ngrambf_v1`, `Index.tokenbf_v1` or `Index.bloom_filter`.
        - `granularity` - index block size (number of multiples of the `index_granularity` defined by the engine).
        N)r   typegranularity)r   r   r#   r$   r   r   r   r   0   s   	
zIndex.__init__c                 C   s   d| j t| j| j| jf S )zZ
        Returns the SQL statement for defining this index during table creation.
        z$INDEX `%s` %s TYPE %s GRANULARITY %d)r   r   r   r#   r$   r   r   r   r   r   =   s   zIndex.create_table_sqlc                   C   s   dS )z
        An index that stores extremes of the specified expression (if the expression is tuple, then it stores
        extremes for each element of tuple). The stored info is used for skipping blocks of data like the primary key.
        minmaxr   r   r   r   r   r%   C   s   zIndex.minmaxc                 C      d|  S )z
        An index that stores unique values of the specified expression (no more than max_rows rows,
        or unlimited if max_rows=0). Uses the values to check if the WHERE expression is not satisfiable
        on a block of data.
        zset(%d)r   )max_rowsr   r   r   setK   s   z	Index.setc                 C   s   d| |||f S )u7  
        An index that stores a Bloom filter containing all ngrams from a block of data.
        Works only with strings. Can be used for optimization of equals, like and in expressions.

        - `n` — ngram size
        - `size_of_bloom_filter_in_bytes` — Bloom filter size in bytes (you can use large values here,
           for example 256 or 512, because it can be compressed well).
        - `number_of_hash_functions` — The number of hash functions used in the Bloom filter.
        - `random_seed` — The seed for Bloom filter hash functions.
        zngrambf_v1(%d, %d, %d, %d)r   )nsize_of_bloom_filter_in_bytesnumber_of_hash_functionsrandom_seedr   r   r   
ngrambf_v1T   s   zIndex.ngrambf_v1c                 C   s   d| ||f S )u  
        An index that stores a Bloom filter containing string tokens. Tokens are sequences
        separated by non-alphanumeric characters.

        - `size_of_bloom_filter_in_bytes` — Bloom filter size in bytes (you can use large values here,
           for example 256 or 512, because it can be compressed well).
        - `number_of_hash_functions` — The number of hash functions used in the Bloom filter.
        - `random_seed` — The seed for Bloom filter hash functions.
        ztokenbf_v1(%d, %d, %d)r   )r*   r+   r,   r   r   r   
tokenbf_v1b   s   zIndex.tokenbf_v1皙?c                 C   r&   )z
        An index that stores a Bloom filter containing values of the index expression.

        - `false_positive` - the probability (between 0 and 1) of receiving a false positive
          response from the filter
        zbloom_filter(%f)r   )false_positiver   r   r   bloom_filtero   s   zIndex.bloom_filter)r/   )r   r   r   r    r   r!   r   r   staticmethodr%   r(   r-   r.   r1   r   r   r   r   r"   (   s     



r"   c                       s>   e Zd ZdZi Z fddZed	ddZedd Z  Z	S )
	ModelBasezP
    A metaclass for ORM models. It adds the _fields list to model classes.
    c              	      sz  i }i }i }|D ]}t |tr!||j ||j ||j q| D ]!\}}	t |	tr4|	||< q&t |	tr>|	||< q&t |	t	rG|	||< q&t
| dd d}i }
d}|D ]&\}}|jsb|jrgt|
|< qXt |jtrtt|
|< d}qX||jtj|
|< qXt|t|||tdd |D |
|d}tt| | t|||}t|| | D ]\}}	t|	d	| t|	d
| q|S )Nc                 S   s
   | d j S )Nr   )creation_counter)itemr   r   r   <lambda>   s   
 z#ModelBase.__new__.<locals>.<lambda>)keyFTc                 S   s   g | ]	}|d  j s|qS )r   readonly.0fr   r   r   
<listcomp>   s    z%ModelBase.__new__.<locals>.<listcomp>)_fields_constraints_indexes_writable_fields	_defaults_has_funcs_as_defaultsr!   r   )
isinstancer3   updater>   r?   r@   itemsr   r   r"   sortedaliasmaterializedr
   defaultr   	to_pythonpytzUTCdictr   super__new__strr   setattr)clsr   basesattrsfieldsconstraintsindexesbaser)   objdefaultshas_funcs_as_defaultsr<   model	__class__r   r   rP      sR   






	zModelBase.__new__
AdHocModelc                 C   sl   t |}|d t| }|| jv r| j| S i }|D ]\}}| |||< q| | |tf|}|| j|< |S )N )listrQ   ad_hoc_model_cachecreate_ad_hoc_fieldrP   Model)rS   rV   
model_name	cache_keyrU   r   db_typemodel_classr   r   r   create_ad_hoc_model   s   


zModelBase.create_ad_hoc_modelc                 C   s   dd l m  m} |dr|j|S |dr/|dd }|j|r+|dd dS d dS |drYd	d
 |dd dD ^}}|jt	||rU|d dd dS d dS |drl| |dd }|
|S |drdd
 |dd dD }tt|dksJ d| | |d }|
|S |drt	|dd }||S |dr|d}dd
 ||d d dD }	t||d | d }
|
|	 S |dr| |dd }||S |dr| |dd }||S |d }t||s
td| t|| S )Nr   Enumz	DateTime(	   r   )timezonezDateTime64(c                 S      g | ]}|  qS r   stripr;   sr   r   r   r=          z1ModelBase.create_ad_hoc_field.<locals>.<listcomp>   ,)	precisionrn   Array   Tuplec                 S   ro   r   rp   rr   r   r   r   r=      rt   z'No support for mixed types in tuples - FixedString   Decimal(c                 S   s   g | ]}t | qS r   )intrq   )r;   r)   r   r   r   r=          r   NullableLowCardinality   zNo field class for %s)infi.clickhouse_orm.fieldsr   rV   
startswithBaseEnumFieldrd   DateTimeFieldsplitDateTime64Fieldr   
ArrayFieldlenr(   FixedStringFieldindexgetattrNullableFieldLowCardinalityFieldhasattrNotImplementedError)rS   rh   
orm_fieldsrn   rw   inner_fieldtypeslengthpargsfield_classr   r   r   r   rd      sX   


 







 



zModelBase.create_ad_hoc_field)r`   )
r   r   r   r    rc   rP   classmethodrj   rd   __classcell__r   r   r^   r   r3   z   s    7r3   c                       s   e Zd ZdZdZdZdZdZ fddZ fddZ	dd	 Z
d
d Zdd Zedd Zedd Zedd Zedd ZeejdfddZd)ddZd)ddZdd Zd*dd Zed!d" Zed+d#d$Zed%d& Zed'd( Z  ZS ),re   a  
    A base class for ORM models. Each model class represent a ClickHouse table. For example:

        class CPUStats(Model):
            timestamp = DateTimeField()
            cpu_id = UInt16Field()
            cpu_percent = Float32Field()
            engine = Memory()
    NFc                    s`   t t|   | j| j | D ]\}}| |}|r$t| || qt	d| j
j|f dS )a  
        Creates a model instance, using keyword arguments as field values.
        Since values are immediately converted to their Pythonic type,
        invalid values will cause a `ValueError` to be raised.
        Unrecognized field names will cause an `AttributeError`.
        z"%s does not have a field called %sN)rO   re   r   __dict__rE   rB   rF   	get_fieldrR   AttributeErrorr_   r   )r   kwargsr   valuefieldr^   r   r   r     s   
zModel.__init__c                    s   |  |}|r6|tkr6z||tj}|| W n ty5   t \}}}d	||}|
|||w tt| || dS )z
        When setting a field value, converts the value to its Pythonic type and validates it.
        This may raise a `ValueError`.
        z{} (field '{}')N)r   r
   rK   rL   utcvalidate
ValueErrorsysexc_infoformatwith_tracebackrO   re   __setattr__)r   r   r   r   tpvtbnew_msgr^   r   r   r   &  s   
zModel.__setattr__c                 C   s(   ddl m} t||sJ d|| _dS )
        Sets the `Database` that this model instance belongs to.
        This is done automatically when the instance is read from the database or written to it.
        r   )Databasez+database must be database.Database instanceN)databaser   rD   	_database)r   dbr   r   r   r   set_database6  s   
zModel.set_databasec                 C      | j S )z
        Gets the `Database` that this model instance belongs to.
        Returns `None` unless the instance was read from the database or written to it.
        )r   r   r   r   r   get_database@     zModel.get_databasec                 C   s   | j |S )zQ
        Gets a `Field` instance given its name, or `None` if not found.
        )r>   get)r   r   r   r   r   r   G  s   zModel.get_fieldc                 C   s
   | j  S )z
        Returns the model's database table name. By default this is the
        class name converted to lowercase. Override this if you want to use
        a different table name.
        )r   lowerrS   r   r   r   
table_nameM  s   
zModel.table_namec                 C   r   )z
        Return True if some of the model's fields use a function expression
        as a default value. This requires special handling when inserting instances.
        )rC   r   r   r   r   r\   V  s   zModel.has_funcs_as_defaultsc                 C   s   d|j |  f g}g }|   D ]\}}|d||j|df  q| j D ]}|d|   q)| j	 D ]}|d|   q:|d
| |d |d| j|  d
|S )	P
        Returns the SQL statement for creating a table for this model.
        &CREATE TABLE IF NOT EXISTS `%s`.`%s` (	    %s %sr   z    %s,
)	ENGINE = 
)db_namer   rV   rF   appendget_sqlr?   valuesr   r@   joinengine)rS   r   partsrF   r   r   cir   r   r   r   ^  s   

zModel.create_table_sqlc                 C   s   d|j |  f S )zJ
        Returns the SQL command for deleting this model's table.
        zDROP TABLE IF EXISTS `%s`.`%s`)r   r   )rS   r   r   r   r   drop_table_sqlt  s   zModel.drop_table_sqlc                 C   sl   t t|}i }|D ]}t| |}t|ddp|}	|t||	||< q
| di |}
|dur4|
| |
S )a  
        Create a model instance from a tab-separated line. The line may or may not include a newline.
        The `field_names` list must match the fields defined in the model, but does not have to include all of them.

        - `line`: the TSV-formatted data.
        - `field_names`: names of the model fields in the data.
        - `timezone_in_use`: the timezone to use when parsing dates and datetimes. Some fields use their own timezones.
        - `database`: if given, sets the database that this instance belongs to.
        rn   Nr   )iterr	   r   rK   nextr   )rS   linefield_namestimezone_in_user   r   r   r   r   field_timezonerZ   r   r   r   from_tsv{  s   

zModel.from_tsvTc                    s0   | j  | j| d}d fdd| D S )z
        Returns the instance's column values as a tab-separated line. A newline is not included.

        - `include_readonly`: if false, returns only fields that can be inserted into database.
        writable	c                 3   s&    | ]\}}|j  | d dV  qdS )FquoteN)to_db_string)r;   r   r   datar   r   	<genexpr>  s   $ zModel.to_tsv.<locals>.<genexpr>)r   rV   r   rF   )r   include_readonlyrV   r   r   r   to_tsv  s   zModel.to_tsvc                 C   s`   | j }| j| d}g }| D ]\}}|| tkr*||d |j|| dd  qd|S )a  
        Returns the instance's column keys and values as a tab-separated line. A newline is not included.
        Fields that were not assigned a value are omitted.

        - `include_readonly`: if false, returns only fields that can be inserted into database.
        r   =Fr   r   )r   rV   rF   r
   r   r   r   )r   r   r   rV   r   r   r   r   r   r   to_tskv  s    
zModel.to_tskvc                 C   s,   | j r| dn| d}|d7 }|dS )z^
        Returns the instance as a bytestring ready to be inserted into the database.
        Fr   zutf-8)rC   r   r   encode)r   rs   r   r   r   r     s   
zModel.to_db_stringc                    s@   | j | d}durfdd|D }| j  fdd|D S )z
        Returns the instance's column values as a dict.

        - `include_readonly`: if false, returns only fields that can be inserted into database.
        - `field_names`: an iterable of field names to return (optional)
        r   Nc                    s   g | ]}| v r|qS r   r   r:   )r   r   r   r=     r   z!Model.to_dict.<locals>.<listcomp>c                    s   i | ]}| | qS r   r   )r;   r   r   r   r   
<dictcomp>  s    z!Model.to_dict.<locals>.<dictcomp>)rV   r   )r   r   r   rV   r   )r   r   r   to_dict  s
   zModel.to_dictc                 C   s
   t | |S )zS
        Returns a `QuerySet` for selecting instances of this model class.
        r   )rS   r   r   r   r   
objects_in  s   
zModel.objects_inc                 C   s   |r| j S | jS )z
        Returns an `OrderedDict` of the model's fields (from name to `Field` instance).
        If `writable` is true, only writable fields are included.
        Callers should not modify the dictionary.
        )rA   r>   )rS   r   r   r   r   rV     s   zModel.fieldsc                 C   r   )zC
        Returns true if the model is marked as read only.
        )	_readonlyr   r   r   r   is_read_only  r   zModel.is_read_onlyc                 C   r   )zF
        Returns true if the model represents a system table.
        )_systemr   r   r   r   is_system_model  r   zModel.is_system_model)T)TN)F)r   r   r   r    r   r   r   r   r   r   r   r   r   r   r   r\   r   r   rL   r   r   r   r   r   r   r   rV   r   r   r   r   r   r^   r   re      sB    










	
re   )	metaclassc                   @   s   e Zd Zedd ZdS )BufferModelc                 C   sB   d|j |  |j | jj f g}| j|}|| d|S )r   z1CREATE TABLE IF NOT EXISTS `%s`.`%s` AS `%s`.`%s`ra   )r   r   r   
main_modelr   r   r   )rS   r   r   
engine_strr   r   r   r     s   


zBufferModel.create_table_sqlN)r   r   r   r   r   r   r   r   r   r     s    r   c                   @   s*   e Zd ZdZdZeddZedd ZdS )
MergeModelz
    Model for Merge engine
    Predefines virtual _table column an controls that rows can't be inserted to this table type
    https://clickhouse.tech/docs/en/single/index.html#document-table_engines/merge
    Tr8   c                 C   s   t | jts
J dd|j|  f g}g }|   D ]\}}|dkr1|d||j|df  q|d	| |d |d| j
|  d		|S )
r   z+engine must be an instance of engines.Merger   _tabler   r   r   r   r   r   )rD   r   r   r   r   rV   rF   r   r   r   r   )rS   r   r   colsr   r   r   r   r   r     s   

zMergeModel.create_table_sqlN)	r   r   r   r    r9   r   r   r   r   r   r   r   r   r     s    
r   c                       s8   e Zd ZdZ fddZedd Zedd Z  ZS )DistributedModelz:
    Model class for use with a `Distributed` engine.
    c                    s(   t | jts
J dtt| |}|S )r   z1engine must be an instance of engines.Distributed)rD   r   r   rO   r   r   )r   r   resr^   r   r   r     s   zDistributedModel.set_databasec                 C   sL   | j jrdS dd | jD }|stdt|dkrtd|d | j _dS )a  
        Remember: Distributed table does not store any data, just provides distributed access to it.

        So if we define a model with engine that has no defined table for data storage
        (see FooDistributed below), that table cannot be successfully created.
        This routine can automatically fix engine's storage table by finding the first
        non-distributed model among your model's superclasses.

        >>> class Foo(Model):
        ...     id = UInt8Field(1)
        ...
        >>> class FooDistributed(Foo, DistributedModel):
        ...     engine = Distributed('my_cluster')
        ...
        >>> FooDistributed.engine.table
        None
        >>> FooDistributed.fix_engine()
        >>> FooDistributed.engine.table
        <class '__main__.Foo'>

        However if you prefer more explicit way of doing things,
        you can always mention the Foo model twice without bothering with any fixes:

        >>> class FooDistributedVerbose(Foo, DistributedModel):
        ...     engine = Distributed('my_cluster', Foo)
        >>> FooDistributedVerbose.engine.table
        <class '__main__.Foo'>

        See tests.test_engines:DistributedTestCase for more examples
        Nc                 S   s$   g | ]}t |trt |ts|qS r   )
issubclassre   r   )r;   br   r   r   r=   C  s    
z5DistributedModel.fix_engine_table.<locals>.<listcomp>zaWhen defining Distributed engine without the table_name ensure that your model has a parent modelr   zyWhen defining Distributed engine without the table_name ensure that your model has exactly one non-distributed superclassr   )r   r   	__bases__	TypeErrorr   table)rS   storage_modelsr   r   r   fix_engine_table  s   "z!DistributedModel.fix_engine_tablec                 C   sN   t | jts
J d|   d|j|  | jjd| j| g}d|S )r   z+engine must be engines.Distributed instancez5CREATE TABLE IF NOT EXISTS `{0}`.`{1}` AS `{0}`.`{2}`r   r   )	rD   r   r   r   r   r   r   r   r   )rS   r   r   r   r   r   r   P  s   
z!DistributedModel.create_table_sql)	r   r   r   r    r   r   r   r   r   r   r   r^   r   r     s    	
2r   )%
__future__r   r   collectionsr   	itertoolsr   loggingr   rL   rV   r   r   utilsr	   r
   r   r   r   queryr   funcsr   enginesr   r   loggerr   r"   r#   r3   re   r   r   r   locals__all__r   r   r   r   <module>   s,    R  eR