U
    x^#                     @   sp   d Z ddlmZ ddlmZ ddlmZ ddlmZ ddgZG dd deZ	G d	d
 d
e
ZG dd deZdS )a  Horizontal sharding support.

Defines a rudimental 'horizontal sharding' system which allows a Session to
distribute queries and persistence operations across multiple databases.

For a usage example, see the :ref:`examples_sharding` example included in
the source distribution.

   )inspect)util)Query)SessionShardedSessionShardedQueryc                       sP   e Zd Z fddZdd Zdd Zdd Zd fd
d	Zd fdd	Z  Z	S )r   c                    s0   t t| j|| | jj| _| jj| _d | _d S N)superr   __init__session
id_chooserquery_chooser	_shard_id)selfargskwargs	__class__ G/tmp/pip-install-dq5v43_d/SQLAlchemy/sqlalchemy/ext/horizontal_shard.pyr
      s    

zShardedQuery.__init__c                 C   s   |   }||_|S )zreturn a new query, limited to a single shard ID.

        all subsequent operations with the returned query will
        be against the single shard regardless of other state.
        )Z_cloner   )r   shard_idqr   r   r   	set_shard"   s    zShardedQuery.set_shardc                    sd    fdd} j d k	r"| j S jd k	r6|jS g }D ]}||| qDt|S d S )Nc                    s:   |   j d<  _j | d jj}| S )Nr   )mapperr   )
attributesidentity_token_connection_from_sessionZ_bind_mapperexecuteZ	statement_paramsZ	instances)r   resultcontextr   r   r   iter_for_shard.   s      z;ShardedQuery._execute_and_instances.<locals>.iter_for_shard)r   r   r   extenditer)r   r!   r"   partialr   r   r    r   _execute_and_instances-   s    



z#ShardedQuery._execute_and_instancesc                    sf    fdd}j d k	r$|j S d}g }D ] }||}||j7 }|| q6t||S d S )Nc                    s$   j  | dd}|j}|S )NT)r   r   clauseZclose_with_result)r   r   r   )r   connr   r   r   stmtr   r   exec_for_shardC   s    z2ShardedQuery._execute_crud.<locals>.exec_for_shard    )r   r   rowcountappendShardedResult)r   r*   r   r+   r-   resultsr   r   r   r)   r   _execute_crudB   s    



zShardedQuery._execute_crudNc           	         s   |dk	r&t t| j||fd|i|S | j|}|r@||}| ||D ]2}t t| j||fd|i|}|dk	rL|  S qLdS dS )zoverride the default Query._identity_lookup method so that we
        search for a given non-token primary key identity across all
        possible identity tokens (e.g. shard ids).

        Nr   )r	   r   _identity_lookupr   queryZ_set_lazyload_fromr   )	r   r   primary_key_identityr   Zlazy_loaded_fromkwr   r   objr   r   r   r2   Y   s0    


 
zShardedQuery._identity_lookupc                    s<    fdd}|dkr&j dk	r&j }ttj|||dS )zOverride the default Query._get_impl() method so that we emit
        a query to the DB for each possible identity token, if we don't
        have one already.

        c                    s\   j d k	r |S t|}|D ](}|} ||}|d k	r*|  S q*d S d S r   )r   r   Zto_listr   r   )r3   r4   identr   r   o
db_load_fnr   r   r   _db_load_fn   s    





z+ShardedQuery._get_impl.<locals>._db_load_fnN)r   )r   r	   r   	_get_impl)r   r4   r:   r   r;   r   r9   r   r<   {   s    
  zShardedQuery._get_impl)NN)N)
__name__
__module____qualname__r
   r   r&   r1   r2   r<   __classcell__r   r   r   r   r      s     "c                   @   s(   e Zd ZdZdZdd Zedd ZdS )r/   a  A value object that represents multiple :class:`.ResultProxy` objects.

    This is used by the :meth:`.ShardedQuery._execute_crud` hook to return
    an object that takes the place of the single :class:`.ResultProxy`.

    Attribute include ``result_proxies``, which is a sequence of the
    actual :class:`.ResultProxy` objects, as well as ``aggregate_rowcount``
    or ``rowcount``, which is the sum of all the individual rowcount values.

    .. versionadded::  1.3
    result_proxiesaggregate_rowcountc                 C   s   || _ || _d S r   rA   )r   rB   rC   r   r   r   r
      s    zShardedResult.__init__c                 C   s   | j S r   )rC   )r   r   r   r   r-      s    zShardedResult.rowcountN)r=   r>   r?   __doc__	__slots__r
   propertyr-   r   r   r   r   r/      s
   r/   c                       sF   e Zd Zdef fdd	Zdd ZdddZddd	Zd
d Z  Z	S )r   Nc                    s`   t t| jf d|i| || _|| _|| _i | _| j| _|dk	r\|D ]}| 	|||  qFdS )a  Construct a ShardedSession.

        :param shard_chooser: A callable which, passed a Mapper, a mapped
          instance, and possibly a SQL clause, returns a shard ID.  This id
          may be based off of the attributes present within the object, or on
          some round-robin scheme. If the scheme is based on a selection, it
          should set whatever state on the instance to mark it in the future as
          participating in that shard.

        :param id_chooser: A callable, passed a query and a tuple of identity
          values, which should return a list of shard ids where the ID might
          reside.  The databases will be queried in the order of this listing.

        :param query_chooser: For a given Query, returns the list of shard_ids
          where the query should be issued.  Results from all shards returned
          will be combined together into a single listing.

        :param shards: A dictionary of string shard names
          to :class:`~sqlalchemy.engine.Engine` objects.

        	query_clsN)
r	   r   r
   shard_chooserr   r   _ShardedSession__binds
connectionZconnection_callable
bind_shard)r   rH   r   r   ZshardsrG   r   kr   r   r   r
      s    zShardedSession.__init__c                 K   s^   |d k	r<t |}|jr0|jd }|d k	s,t|S |jr<|jS | j||f|}|d k	rZ||_|S )Nr   )r   keyAssertionErrorr   rH   )r   r   instancer5   statetokenr   r   r   r   _choose_shard_and_assign   s    
z'ShardedSession._choose_shard_and_assignc                 K   sJ   |d kr|  ||}| jd k	r.| jj||dS | j|||djf |S d S )N)r   )r   rO   )rR   ZtransactionrJ   get_bindZ_contextual_connect)r   r   rO   r   r   r   r   r   rJ      s    
  zShardedSession.connectionc                 K   s"   |d kr| j |||d}| j| S )N)r'   )rR   rI   )r   r   r   rO   r'   r5   r   r   r   rS      s      zShardedSession.get_bindc                 C   s   || j |< d S r   )rI   )r   r   bindr   r   r   rK      s    zShardedSession.bind_shard)NNN)NNN)
r=   r>   r?   r   r
   rR   rJ   rS   rK   r@   r   r   r   r   r      s   (
     
	N)rD    r   r   Z	orm.queryr   Zorm.sessionr   __all__r   objectr/   r   r   r   r   r   <module>   s   
 