B
    KLcN                 @   s@  d Z ddl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eZG dd deZG dd deZG dd deZedddd Zedddd Ze dd Zejedddd Zejedddd Z ej!Ze j"Z ej#Z#dS )zBaked query extension.

Provides a creational pattern for the :class:`.query.Query` object which
allows the fully constructed object, Core select statement, and string
compiled result to be fully cached.


    N   )exc)util)strategy_options)Query)Session)func)literal_column)collections_abcc               @   s$   e Zd ZdZdZdd Zdd ZdS )BakeryzCallable which returns a :class:`.BakedQuery`.

    This object is returned by the class method
    :meth:`.BakedQuery.bakery`.  It exists as an object
    so that the "cache" can be easily inspected.

    .. versionadded:: 1.2


    )clscachec             C   s   || _ || _d S )N)r   r   )selfZcls_r    r   3lib/python3.7/site-packages/sqlalchemy/ext/baked.py__init__/   s    zBakery.__init__c             G   s   |  | j||S )N)r   r   )r   
initial_fnargsr   r   r   __call__3   s    zBakery.__call__N)__name__
__module____qualname____doc__	__slots__r   r   r   r   r   r   r   !   s   
r   c               @   s   e Zd ZdZdZd+ddZed,dd	Zd
d Zd-ddZ	dd Z
dd Zdd Zdd Zdd Zdd Zd.ddZdd Zd/dd Zd0d!d"Zd#d$ Zd%d& Zd'd( Zd)d* ZdS )1
BakedQueryz3A builder object for :class:`.query.Query` objects.)steps_bakery
_cache_key_spoiledr   c             C   s*   d| _ | || |g| _d| _|| _d S )Nr   F)r   _update_cache_keyr   r   r   )r   bakeryr   r   r   r   r   r   <   s
    zBakedQuery.__init__   Nc             C   s   t | tj||dS )zSConstruct a new bakery.

        :return: an instance of :class:`.Bakery`

        )Z
size_alert)r   r   ZLRUCache)r   sizeZ_size_alertr   r   r   r    C   s    zBakedQuery.bakeryc             C   s2   t t }| j|_t| j|_| j|_| j|_|S )N)r   __new__r   listr   r   r   )r   Zb1r   r   r   _cloneM   s    
zBakedQuery._clonec             C   s   |  j |jf| 7  _ d S )N)r   __code__)r   fnr   r   r   r   r   U   s    zBakedQuery._update_cache_keyc             C   s$   t |tr| j|  n
| | | S )N)
isinstancetupleadd_criteria)r   otherr   r   r   __iadd__X   s    

zBakedQuery.__iadd__c             C   s"   t |tr| j| S | |S d S )N)r(   r)   with_criteria)r   r+   r   r   r   __add___   s    

zBakedQuery.__add__c             G   s   |  || | j| | S )zAdd a criteria function to this :class:`.BakedQuery`.

        This is equivalent to using the ``+=`` operator to
        modify a :class:`.BakedQuery` in-place.

        )r   r   append)r   r'   r   r   r   r   r*   e   s    zBakedQuery.add_criteriac             G   s   |   j|f| S )zAdd a criteria function to a :class:`.BakedQuery` cloned from this
        one.

        This is equivalent to using the ``+`` operator to
        produce a new :class:`.BakedQuery` with modifications.

        )r%   r*   )r   r'   r   r   r   r   r-   p   s    zBakedQuery.with_criteriac             C   s
   t | |S )zReturn a :class:`_baked.Result` object for this
        :class:`.BakedQuery`.

        This is equivalent to calling the :class:`.BakedQuery` as a
        Python callable, e.g. ``result = my_baked_query(session)``.

        )Result)r   sessionr   r   r   for_sessionz   s    zBakedQuery.for_sessionc             C   s
   |  |S )N)r2   )r   r1   r   r   r   r      s    zBakedQuery.__call__Fc             C   s4   |s*| j s*|  }| jd7  _|jg| _d| _ | S )a  Cancel any query caching that will occur on this BakedQuery object.

        The BakedQuery can continue to be used normally, however additional
        creational functions will not be cached; they will be called
        on every invocation.

        This is to support the case where a particular step in constructing
        a baked query disqualifies the query from being cacheable, such
        as a variant that relies upon some uncacheable value.

        :param full: if False, only functions added to this
         :class:`.BakedQuery` object subsequent to the spoil step will be
         non-cached; the state of the :class:`.BakedQuery` up until
         this point will be pulled from the cache.   If True, then the
         entire :class:`_query.Query` object is built from scratch each
         time, with all creational functions being called on each
         invocation.

        )Z_query_onlyT)r   r%   r   _retrieve_baked_queryr   )r   fullZ_spoil_pointr   r   r   spoil   s    

zBakedQuery.spoilc             C   s   | j |jf S )a  Return the key that actually goes into the cache dictionary for
        this :class:`.BakedQuery`, taking into account the given
        :class:`.Session`.

        This basically means we also will include the session's query_class,
        as the actual :class:`_query.Query` object is part of what's cached
        and needs to match the type of :class:`_query.Query` that a later
        session will want to use.

        )r   Z
_query_cls)r   r1   r   r   r   _effective_key   s    zBakedQuery._effective_keyc             C   s   |   }|j|||d |S )z)Cloning version of _add_lazyload_options.)
cache_path)r%   _add_lazyload_options)r   optionseffective_pathr7   qr   r   r   _with_lazyload_options   s    z!BakedQuery._with_lazyload_optionsc                s~   d}|s }xRD ]J}|j s"|jr| }|dkr@| jdd q|d rPtd||d 7 }qW |  fdd	|j| dS )
a*  Used by per-state lazy loaders to add options to the
        "lazy load" query from a parent query.

        Creates a cache key based on given load path and query options;
        if a repeatable cache key cannot be generated, the query is
        "spoiled" so that it won't use caching.

        r   NT)r4      zloader options with variable bound parameters not supported with baked queries.  Please use new-style select() statements for cached ORM queries.r   c                s   |   j S )N)Z_with_current_pathr9   )r;   )r:   r9   r   r   <lambda>       z2BakedQuery._add_lazyload_options.<locals>.<lambda>)Z_is_legacy_optionZ_is_compile_stateZ_generate_cache_keyr5   AssertionErrorr*   path)r   r9   r:   r7   keyZoptZckr   )r:   r9   r   r8      s    


z BakedQuery._add_lazyload_optionsc             C   sF   | j | |d }|d kr<| |}|d | j | |< ||S )N)r   getr6   	_as_queryZwith_session)r   r1   queryr   r   r   r3      s    
z BakedQuery._retrieve_baked_queryc             C   s<   |  |}d |_| }|jjr4||f| j| |< ||fS )N)rD   r1   Z_statement_20Z_compile_optionsZ_bake_okr   r6   )r   r1   rE   	statementr   r   r   _bake   s    
zBakedQuery._bakec             C   sN   t |tr|}n4t |tr4|j}|dkrDtdntdt| | |S )aL  Return the :class:`_query.Query` object for use as a subquery.

        This method should be used within the lambda callable being used
        to generate a step of an enclosing :class:`.BakedQuery`.   The
        parameter should normally be the :class:`_query.Query` object that
        is passed to the lambda::

            sub_bq = self.bakery(lambda s: s.query(User.name))
            sub_bq += lambda q: q.filter(
                User.id == Address.user_id).correlate(Address)

            main_bq = self.bakery(lambda s: s.query(Address))
            main_bq += lambda q: q.filter(
                sub_bq.to_query(q).exists())

        In the case where the subquery is used in the first callable against
        a :class:`.Session`, the :class:`.Session` is also accepted::

            sub_bq = self.bakery(lambda s: s.query(User.name))
            sub_bq += lambda q: q.filter(
                User.id == Address.user_id).correlate(Address)

            main_bq = self.bakery(
                lambda s: s.query(
                Address.id, sub_bq.to_query(q).scalar_subquery())
            )

        :param query_or_session: a :class:`_query.Query` object or a class
         :class:`.Session` object, that is assumed to be within the context
         of an enclosing :class:`.BakedQuery` callable.


         .. versionadded:: 1.3


        Nz1Given Query needs to be associated with a Sessionz)Query or Session object expected, got %r.)	r(   r   r   r1   sa_excArgumentError	TypeErrortyperD   )r   Zquery_or_sessionr1   r   r   r   to_query   s    &

zBakedQuery.to_queryc             C   s2   | j d |}x| j dd  D ]}||}qW |S )Nr   r=   )r   )r   r1   rE   stepr   r   r   rD   /  s    zBakedQuery._as_query)r   )r!   N)r   )F)N)N)r   r   r   r   r   r   classmethodr    r%   r   r,   r.   r*   r-   r2   r   r5   r6   r<   r8   r3   rG   rL   rD   r   r   r   r   r   7   s*   
	





#	5r   c               @   s   e Zd ZdZdZdd Zdd Zdd Zd	d
 Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Zd!d" Zd#S )$r0   a  Invokes a :class:`.BakedQuery` against a :class:`.Session`.

    The :class:`_baked.Result` object is where the actual :class:`.query.Query`
    object gets created, or retrieved from the cache,
    against a target :class:`.Session`, and is then invoked for results.

    )bqr1   _params_post_criteriac             C   s   || _ || _i | _g | _d S )N)rO   r1   rP   rQ   )r   rO   r1   r   r   r   r   C  s    zResult.__init__c             O   sB   t |dkr||d  nt |dkr2td| j| | S )z@Specify parameters to be replaced into the string SQL statement.r=   r   zFparams() takes zero or one positional argument, which is a dictionary.)lenupdaterH   rI   rP   )r   r   kwr   r   r   paramsI  s    zResult.paramsc             C   s   |r| j | | S )N)rQ   extend)r   Zfnsr   r   r   _using_post_criteriaV  s    zResult._using_post_criteriac             C   s   |  |gS )a  Add a criteria function that will be applied post-cache.

        This adds a function that will be run against the
        :class:`_query.Query` object after it is retrieved from the
        cache.    This currently includes **only** the
        :meth:`_query.Query.params` and :meth:`_query.Query.execution_options`
        methods.

        .. warning::  :meth:`_baked.Result.with_post_criteria`
           functions are applied
           to the :class:`_query.Query`
           object **after** the query's SQL statement
           object has been retrieved from the cache.   Only
           :meth:`_query.Query.params` and
           :meth:`_query.Query.execution_options`
           methods should be used.


        .. versionadded:: 1.2


        )rW   )r   r'   r   r   r   with_post_criteria[  s    zResult.with_post_criteriac             C   s2   | j | j| j}x| jD ]}||}qW |S )N)rO   rD   r1   rU   rP   rQ   )r   r;   r'   r   r   r   rD   t  s    zResult._as_queryc             C   s   t |  S )N)strrD   )r   r   r   r   __str__z  s    zResult.__str__c             C   s   |    S )N)_iter__iter__)r   r   r   r   r\   }  s    zResult.__iter__c       	      C   s   | j }| jjr|jr |   S |j|| jd\}}|d krR|	| j\}}| j
rf|| j
}n|}x| jD ]}||}qrW |j
}t|j}||j|jd | jj|||d}|jddr| }|jddr| }|S )N)NN)Z_sa_orm_load_optionsZcompiled_cache)execution_optionsZis_single_entityFZfiltered)rO   r1   Zenable_baked_queriesr   rD   r[   r   rC   r6   rG   rP   rU   rQ   dictZ_execution_optionsrS   Zload_optionsZexecute_attributesZscalarsunique)	r   rO   rE   rF   r;   r'   rU   r]   resultr   r   r   r[     s0    
zResult._iterc                s:   t td | j fdd}|| j| j	 S )zreturn the 'count'.

        Equivalent to :meth:`_query.Query.count`.

        Note this uses a subquery to ensure an accurate count regardless
        of the structure of the original statement.

        .. versionadded:: 1.1.6

        *c                s
   |   S )N)Z
_from_self)r;   )colr   r   r>     r?   zResult.count.<locals>.<lambda>)
r   countr	   rO   r-   r2   r1   rU   rP   scalar)r   rO   r   )rc   r   rd     s    zResult.countc             C   s<   y |   }t|tjs|S |d S  tjk
r6   dS X dS )zReturn the first element of the first result or None
        if no rows present.  If multiple rows are returned,
        raises MultipleResultsFound.

        Equivalent to :meth:`_query.Query.scalar`.

        .. versionadded:: 1.1.6

        r   N)oner(   r
   Sequenceorm_excZNoResultFound)r   Zretr   r   r   re     s    
zResult.scalarc             C   s4   | j dd }|| j| j| j 	 S )zRReturn the first row.

        Equivalent to :meth:`_query.Query.first`.

        c             S   s   |  ddS )Nr   r=   )slice)r;   r   r   r   r>     r?   zResult.first.<locals>.<lambda>)
rO   r-   r2   r1   rU   rP   rW   rQ   r[   first)r   rO   r   r   r   rj     s    zResult.firstc             C   s   |    S )zkReturn exactly one result or raise an exception.

        Equivalent to :meth:`_query.Query.one`.

        )r[   rf   )r   r   r   r   rf     s    z
Result.onec             C   s   |    S )zReturn one or zero results, or raise an exception for multiple
        rows.

        Equivalent to :meth:`_query.Query.one_or_none`.

        .. versionadded:: 1.0.9

        )r[   one_or_none)r   r   r   r   rk     s    	zResult.one_or_nonec             C   s   |    S )zKReturn all rows.

        Equivalent to :meth:`_query.Query.all`.

        )r[   all)r   r   r   r   rl     s    z
Result.allc             C   s    | j jd | j}||| jS )z`Retrieve an object based on identity.

        Equivalent to :meth:`_query.Query.get`.

        r   )rO   r   r1   Z	_get_impl_load_on_pk_identity)r   ZidentrE   r   r   r   rC     s    z
Result.getc       
         s   |j d jd j\  fdd}j}| }| j f7  _||tdd D }tfddt	j
D }t|jjf |}t|}	|	d	krt n|	r|d S d
S d
S )z6Load the given primary key identity from the database.r   Zparententityc                s|    }|   }|  d |_d krLtfddtjD }t||}t|ddif|_	xj
D ]}||}qhW |S )Nc                s"   g | ]\}}|d kr | j qS )N)rB   ).0rc   value)_get_paramsr   r   
<listcomp>  s   z>Result._load_on_pk_identity.<locals>.setup.<locals>.<listcomp>Z
_orm_adaptT)r%   Z_get_conditionZ	_order_bysetzipprimary_keysql_utilZadapt_criterion_to_nullZ_deep_annotateZ_where_criteriarQ   )rE   Z_lcl_get_clauser;   Znonesr'   )_get_clauserp   mapperprimary_key_identityr   r   r   setup  s    
z*Result._load_on_pk_identity.<locals>.setupc             s   s   | ]}|d kV  qd S )Nr   )rn   elemr   r   r   	<genexpr>0  s    z.Result._load_on_pk_identity.<locals>.<genexpr>c                s   g | ]\}} | j |fqS r   )rB   )rn   Zid_valrt   )rp   r   r   rq   5  s   z/Result._load_on_pk_identity.<locals>.<listcomp>r=   N)Z_raw_columnsZ_annotationsrv   rO   r%   r   r-   r)   r^   rs   rt   r$   r2   r1   rU   rR   rh   ZMultipleResultsFound)
r   r1   rE   rx   rT   ry   rO   rU   ra   lr   )rv   rp   rw   rx   r   r   rm     s&    
"

zResult._load_on_pk_identityN)r   r   r   r   r   r   rU   rW   rX   rD   rZ   r\   r[   rd   re   rj   rf   rk   rl   rC   rm   r   r   r   r   r0   8  s$   '
r0   z1.2z5Baked lazy loading is now the default implementation.c               C   s   dS )zEnable the use of baked queries for all lazyloaders systemwide.

    The "baked" implementation of lazy loading is now the sole implementation
    for the base lazy loader; this method has no effect except for a warning.

    Nr   r   r   r   r   bake_lazy_loadersF  s    
r}   c               C   s   t ddS )aH  Disable the use of baked queries for all lazyloaders systemwide.

    This method now raises NotImplementedError() as the "baked" implementation
    is the only lazy load implementation.  The
    :paramref:`_orm.relationship.bake_queries` flag may be used to disable
    the caching of queries on a per-relationship basis.

    z4Baked lazy loading is now the default implementationN)NotImplementedErrorr   r   r   r   unbake_lazy_loadersS  s    r   c             C   s   |  |ddiS )zxIndicate that the given attribute should be loaded using "lazy"
    loading with a "baked" query used in the load.

    ZlazyZbaked_select)Zset_relationship_strategy)Zloadoptattrr   r   r   baked_lazyloadd  s    r   zFBaked lazy loading is now the default implementation for lazy loading.c              G   s   t jt jj| di S )NF)r   _UnboundLoad
_from_keysr   )keysr   r   r   r   m  s    c              G   s   t jt jj| di S )NT)r   r   r   r   )r   r   r   r   baked_lazyload_ally  s    r   )$r   Zlogging r   rH   r   Zormrh   r   Z	orm.queryr   Zorm.sessionr   Zsqlr   r	   ru   r
   Z	getLoggerr   logobjectr   r   r0   Z
deprecatedr}   r   Zloader_optionr   Z_add_unbound_fnZ_add_unbound_all_fnr   Z_unbound_fnZ_unbound_all_fnr    r   r   r   r   <module>   sF   
    			