o
    KDiUE                     @   s   d dl mZ d dlZd dlZd dlmZ ddlmZ ddlm	Z	m
Z
mZ d dlmZ d dlZd dlmZ d dlZd dlZed	Zed
dZG dd deZG dd deZG dd deZdd eeeefD ZdS )    )unicode_literalsN)
namedtuple   )	ModelBase)escape	parse_tsvimport_submodules)ceil)Templateclickhouse_ormPagez6objects number_of_objects pages_total number page_sizec                   @   s   e Zd ZdZdS )DatabaseExceptionz1
    Raised when a database operation fails.
    N)__name__
__module____qualname____doc__ r   r   U/var/www/Datamplify/venv/lib/python3.10/site-packages/infi/clickhouse_orm/database.pyr      s    r   c                       sn   e Zd ZdZ fddZedejejB edejejB edejejB fZ	e
dd Zd	d
 Z  ZS )ServerErrorz0
    Raised when a server returns an error.
    c                    s>   d | _ | |}|r|\| _ | _d S || _tt| | d S N)codeget_error_code_msgmessagesuperr   __init__)selfr   	processed	__class__r   r   r      s   
zServerError.__init__z
            Code:\ (?P<code>\d+),
            \ e\.displayText\(\)\ =\ (?P<type1>[^ \n]+):\ (?P<msg>.+?),
            \ e.what\(\)\ =\ (?P<type2>[^ \n]+)
        zq
            Code:\ (?P<code>\d+),
            \ e\.displayText\(\)\ =\ (?P<type1>[^ \n]+):\ (?P<msg>.+)
        zZ
            Code:\ (?P<code>\d+).
            \ (?P<type1>[^ \n]+):\ (?P<msg>.+)
        c                 C   sB   | j D ]}||}|rt|d|d f  S qd|fS )z
        Extract the code and message of the exception that clickhouse-server generated.

        See the list of error codes here:
        https://github.com/yandex/ClickHouse/blob/master/dbms/src/Common/ErrorCodes.cpp
        r   msgr   )ERROR_PATTERNSmatchintgroupstrip)clsfull_error_messagepatternr!   r   r   r   r   =   s   

 zServerError.get_error_code_msgc                 C   s   | j d urd| j| j S d S )Nz{} ({}))r   formatr   r   r   r   r   __str__M   s   
zServerError.__str__)r   r   r   r   r   recompileVERBOSEDOTALLr    classmethodr   r*   __classcell__r   r   r   r   r      s     



r   c                   @   s   e Zd ZdZ			d7ddZd	d
 Zdd Zdd Zdd Zdd Z	d8ddZ
dd Zd9ddZd:ddZd;ddZd<ddZd=d"d#Zd>d%d&Zd'd( Zd<d)d*Zd+d, Zd:d-d.Zd/d0 Zd?d1d2Zd3d4 Zd5d6 ZdS )@Databasez
    Database instances connect to a specific ClickHouse database for running queries,
    inserting data and other operations.
    http://localhost:8123/NFT<   c
           
      C   s   || _ || _d| _|| _t | _|| j_|r||pdf| j_|	| _	i | _
d| _|  | _|r?| js6td|  | _d| _n	|rH| jsH|   |  | _| jdkrV|  ntj| _| jdk| _| jdk| _dS )	a   
        Initializes a database instance. Unless it's readonly, the database will be
        created on the ClickHouse server if it does not already exist.

        - `db_name`: name of the database to connect to.
        - `db_url`: URL of the ClickHouse server.
        - `username`: optional connection credentials.
        - `password`: optional connection credentials.
        - `readonly`: use a read-only connection.
        - `autocreate`: automatically create the database if it does not exist (unless in readonly mode).
        - `timeout`: the connection timeout in seconds.
        - `verify_ssl_cert`: whether to verify the server's certificate when connecting via HTTPS.
        - `log_statements`: when True, all database statements are logged.
        F zHDatabase does not exist, and cannot be created under readonly connectionT)r   r   i  )   r      )r5   r   N)db_namedb_urlreadonlytimeoutrequestsSessionrequest_sessionverifyauthlog_statementssettings	db_exists_is_existing_databaser   _is_connection_readonlyconnection_readonlycreate_database_get_server_versionserver_version_get_server_timezonepytzutcserver_timezonehas_codec_supporthas_low_cardinality_support)
r   r7   r8   usernamepasswordr9   
autocreater:   verify_ssl_certr@   r   r   r   r   X   s.   




zDatabase.__init__c                 C      |  d| j  d| _dS )z]
        Creates the database on the ClickHouse server if it does not already exist.
        z"CREATE DATABASE IF NOT EXISTS `%s`TN_sendr7   rB   r)   r   r   r   rF         
zDatabase.create_databasec                 C   rS   )z@
        Deletes the database on the ClickHouse server.
        zDROP DATABASE `%s`FNrT   r)   r   r   r   drop_database   rV   zDatabase.drop_databasec                 C   s@   |  rtdt|ddu rtd|j | ||  dS )zZ
        Creates a table for the given model class, if it does not exist already.
        zYou can't create system tableengineNz%s class must define an engine)is_system_modelr   getattrr   rU   create_table_sqlr   model_classr   r   r   create_table   s
   zDatabase.create_tablec                 C   s$   |  rtd| ||  dS )zR
        Drops the database table of the given model class, if it exists.
        zYou can't drop system tableN)rY   r   rU   drop_table_sqlr\   r   r   r   
drop_table   s   zDatabase.drop_tablec                 C   s*   d}|  || j| f }|j dkS )z
        Checks whether a table for the given model class already exists.
        Note that this only checks for existence of a table with the expected name.
        zGSELECT count() FROM system.tables WHERE database = '%s' AND name = '%s'1)rU   r7   
table_nametextr$   )r   r]   sqlrr   r   r   does_table_exist   s   zDatabase.does_table_existc                 C   sV   |rdn| j }d||f }| | }dd |D }t||}|r)d |_|_|S )aj  
        Generates a model class from an existing table in the database.
        This can be used for querying tables which don't have a corresponding model class,
        for example system tables.

        - `table_name`: the table to create a model for
        - `system_table`: whether the table is a system table, or belongs to the current database
        systemzDESCRIBE `%s`.`%s` FORMAT TSVc                 S   s   g | ]
}t |d d qS )N   )r   ).0liner   r   r   
<listcomp>   s    z0Database.get_model_for_table.<locals>.<listcomp>T)r7   rU   
iter_linesr   create_ad_hoc_model_system	_readonly)r   rb   system_tabler7   rd   linesfieldsmodelr   r   r   get_model_for_table   s   	zDatabase.get_model_for_tablec                 C   s>   t |ts	J d|du r| j|d dS t|| j|< dS )aU  
        Adds a database setting that will be sent with every request.
        For example, `db.add_setting("max_execution_time", 10)` will
        limit query execution time to 10 seconds.
        The name must be string, and the value is converted to string in case
        it isn't. To remove a setting, pass `None` as the value.
        zSetting name must be a stringN)
isinstancestrrA   pop)r   namevaluer   r   r   add_setting   s   zDatabase.add_setting  c                    s   ddl m  t|ztW n
 ty   Y dS w j s& r*tdd	dd j
dd	D } r>d
nd}d||f  fdd}|  dS )z
        Insert records into the database.

        - `model_instances`: any iterable containing instances of a single model class.
        - `batch_size`: number of records to send per chunk (use a lower number if your records are very large).
        r   )BytesIONz1You can't insert into read only and system tables,c                 S   s   g | ]}d | qS )`%s`r   )ri   rx   r   r   r   rk      s    z#Database.insert.<locals>.<listcomp>T)writableTSKVTabSeparatedz"INSERT INTO $table (%s) FORMAT %s
c                  3   s      } |  d  |    d}D ] }| |  |  |d7 }|kr@|  V    } d}q |rJ|  V  d S d S )Nutf-8rh   r   r   )write_substituteencodeset_databaseto_db_stringgetvalue)bufrq   instancer|   
batch_sizefirst_instanceir]   queryr   r   r   gen   s$   


zDatabase.insert.<locals>.gen)ior|   iternextStopIterationr   is_read_onlyrY   r   joinrr   has_funcs_as_defaultsrU   )r   model_instancesr   fields_listfmtr   r   r   r   insert   s"   zDatabase.insertc                 C   sb   ddl m} d}|rt||r||}|dt| 7 }| ||}| |}|jr/t|jS dS )z
        Counts the number of records in the model's table.

        - `model_class`: the model to count.
        - `conditions`: optional SQL conditions (contents of the WHERE clause).
        r   QzSELECT count() FROM $table WHERE )	infi.clickhouse_orm.queryr   ru   to_sqlrv   r   rU   rc   r"   )r   r]   
conditionsr   r   re   r   r   r   count   s   


zDatabase.countc           	      c   s~    |d7 }|  ||}| ||d}| }tt|}tt|}|p+tt||}|D ]}|r<|||| j	| V  q.dS )aH  
        Performs a query and returns a generator of model instances.

        - `query`: the SQL query to execute.
        - `model_class`: the model class matching the query's table,
          or `None` for getting back instances of an ad-hoc model.
        - `settings`: query settings to send as HTTP GET parameters
        z% FORMAT TabSeparatedWithNamesAndTypesTN)
r   rU   rl   r   r   r   rm   zipfrom_tsvrL   )	r   r   r]   rA   re   rq   field_namesfield_typesrj   r   r   r   select  s   	zDatabase.selectc                 C   s   |  |d}| j|||djS )a  
        Performs a query and returns its output as text.

        - `query`: the SQL query to execute.
        - `settings`: query settings to send as HTTP GET parameters
        - `stream`: if true, the HTTP response from ClickHouse will be streamed.
        N)rA   stream)r   rU   rc   )r   r   rA   r   r   r   r   raw   s   zDatabase.rawr   d   c                 C   s   ddl m} | ||}tt|t| }	|dkr t|	d}n
|dk r*td| |d | }
dd	|
  }|rPt||rH||}|dt| 7 }|d	| 7 }|d
|
|f 7 }| ||}t|rpt| |||ng ||	||dS )a  
        Selects records and returns a single page of model instances.

        - `model_class`: the model class matching the query's table,
          or `None` for getting back instances of an ad-hoc model.
        - `order_by`: columns to use for sorting the query (contents of the ORDER BY clause).
        - `page_num`: the page number (1-based), or -1 to get the last page.
        - `page_size`: number of records to return per page.
        - `conditions`: optional SQL conditions (contents of the WHERE clause).
        - `settings`: query settings to send as HTTP GET parameters

        The result is a namedtuple containing `objects` (list), `number_of_objects`,
        `pages_total`, `number` (of the current page), and `page_size`.
        r   r   r   zInvalid page number: %dzSELECT {} FROM $tablez, r   z ORDER BY %sz LIMIT %d, %d)objectsnumber_of_objectspages_totalnumber	page_size)r   r   r   r"   r	   floatmax
ValueErrorr(   r   rr   keysru   r   rv   r   r   listr   )r   r]   order_bypage_numr   r   rA   r   r   r   offsetr   r   r   r   paginate+  s.   

zDatabase.paginate'  c           
      C   s   ddl m} td}| |}t|}t| | }t|D ]0}|	d| || j
D ]}	|	|  q-| |||tj dg t|dd |krP dS q dS )z
        Executes schema migrations.

        - `migrations_package_name` - fully qualified name of the Python package
          containing the migrations.
        - `up_to` - number of the last migration to apply.
        r   MigrationHistory
migrationszApplying migration %s...)package_namemodule_nameappliedN   )r   r   logging	getLogger_get_applied_migrationsr   setr   sortedinfo
operationsapplyr   datetimedatetodayr"   )
r   migrations_package_nameup_tor   loggerapplied_migrationsmodulesunapplied_migrationsrx   	operationr   r   r   migrateS  s   

zDatabase.migratec                 C   sB   ddl m} | | d| }| ||}tdd | |D S )Nr   r   z8SELECT module_name from $table WHERE package_name = '%s'c                 s   s    | ]}|j V  qd S r   )r   )ri   objr   r   r   	<genexpr>m  s    z3Database._get_applied_migrations.<locals>.<genexpr>)r   r   r^   r   r   r   )r   r   r   r   r   r   r   r   h  s
   
z Database._get_applied_migrationsc                 C   s`   t |tr|d}| jrt| | |}| jj| j	|||| j
d}|jdkr.t|j|S )Nr   )paramsdatar   r:      )ru   rv   r   r@   r   r   _build_paramsr=   postr8   r:   status_coder   rc   )r   r   rA   r   r   re   r   r   r   rU   o  s   





zDatabase._sendc                 C   s@   t |pi }|| j | jr| j|d< | jr| jsd|d< |S )Ndatabasera   r9   )dictupdaterA   rB   r7   r9   rE   )r   rA   r   r   r   r   r   z  s   
zDatabase._build_paramsc                 C   s^   d|v r-t d| j d}|r&| rd|  |d< nd| j| f |d< t||}|S )zD
        Replaces $db and $table placeholders in the query.
        $r~   )dbz`system`.`%s`tablez	`%s`.`%s`)r   r7   rY   rb   r
   safe_substitute)r   r   r]   mappingr   r   r   r     s   zDatabase._substitutec              
   C   sT   z|  d}t|j W S  ty) } ztd| tjW  Y d }~S d }~ww )NzSELECT timezone()z3Cannot determine server timezone (%s), assuming UTC)	rU   rJ   timezonerc   r$   r   r   	exceptionrK   )r   re   er   r   r   rI     s   
zDatabase._get_server_timezonec              
   C   sj   z
|  d}|j}W n ty$ } ztd| d}W Y d }~nd }~ww |r3tdd |dD S |S )NzSELECT version();z4Cannot determine server version (%s), assuming 1.1.0z1.1.0c                 s   s     | ]}|  rt|V  qd S r   )isdigitr"   )ri   nr   r   r   r     s    z/Database._get_server_version.<locals>.<genexpr>.)rU   rc   r   r   r   tuplesplit)r   as_tuplere   verr   r   r   r   rG     s   

 zDatabase._get_server_versionc                 C   s   |  d| j }|j dkS )Nz6SELECT count() FROM system.databases WHERE name = '%s'ra   )rU   r7   rc   r$   r   re   r   r   r   rC     s   zDatabase._is_existing_databasec                 C   s   |  d}|j dkS )Nz9SELECT value FROM system.settings WHERE name = 'readonly'0)rU   rc   r$   r   r   r   r   rD     s   
z Database._is_connection_readonly)r2   NNFTr3   TF)F)r{   r   )NN)NF)r   r   NN)r   )T)r   r   r   r   r   rF   rW   r^   r`   rf   rt   rz   r   r   r   r   r   r   r   rU   r   r   rI   rG   rC   rD   r   r   r   r   r1   R   s6    
,

	

-



(



	r1   c                 C   s   g | ]}|j qS r   )r   )ri   cr   r   r   rk     s    rk   )
__future__r   r+   r;   collectionsr   modelsr   utilsr   r   r   mathr	   r   stringr
   rJ   r   r   r   r   	Exceptionr   r   objectr1   __all__r   r   r   r   <module>   s&    

7  ]