
    g Y                        U d dl mZ d dlmZmZ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j                   e      Zded	<   er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  G d de	d         Zy)    )annotations)TYPE_CHECKINGFinalcast)logger)BaseConnection)running_in_sis)StreamlitAPIException)
cache_datar   _LOGGER)	timedelta)	DataFrame)SnowflakeCursorSession)SnowflakeConnectionc                      e Zd ZdZddZdddd	 	 	 	 	 	 	 ddZ	 	 	 d	 	 	 	 	 	 	 	 	 	 	 ddZddZedd	       Z	dd
Z
y)r   am  A connection to Snowflake using the Snowflake Connector for Python.

    Initialize this connection object using ``st.connection("snowflake")`` or
    ``st.connection("<name>", type="snowflake")``. Connection parameters for a
    SnowflakeConnection can be specified using ``secrets.toml`` and/or
    ``**kwargs``. Connection parameters are passed to
    |snowflake.connector.connect()|.

    When an app is running in Streamlit in Snowflake,
    ``st.connection("snowflake")`` connects automatically using the app owner's
    role without further configuration. ``**kwargs`` will be ignored in this
    case. Use ``secrets.toml`` and ``**kwargs`` to configure your connection
    for local development.

    SnowflakeConnection includes several convenience methods. For example, you
    can directly execute a SQL query with ``.query()`` or access the underlying
    Snowflake Connector object with ``.raw_connection``.

    .. |snowflake.connector.connect()| replace:: ``snowflake.connector.connect()``
    .. _snowflake.connector.connect(): https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#label-snowflake-connector-methods-connect

    .. Tip::
        `snowflake-snowpark-python <https://pypi.org/project/snowflake-snowpark-python/>`_
        must be installed in your environment to use this connection. You can
        install Snowflake extras along with Streamlit:

        >>> pip install streamlit[snowflake]

    .. Important::
        Account identifiers must be of the form ``<orgname>-<account_name>``
        where ``<orgname>`` is the name of your Snowflake organization and
        ``<account_name>`` is the unique name of your account within your
        organization. This is dash-separated, not dot-separated like when used
        in SQL queries. For more information, see `Account identifiers
        <https://docs.snowflake.com/en/user-guide/admin-account-identifier>`_.

    Examples
    --------

    **Example 1: Configuration with Streamlit secrets**

    You can configure your Snowflake connection using Streamlit's
    `Secrets management <https://docs.streamlit.io/develop/concepts/connections/secrets-management>`_.
    For example, if you have MFA enabled on your account, you can connect using
    `key-pair authentication <https://docs.snowflake.com/en/user-guide/key-pair-auth>`_.

    ``.streamlit/secrets.toml``:

    >>> [connections.snowflake]
    >>> account = "xxx-xxx"
    >>> user = "xxx"
    >>> private_key_file = "/xxx/xxx/xxx.p8"
    >>> role = "xxx"
    >>> warehouse = "xxx"
    >>> database = "xxx"
    >>> schema = "xxx"

    Your app code:

    >>> import streamlit as st
    >>> conn = st.connection("snowflake")
    >>> df = conn.query("SELECT * FROM my_table")

    **Example 2: Configuration with keyword arguments and external authentication**

    You can configure your Snowflake connection with keyword arguments. The
    keyword arguments are merged with (and take precedence over) the values in
    ``secrets.toml``. However, if you name your connection ``"snowflake"`` and
    don't have a ``[connections.snowflake]`` dictionary in your
    ``secrets.toml`` file, Streamlit will ignore any keyword arguments and use
    the default Snowflake connection as described in Example 5 and Example 6.
    To configure your connection using only keyword arguments, declare a name
    for the connection other than ``"snowflake"``.

    For example, if your Snowflake account supports SSO, you can set up a quick
    local connection for development using `browser-based SSO
    <https://docs.snowflake.com/en/user-guide/admin-security-fed-auth-use#how-browser-based-sso-works>`_.
    Because there is nothing configured in ``secrets.toml``, the name is an
    empty string and the type is set to ``"snowflake"``. This prevents
    Streamlit from ignoring the keyword arguments and using a default
    Snowflake connection.

    >>> import streamlit as st
    >>> conn = st.connection(
    ...     "",
    ...     type="snowflake",
    ...     account="xxx-xxx",
    ...     user="xxx",
    ...     authenticator="externalbrowser",
    ... )
    >>> df = conn.query("SELECT * FROM my_table")

    **Example 3: Named connection with Snowflake's connection configuration file**

    Snowflake's Python Connector supports a `connection configuration file
    <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#connecting-using-the-connections-toml-file>`_,
    which is well integrated with Streamlit's ``SnowflakeConnection``. If you
    already have one or more connections configured, all you need to do is pass
    the name of the connection to use.

    ``~/.snowflake/connections.toml``:

    >>> [my_connection]
    >>> account = "xxx-xxx"
    >>> user = "xxx"
    >>> password = "xxx"
    >>> warehouse = "xxx"
    >>> database = "xxx"
    >>> schema = "xxx"

    Your app code:

    >>> import streamlit as st
    >>> conn = st.connection("my_connection", type="snowflake")
    >>> df = conn.query("SELECT * FROM my_table")

    **Example 4: Named connection with Streamlit secrets and Snowflake's connection configuration file**

    If you have a Snowflake configuration file with a connection named
    ``my_connection`` as in Example 3, you can pass the connection name through
    ``secrets.toml``.

    ``.streamlit/secrets.toml``:

    >>> [connections.snowflake]
    >>> connection_name = "my_connection"

    Your app code:

    >>> import streamlit as st
    >>> conn = st.connection("snowflake")
    >>> df = conn.query("SELECT * FROM my_table")

    **Example 5: Default connection with an environment variable**

    If you don't have a ``[connections.snowflake]`` dictionary in your
    ``secrets.toml`` file and use ``st.connection("snowflake")``, Streamlit
    will use the default connection for the `Snowflake Python Connector
    <https://docs.snowflake.cn/en/developer-guide/python-connector/python-connector-connect#setting-a-default-connection>`_.

    If you have a Snowflake configuration file with a connection named
    ``my_connection`` as in Example 3, you can set an environment variable to
    declare it as the default Snowflake connection.

    >>> SNOWFLAKE_DEFAULT_CONNECTION_NAME = "my_connection"

    Your app code:

    >>> import streamlit as st
    >>> conn = st.connection("snowflake")
    >>> df = conn.query("SELECT * FROM my_table")

    **Example 6: Default connection in Snowflake's connection configuration file**

    If you have a Snowflake configuration file that defines your ``default``
    connection, Streamlit will automatically use it if no other connection is
    declared.

    ``~/.snowflake/connections.toml``:

    >>> [default]
    >>> account = "xxx-xxx"
    >>> user = "xxx"
    >>> password = "xxx"
    >>> warehouse = "xxx"
    >>> database = "xxx"
    >>> schema = "xxx"

    Your app code:

    >>> import streamlit as st
    >>> conn = st.connection("snowflake")
    >>> df = conn.query("SELECT * FROM my_table")

    c                   dd l }ddl m} t               r;ddlm}  |       }t        |d      r|j                  S |j                  j                  S d|j                  _	        	 | j                  j                         }t        |      r7t        j                  d       i ||} |j                  j                  d
i |S | j                   dk(  r/t        j                  d       |j                  j                         S  |j                  j                  d
i |S # |$ r(}t              st        |      st#        d	      |d }~ww xY w)Nr   )Errorget_active_session
connectionqmarkzVConnect to Snowflake using the Streamlit secret defined under [connections.snowflake].	snowflakezConnect to Snowflake using the default configuration as defined in https://docs.snowflake.cn/en/developer-guide/python-connector/python-connector-connect#setting-a-default-connectiona?  Missing Snowflake connection configuration. Did you forget to set this in `secrets.toml`, a Snowflake configuration file, or as kwargs to `st.connection`? See the [SnowflakeConnection configuration documentation](https://docs.streamlit.io/st.connections.snowflakeconnection-configuration) for more details and examples. )snowflake.connectorr   r	   snowflake.snowpark.contextr   hasattrr   _conn	connector
paramstyle_secretsto_dictlenr   infoconnect_connection_namer
   )	selfkwargsr   SnowflakeErrorr   session
st_secretsconn_kwargses	            _/var/www/openai/venv/lib/python3.12/site-packages/streamlit/connections/snowflake_connection.py_connectzSnowflakeConnection._connect   s>   "?  )*Gw-))) ==&&& *1	&	..0J:/ 76v62y**22A[AA $$3M !**2244.9&&..888 		z?3v;+5  G		s%   "AD >=D <D E#E  ENzRunning `snowflake.query(...)`.)ttlshow_spinnerparamsc          	     8    ddl m}m}m}m}	 d | fd |d      d |fd       |	d      	      d fd
       }
t        |      j                  dd      }|
j                   d j                   d| |
_         t        ||      |
      }
 |
|      S )a  Run a read-only SQL query.

        This method implements query result caching and simple error
        handling/retries. The caching behavior is identical to that of using
        ``@st.cache_data``.

        .. note::
            Queries that are run without a specified ``ttl`` are cached
            indefinitely.

        Parameters
        ----------
        sql : str
            The read-only SQL query to execute.
        ttl : float, int, timedelta or None
            The maximum number of seconds to keep results in the cache. If this
            is ``None`` (default), cached results do not expire with time.
        show_spinner : boolean or string
            Whether to enable the spinner. When a cached query is executed, no
            spinner is displayed because the result is immediately available.
            When a new query is executed, the default is to show a spinner with
            the message "Running ``snowflake.query(...)``."

            If this is ``False``, no spinner displays while executing the
            query. If this is a string, the string will be used as the message
            for the spinner.
        params : list, tuple, dict or None
            List of parameters to pass to the Snowflake Connector for Python
            ``Cursor.execute()`` method. This connector supports binding data
            to a SQL statement using qmark bindings. For more information and
            examples, see the `Snowflake Connector for Python documentation
            <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example#using-qmark-or-numeric-binding>`_.
            This defaults to ``None``.

        Returns
        -------
        pandas.DataFrame
            The result of running the query, formatted as a pandas DataFrame.

        Example
        -------

        >>> import streamlit as st
        >>>
        >>> conn = st.connection("snowflake")
        >>> df = conn.query("SELECT * FROM my_table")
        >>> st.dataframe(df)

        r   )retryretry_if_exceptionstop_after_attempt
wait_fixed08001c                $    j                         S )N)reset)_r(   s    r/   <lambda>z+SnowflakeConnection.query.<locals>.<lambda>V  s    DJJL       Tc                >    t        | d      xr | j                  k(  S )Nsqlstate)r   rA   )r.   'SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHEDs    r/   r=   z+SnowflakeConnection.query.<locals>.<lambda>]  s%    '!Z0 JJJ"IIJr>      )afterstopreraiser5   waitc                    j                   j                         } |j                  | fdi |j                         S )Nr3   )	_instancecursorexecutefetch_pandas_all)sqlcurr)   r3   r(   s     r/   _queryz)SnowflakeConnection.query.<locals>._queryU  s>     ..'')CCKK5F5f5''))r>   .r<   )r2   r1   )rM   strreturnr   )
tenacityr5   r6   r7   r8   rQ   replace__qualname__r'   r   )r(   rM   r1   r2   r3   r)   r5   r6   r7   r8   rO   ttl_strrB   s   `   ``      @r/   queryzSnowflakeConnection.query  s    t 	WV 3:/	(#A& %J A

	*

	* 

'#s
 	 "(!4!4 5Qt7L7L6MQwiX
%
 
 c{r>   c           
     T    ddl m}  |d| j                  |||||d|\  }}	}
}||	|
fS )aS	  Write a ``pandas.DataFrame`` to a table in a Snowflake database.

        This convenience method is a thin wrapper around
        ``snowflake.connector.pandas_tools.write_pandas()`` using the
        underlying connection. The ``conn`` parameter is passed automatically.
        For more information and additional keyword arguments, see the
        `Snowflake Connector for Python documentation
        <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#write_pandas>`_.

        Parameters
        ----------
        df: pandas.DataFrame
            The ``pandas.DataFrame`` object containing the data to be copied
            into the table.
        table_name: str
            Name of the table where the data should be copied to.
        database: str
            Name of the database containing the table. By default, the function
            writes to the database that is currently in use in the session.

            .. Note::
                If you specify this parameter, you must also specify the schema
                parameter.

        schema: str
            Name of the schema containing the table. By default, the function
            writes to the table in the schema that is currently in use in the
            session.
        chunk_size: int
            Number of elements to insert at a time. By default, the function
            inserts all elements in one chunk.
        **kwargs: Any
            Additional keyword arguments for
            ``snowflake.connector.pandas_tools.write_pandas()``.

        Returns
        -------
        tuple[bool, int, int]
            A tuple containing three values:

            1. A boolean value that is ``True`` if the write was successful.
            2. An integer giving the number of chunks of data that were copied.
            3. An integer giving the number of rows that were inserted.

        Example
        -------
        The following example uses the database and schema currently in use in
        the session and copies the data into a table named "my_table."

        >>> import streamlit as st
        >>> import pandas as pd
        >>>
        >>> df = pd.DataFrame(
        ...     {"Name": ["Mary", "John", "Robert"], "Pet": ["dog", "cat", "bird"]}
        ... )
        >>> conn = st.connection("snowflake")
        >>> conn.write_pandas(df, "my_table")

        r   )write_pandas)conndf
table_namedatabaseschema
chunk_sizer   ) snowflake.connector.pandas_toolsrY   rI   )r(   r[   r\   r]   r^   r_   r)   rY   successnchunksnrowsr<   s               r/   rY   z SnowflakeConnection.write_pandasv  sP    H 	B%1 &
!!&
 &
"% %((r>   c                6    | j                   j                         S )aD  Create a new cursor object from this connection.

        Snowflake Connector cursors implement the Python Database API v2.0
        specification (PEP-249). For more information, see the
        `Snowflake Connector for Python documentation
        <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#object-cursor>`_.

        Returns
        -------
        snowflake.connector.cursor.SnowflakeCursor
            A cursor object for the connection.

        Example
        -------
        The following example uses a cursor to insert multiple rows into a
        table. The ``qmark`` parameter style is specified as an optional
        keyword argument. Alternatively, the parameter style can be declared in
        your connection configuration file. For more information, see the
        `Snowflake Connector for Python documentation
        <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example#using-qmark-or-numeric-binding>`_.

        >>> import streamlit as st
        >>>
        >>> conn = st.connection("snowflake", "paramstyle"="qmark")
        >>> rows_to_insert = [("Mary", "dog"), ("John", "cat"), ("Robert", "bird")]
        >>> conn.cursor().executemany(
        ...     "INSERT INTO mytable (name, pet) VALUES (?, ?)", rows_to_insert
        ... )

        )rI   rJ   r(   s    r/   rJ   zSnowflakeConnection.cursor  s    > ~~$$&&r>   c                    | j                   S )a  Access the underlying connection object from the Snowflake        Connector for Python.

        For information on how to use the Snowflake Connector for Python, see
        the `Snowflake Connector for Python documentation
        <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example>`_.

        Returns
        -------
        snowflake.connector.connection.SnowflakeConnection
            The connection object.

        Example
        -------
        The following example uses a cursor to submit an asynchronous query,
        saves the query ID, then periodically checks the query status through
        the connection before retrieving the results.

        >>> import streamlit as st
        >>> import time
        >>>
        >>> conn = st.connection("snowflake")
        >>> cur = conn.cursor()
        >>> cur.execute_async("SELECT * FROM my_table")
        >>> query_id = cur.sfqid
        >>> while True:
        ...     status = conn.raw_connection.get_query_status(query_id)
        ...     if conn.raw_connection.is_still_running(status):
        ...         time.sleep(1)
        ...     else:
        ...         break
        >>> cur.get_results_from_sfqid(query_id)
        >>> df = cur.fetchall()

        )rI   re   s    r/   raw_connectionz"SnowflakeConnection.raw_connection  s    J ~~r>   c                    ddl m} ddlm} t	               r |       S t        ||j                  j                  d| j                  i      j                               S )aV  Create a new Snowpark session from this connection.

        For information on how to use Snowpark sessions, see the
        `Snowpark developer guide
        <https://docs.snowflake.com/en/developer-guide/snowpark/python/working-with-dataframes>`_
        and `Snowpark API Reference
        <https://docs.snowflake.com/en/developer-guide/snowpark/reference/python/latest/snowpark/session>`_.

        Returns
        -------
        snowflake.snowpark.Session
            A new Snowpark session for this connection.

        Example
        -------
        The following example creates a new Snowpark session and uses it to run
        a query.

        >>> import streamlit as st
        >>>
        >>> conn = st.connection("snowflake")
        >>> session = conn.session()
        >>> df = session.sql("SELECT * FROM my_table").collect()

        r   r   r   r   )
r   r   snowflake.snowpark.sessionr   r	   r   builderconfigsrI   create)r(   r   r   s      r/   r+   zSnowflakeConnection.session  sM    4 	B6%''W__,,lDNN-KLSSU
 	
r>   )rR   InternalSnowflakeConnection)rM   rQ   r1   zfloat | int | timedelta | Noner2   z
bool | strrR   r   )NNN)r[   r   r\   rQ   r]   
str | Noner^   rn   r_   z
int | NonerR   ztuple[bool, int, int])rR   r   )rR   r   )__name__
__module__rU   __doc__r0   rW   rY   rJ   propertyrg   r+   r   r>   r/   r   r   -   s    n`6x /3#D^^ ,	^
 !^ 
^H  $!!%P)P) P) 	P)
 P) P) 
P)d'B $ $L"
r>   r   rm   N)
__future__r   typingr   r   r   	streamlitr   streamlit.connectionsr   streamlit.connections.utilr	   streamlit.errorsr
   streamlit.runtime.cachingr   
get_loggerro   r   __annotations__datetimer   pandasr   snowflake.connector.cursorr   ri   r   r   r   rm   r   r>   r/   <module>r      s^   * # - -  0 5 2 0"""8, ," :2
E
.)FG E
r>   