U
    [h3A                     @  sv  d Z ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm	Z	 dd	lm
Z
 dd
lmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlm Z  ddl!m"Z" ddl!m#Z# erddl$m%Z% ddl$m&Z& ddl$m'Z' ddl(m)Z) dd l(m*Z* dd!lm+Z+ dd"lm,Z, dd#l-m.Z. dd$l/m0Z0 dd%lm1Z1 dd&lm2Z2 dd'lm3Z3 dd(l4m5Z5 dd)l6m7Z7 dd*l8m9Z9 dd+l:m;Z; d,d-gZ<ed.ed/Z=e>Z?G d0d1 d1e"Z@G d2d3 d3e"ZAG d4d- d-ee= ZBG d5d, d,e ZCG d6d7 d7eZDd8d9d:d;d<ZEd=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.

.. deepalchemy:: The horizontal sharding extension is an advanced feature,
   involving a complex statement -> database interaction as well as
   use of semi-public APIs for non-trivial cases.   Simpler approaches to
   refering to multiple database "shards", most commonly using a distinct
   :class:`_orm.Session` per "shard", should always be considered first
   before using this more complex and less-production-tested system.



    )annotations)Any)Callable)Dict)Iterable)Optional)Tuple)Type)TYPE_CHECKING)TypeVar)Union   )event)exc)inspect)util)PassiveFlag)OrmExecuteOptionsParameter)	ORMOption)Mapper)Query)_BindArguments)_PKIdentityArgument)Session)Protocol)Self)
Connection)Engine)OptionEngine)IteratorResult)Result)LoaderCallableStatus)_O)BulkUDCompileState)QueryContext)_EntityBindKey)_SessionBind)ORMExecuteState)InstanceState)
Executable)_TP)ClauseElementShardedSessionShardedQuery_T)Zboundc                   @  s    e Zd ZdddddddZdS )ShardChooserOptional[Mapper[_T]]r   Optional[ClauseElement])mapperinstanceclausereturnc                 C  s   d S N )selfr2   r3   r4   r7   r7   E./venv/lib/python3.8/site-packages/sqlalchemy/ext/horizontal_shard.py__call__Q   s    zShardChooser.__call__N__name__
__module____qualname__r:   r7   r7   r7   r9   r/   P   s   r/   c                   @  s&   e Zd Zdddddddddd	Zd
S )IdentityChooser
Mapper[_T]r   Optional[InstanceState[Any]]r   r   r   r2   primary_keylazy_loaded_fromexecution_optionsbind_argumentskwr5   c                K  s   d S r6   r7   )r8   r2   rC   rD   rE   rF   rG   r7   r7   r9   r:   Z   s    	zIdentityChooser.__call__Nr;   r7   r7   r7   r9   r?   Y   s   r?   c                      s:   e Zd ZdZdddd fddZddd	d
dZ  ZS )r-   a  Query class used with :class:`.ShardedSession`.

    .. legacy:: The :class:`.ShardedQuery` is a subclass of the legacy
       :class:`.Query` class.   The :class:`.ShardedSession` now supports
       2.0 style execution via the :meth:`.ShardedSession.execute` method.

    r   None)argskwargsr5   c                   s<   t  j|| t| jtst| jj| _| jj| _d | _d S r6   )	super__init__
isinstancesessionr,   AssertionErroridentity_chooserexecute_chooserZ	_shard_id)r8   rI   rJ   	__class__r7   r9   rL   o   s
    

zShardedQuery.__init__ShardIdentifierr   shard_idr5   c                 C  s   | j |dS )a  Return 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.

        The shard_id can be passed for a 2.0 style execution to the
        bind_arguments dictionary of :meth:`.Session.execute`::

            results = session.execute(stmt, bind_arguments={"shard_id": "my_shard"})

        )_sa_shard_id)rE   )r8   rV   r7   r7   r9   	set_shardw   s    zShardedQuery.set_shard)r<   r=   r>   __doc__rL   rX   __classcell__r7   r7   rR   r9   r-   f   s   c                      s   e Zd ZU ded< ded< ded< dddefddddd	d
ddddddd	 fddZdejdej	dfdddddddddd	 fddZ
ddddd d!d"Zd4d#dd$dd%d&d'd(Zd5dddd)dd$dd*dd+d,d-d.Zd/d0dd1d2d3Z  ZS )6r,   r/   shard_chooserr?   rP   z*Callable[[ORMExecuteState], Iterable[Any]]rQ   N)
id_chooserquery_chooserzOptional[IdentityChooser]z4Optional[Callable[[ORMExecuteState], Iterable[Any]]]zOptional[Dict[str, Any]]zType[Query[_T]]z<Optional[Callable[[Query[_T], Iterable[_T]], Iterable[Any]]]z/Optional[Callable[[Executable], Iterable[Any]]]r   rH   )	r[   rP   rQ   shards	query_clsr\   r]   rJ   r5   c                  s  t  jf d|i| tjdtdd |_|rj| tdd ddd	d
dddd fdd}	|	_n|rv|_n
t	
d|r|tdd |rt	
ddddfdd}
|dkr|
}|dkrt	
d|_i _|dk	r|D ]}|||  qd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 identity_chooser: A callable, passed a Mapper and primary key
         argument, which should return a list of shard ids where this
         primary key might reside.

          .. versionchanged:: 2.0  The ``identity_chooser`` parameter
             supersedes the ``id_chooser`` parameter.

        :param execute_chooser: For a given :class:`.ORMExecuteState`,
          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.

          .. versionchanged:: 1.4  The ``execute_chooser`` parameter
             supersedes the ``query_chooser`` parameter.

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

        r_   Zdo_orm_executeT)ZretvalzLThe ``id_chooser`` parameter is deprecated; please use ``identity_chooser``.z2.0r@   r   rA   r   r   r   rB   c                  s"    | }|r||} ||S r6   )ZqueryZ_set_lazyload_from)r2   rC   rD   rE   rF   rG   q)_id_chooserr8   r7   r9   _legacy_identity_chooser   s    	

z9ShardedSession.__init__.<locals>._legacy_identity_chooserz*identity_chooser or id_chooser is requiredzNThe ``query_chooser`` parameter is deprecated; please use ``execute_chooser``.z1.4z>Can't pass query_chooser and execute_chooser at the same time.r'   zIterable[Any]orm_contextr5   c                   s
    | j S r6   )Z	statementrd   )_query_chooserr7   r9   _default_execute_chooser   s    z9ShardedSession.__init__.<locals>._default_execute_chooserNz,execute_chooser or query_chooser is required)rK   rL   r   Zlistenexecute_and_instancesr[   r   Zwarn_deprecatedrP   r   ArgumentErrorrQ   _ShardedSession__shards
bind_shard)r8   r[   rP   rQ   r^   r_   r\   r]   rJ   rb   rg   krR   )ra   rf   r8   r9   rL      sV    ,    
zShardedSession.__init__z
Mapper[_O]zUnion[Any, Tuple[Any, ...]]zOptional[Any]r   rA   r   zOptional[_BindArguments]z)Union[Optional[_O], LoaderCallableStatus])	r2   primary_key_identityidentity_tokenpassiverD   rE   rF   rG   r5   c                   s   |dk	r&t  j||fd|i|}	|	S | j|||||r>t|ni dD ]0}
t  j||f|
|d|}|dk	rF|  S qFdS dS )a_  override the default :meth:`.Session._identity_lookup` method so
        that we search for a given non-token primary key identity across all
        possible identity tokens (e.g. shard ids).

        .. versionchanged:: 1.4  Moved :meth:`.Session._identity_lookup` from
           the :class:`_query.Query` object to the :class:`.Session`.

        Nrn   )rD   rE   rF   )rn   rD   )rK   _identity_lookuprP   dict)r8   r2   rm   rn   ro   rD   rE   rF   rG   objrV   Zobj2rR   r7   r9   rp      s:    

zShardedSession._identity_lookupzOptional[_EntityBindKey[_O]])r2   r3   rG   r5   c                 K  sl   |d k	r<t |}|jr0|jd }|d k	s,t|S |jr<|jS t|tsJt| j||f|}|d k	rh||_|S )Nr   )r   keyrO   rn   rM   r   r[   )r8   r2   r3   rG   statetokenrV   r7   r7   r9   _choose_shard_and_assign,  s    
z'ShardedSession._choose_shard_and_assignr0   zOptional[ShardIdentifier]r   )r2   r3   rV   rG   r5   c                 K  sz   |dkr|  ||}|  r>|  }|dk	s0t|j||dS | j|||d}t|trd|jf |S t|t	srt|S dS )zaProvide a :class:`_engine.Connection` to use in the unit of work
        flush process.

        N)rV   )r2   rV   r3   )
rv   Zin_transactionZget_transactionrO   Z
connectionget_bindrM   r   Zconnectr   )r8   r2   r3   rV   rG   Ztransbindr7   r7   r9   connection_callableA  s      
z"ShardedSession.connection_callable)rV   r3   r4   r1   r&   )r2   rV   r3   r4   rG   r5   c                K  s.   |d kr$| j |||d}|d k	s$t| j| S )N)r3   r4   )rv   rO   rj   )r8   r2   rV   r3   r4   rG   r7   r7   r9   rw   _  s    	  zShardedSession.get_bindrT   zUnion[Engine, OptionEngine])rV   rx   r5   c                 C  s   || j |< d S r6   )rj   )r8   rV   rx   r7   r7   r9   rk   o  s    zShardedSession.bind_shard)NNN)N)r<   r=   r>   __annotations__r-   rL   r   ZPASSIVE_OFFr   Z
EMPTY_DICTrp   rv   ry   rw   rk   rZ   r7   r7   rR   r9   r,      s:   
&t$1     c                   @  s&   e Zd ZdZdZd	dddddZdS )
set_shard_ida  a loader option for statements to apply a specific shard id to the
    primary query as well as for additional relationship and column
    loaders.

    The :class:`_horizontal.set_shard_id` option may be applied using
    the :meth:`_sql.Executable.options` method of any executable statement::

        stmt = (
            select(MyObject)
            .where(MyObject.name == "some name")
            .options(set_shard_id("shard1"))
        )

    Above, the statement when invoked will limit to the "shard1" shard
    identifier for the primary query as well as for all relationship and
    column loading strategies, including eager loaders such as
    :func:`_orm.selectinload`, deferred column loaders like :func:`_orm.defer`,
    and the lazy relationship loader :func:`_orm.lazyload`.

    In this way, the :class:`_horizontal.set_shard_id` option has much wider
    scope than using the "shard_id" argument within the
    :paramref:`_orm.Session.execute.bind_arguments` dictionary.


    .. versionadded:: 2.0.0

    rV   propagate_to_loadersTrT   boolc                 C  s   || _ || _dS )aH  Construct a :class:`_horizontal.set_shard_id` option.

        :param shard_id: shard identifier
        :param propagate_to_loaders: if left at its default of ``True``, the
         shard option will take place for lazy loaders such as
         :func:`_orm.lazyload` and :func:`_orm.defer`; if False, the option
         will not be propagated to loaded objects. Note that :func:`_orm.defer`
         always limits to the shard_id of the parent row in any case, so the
         parameter only has a net effect on the behavior of the
         :func:`_orm.lazyload` strategy.

        Nr|   )r8   rV   r}   r7   r7   r9   rL     s    zset_shard_id.__init__N)T)r<   r=   r>   rY   	__slots__rL   r7   r7   r7   r9   r{   u  s    r{   r'   &Union[Result[_T], IteratorResult[_TP]]rc   c                   s   j r j}n js jr" j}nd } j}t|ts:tddd fdd} j	D ]}t|t
rT|j} qqT|r|jd k	r|j}n0d jkr jd }nd jkr jd }nd }|d k	r||S g }| D ]}||}|| q|d j|d	d   S d S )
NrT   r   rU   c                   s*   t  j}| |d<  j| d  j|dS )NrV   )rn   )rF   )rq   rF   Zupdate_execution_optionsZinvoke_statement)rV   rF   re   r7   r9   iter_for_shard  s    
z-execute_and_instances.<locals>.iter_for_shardrW   rV   r      )Z	is_selectZload_optionsZ	is_updateZ	is_deleteZupdate_delete_optionsrN   rM   r,   rO   Z_non_compile_orm_optionsr{   rV   Z_identity_tokenrE   rF   rQ   appendmerge)rd   Zactive_optionsrN   r   Zorm_optrV   partialZresult_r7   re   r9   rh     s4    	



rh   N)FrY   Z
__future__r   typingr   r   r   r   r   r   r	   r
   r   r    r   r   r   r   Zormr   Zorm._typingr   Zorm.interfacesr   Z
orm.mapperr   Z	orm.queryr   Zorm.sessionr   r   r   Zutil.typingr   r   Zengine.baser   r   r   Zengine.resultr   r    r!   r"   Zorm.bulk_persistencer#   Zorm.contextr$   r%   r&   r'   Z	orm.stater(   Zsqlr)   Zsql._typingr*   Zsql.elementsr+   __all__r.   strrT   r/   r?   r-   r,   r{   rh   r7   r7   r7   r9   <module>   sh   	  p2