
    KiE                         d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZmZmZmZ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 ddlmZ d	Zd
ZddddddZd	Z dee!         de!fdZ"ededee#e$e%e%f                           fd            Z&	 d3dede%deddfdZ'de%fdZ(d4de
e%ge)f         de*de%fdZ+de	dee%ee	         f         fd Z,dedej-        fd!Z.ded"e%d#e%dej-        fd$Z/ded"e%d#e%de)fd%Z0ded"e%d#e%ddfd&Z1	 d3ded"e%d#e%d'ej-        d(ee%e%f         ddfd)Z2ded"e%d'ej-        dee%e%f         fd*Z3d+e%de%fd,Z4dede%fd-Z5ded"e%d#e%de)fd.Z6	 d5d0eeej-        ej7        ej8        f                  d1e%deej-                 fd2Z9dS )6a.  General utilities for AI features in MySQL Connector/Python.

Includes helpers for:
- defensive dict copying
- temporary table lifecycle management
- SQL execution and result conversions
- DataFrame to/from SQL table utilities
- schema/table/column name validation
- array-like to DataFrame conversion
    N)contextmanager)AnyCallableDictIteratorListOptionalTupleUnion)atomic_transaction)MySQLConnectionAbstract)MySQLCursorAbstract)ParamsSequenceOrDictTypemysql_ai    BIGINTDOUBLELONGTEXTBOOLEANDATETIME)int64float64objectboolzdatetime64[ns]optionsreturnc                 2    | i S t          j        |           S )z
    Make a defensive copy of a dictionary, or return an empty dict if None.

    Args:
        options: param dict or None

    Returns:
        dict
    )copydeepcopy)r   s    P/var/www/html/analyses/venv/lib/python3.11/site-packages/mysql/ai/utils/utils.py	copy_dictr!   I   s     	=!!!    db_connectionc           	   #   (  K   g }	 |V  t          |           5 }|D ]\  }}t          |||           	 ddd           dS # 1 swxY w Y   dS # t          |           5 }|D ]\  }}t          |||           	 ddd           w # 1 swxY w Y   w xY w)a  
    Context manager to track and automatically clean up temporary SQL tables.

    Args:
        db_connection: Database connection object used to create and delete tables.

    Returns:
        None

    Raises:
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.

    Yields:
        temporary_tables: List of (schema_name, table_name) tuples created during the
            context. All tables in this list are deleted on context exit.
    N)r   delete_sql_table)r#   temporary_tablescursorschema_name
table_names        r    temporary_sql_tablesr*   Y   s~     , /1B.. 	B&+; B B'Z jAAAAB	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B.. 	B&+; B B'Z jAAAAB	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	Bs@   A AAABB9BB		BB	Br'   queryparamsc                 6    |                      ||pd           dS )aB  
    Execute an SQL query with optional parameters using the given cursor.

    Args:
        cursor: MySQLCursorAbstract object to execute the query.
        query: SQL query string to execute.
        params: Optional sequence or dict providing parameters for the query.

    Raises:
        DatabaseError:
            If the provided SQL query/params are invalid
            If the query is valid but the sql raises as an exception
            If a database connection issue occurs.
            If an operational error occurs during execution.

    Returns:
        None
     N)execute)r'   r+   r,   s      r    execute_sqlr0   x   s"    * NN5&,B'''''r"   c                  v    t           j        } d                    t          j        | t
                              S )z
    Generate a random uppercase string of fixed length for table names.

    Returns:
        Random string of length RANDOM_TABLE_NAME_LENGTH.
     )k)stringascii_uppercasejoinrandomchoicesRANDOM_TABLE_NAME_LENGTH)char_sets    r    	_get_namer;      s.     %H776>(.FGGGHHHr"   d   	condition	max_callsc                 ~    t          |          D ]} | t                      x}          r|c S  t          d          )a  
    Generate a random string name that satisfies a given condition.

    Args:
        condition: Callable that takes a generated name and returns True if it is valid.
        max_calls: Maximum number of attempts before giving up (default 100).

    Returns:
        A random string that fulfills the provided condition.

    Raises:
        RuntimeError: If the maximum number of attempts is reached without success.
    z<Reached max tries without successfully finding a unique name)ranger;   RuntimeError)r=   r>   _names       r    get_random_namerD      sU     9  9Y[[(T)) 	KKK	 U
V
VVr"   valuec                     t          | t          t          f          r/t          |           dk    rddgfS dt	          j        |           gfS d| gfS )a.  
    Convert a Python value into its SQL-compatible string representation and parameters.

    Args:
        value: The value to format.

    Returns:
        Tuple containing:
            - A string for substitution into a SQL query.
            - A list of parameters to be bound into the query.
    r   z%sNzCAST(%s as JSON))
isinstancedictlistlenjsondumps)rE   s    r    format_value_sqlrM      sY     %$&& 7u::??$<!DJu$5$5#666%=r"   c                    dt           t                   dt           t                   fd}dt          dt          fd}i }t	          | j                  D ]\  }}|d         dk    r|||<   |||<   |                                 }g }|D ]O}t          |          }	t	          |          D ]\  }}
 ||         |
          |	|<   |                    |	           Pt          j
        || j                  S )a  
    Convert the results of a cursor's last executed query to a pandas DataFrame.

    Args:
        cursor: MySQLCursorAbstract with a completed query.

    Returns:
        DataFrame with data from the cursor.

    Raises:
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.
            If a compatible SELECT query wasn't the last statement ran
    elemr   c                 2    | t          j        |           nd S N)rK   loadsrO   s    r    _json_processorz+sql_response_to_df.<locals>._json_processor   s    #'#3tz$=r"   c                     | S rQ   r.   rS   s    r    _default_processorz.sql_response_to_df.<locals>._default_processor   s    r"         )columns)r	   strrH   r   	enumeratedescriptionfetchallrI   appendpd	DataFramecolumn_names)r'   rT   rV   idx_to_processoridxcolrowsprocessed_rowsrowprocessed_rowrO   s              r    sql_response_to_dfri      s2   ">hsm > > > > >      f011 7 7Sq6S==$3S!!$6S!!??D N - -S		"3 	= 	=IC!6!1#!6t!<!<M#m,,,,<0CDDDDr"   r(   r)   c                     t          |           t          |           t          | d| d|            t          |           S )aD  
    Load the entire contents of a SQL table into a pandas DataFrame.

    Args:
        cursor: MySQLCursorAbstract to execute the query.
        schema_name: Name of the schema containing the table.
        table_name: Name of the table to fetch.

    Returns:
        DataFrame containing all rows from the specified table.

    Raises:
        DatabaseError:
            If the table does not exist
            If a database connection issue occurs.
            If an operational error occurs during execution.
        ValueError: If the schema or table name is not valid
    zSELECT * FROM .)validate_namer0   ri   r'   r(   r)   s      r    sql_table_to_dfrn      sO    * +*CCCzCCDDDf%%%r"   c                     t          |           t          |           |                     d||f           |                                 duS )a  
    Check whether a table exists in a specific schema.

    Args:
        cursor: MySQLCursorAbstract object to execute the query.
        schema_name: Name of the database schema.
        table_name: Name of the table.

    Returns:
        True if the table exists, False otherwise.

    Raises:
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.
        ValueError: If the schema or table name is not valid
    z
        SELECT 1
        FROM information_schema.tables
        WHERE table_schema = %s AND table_name = %s
        LIMIT 1
        Nrl   r/   fetchonerm   s      r    table_existsrr     s\    ( +*
NN	 
j!   ??D((r"   c                 n    t          |           t          |           t          | d| d|            dS )a  
    Drop a table from the SQL database if it exists.

    Args:
        cursor: MySQLCursorAbstract to execute the drop command.
        schema_name: Name of the schema.
        table_name: Name of the table to delete.

    Returns:
        None

    Raises:
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.
        ValueError: If the schema or table name is not valid
    zDROP TABLE IF EXISTS rk   N)rl   r0   rm   s      r    r%   r%   6  sG    ( +*JJJjJJKKKKKr"   dfcol_name_to_placeholder_stringc                    |i }t          |           t          |           |j        D ]}t          t          |                     | d| }|j        D ]}g g }	}t	          ||j                  D ]\  }
}t          |
d          r|
                                n|
}
||v r||         t          |
          g}}nt          |
          \  }}|                    |           |		                    |           d
                    d |j        D                       }d
                    |          }d| d| d| d	}t          | ||	
           dS )a  
    Insert all rows from a pandas DataFrame into an existing SQL table.

    Args:
        cursor: MySQLCursorAbstract for execution.
        schema_name: Name of the database schema.
        table_name: Table to insert new rows into.
        df: DataFrame containing the rows to insert.
        col_name_to_placeholder_string:
            Optional mapping of column names to custom SQL value/placeholder
            strings.

    Returns:
        None

    Raises:
        DatabaseError:
            If the rows could not be inserted into the table, e.g., a type or shape issue
            If a database connection issue occurs.
            If an operational error occurs during execution.
        ValueError: If the schema or table name is not valid
    Nrk   item, c                 ,    g | ]}t          |          S r.   )rZ   .0rd   s     r    
<listcomp>z$extend_sql_table.<locals>.<listcomp>  s    ===3c#hh===r"   zINSERT INTO  (z
) VALUES ())r,   )rl   rY   rZ   valuesziphasattrrw   rM   r^   extendr6   r0   )r'   r(   r)   rt   ru   rd   qualified_table_namerg   placeholdersr,   rO   elem_placeholderelem_paramscols_sqlplaceholders_sql
insert_sqls                   r    extend_sql_tabler   P  s   : &-)+&+*z    c#hh)88J88 y 7 7!2fS"*-- 	' 	'ID#")$"7"7A499;;;TD4440Ns0SIIV+   1A0F0F- + 0111MM+&&&&99=="*===>>99\228/ 8 88 8$48 8 8 	 	FJv66666+7 7r"   c                     t           fd          } d| }t                     t          |           |j        D ]}t          t          |                     g }|j                                        D ]c\  }}t                              t          |          d          }t          t          |                     |                    | d|            dd	                    |          }	t          d |j        D                       }
|
r|	dz  }	d| d	|	 d
}t           |           	 t           ||           n # t          $ r t           |            w xY w||fS )a  
    Create a new SQL table with a random name, and populate it with data from a DataFrame.

    If an 'id' column is defined in the dataframe, it will be used as the primary key.

    Args:
        cursor: MySQLCursorAbstract for executing SQL.
        schema_name: Schema in which to create the table.
        df: DataFrame containing the data to be inserted.

    Returns:
        Tuple (qualified_table_name, table_name): The schema-qualified and
        unqualified table names.

    Raises:
        RuntimeError: If a random available table name could not be found.
        ValueError: If any schema, table, or a column name is invalid.
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.
    c                 (    t          |            S rQ   )rr   )r)   r'   r(   s    r    <lambda>z#sql_table_from_df.<locals>.<lambda>  s    |FKLLL r"   rk   r    rx   c              3   F   K   | ]}|                                 d k    V  dS )idN)lowerrz   s     r    	<genexpr>z$sql_table_from_df.<locals>.<genexpr>  s/      ??SSYY[[D(??????r"   z, PRIMARY KEY (id)zCREATE TABLE r}   r~   )rD   rl   rY   rZ   dtypesitemsPD_TO_SQL_DTYPE_MAPPINGgetr^   r6   anyr0   r   	Exceptionr%   )r'   r(   rt   r)   r   rd   columns_sqldtypesql_typecolumns_str
has_id_colcreate_table_sqls   ``          r    sql_table_from_dfr     s   0 !LLLLL J *88J88+*z    c#hhKioo'' 0 0
U*..s5zz:FFc#hhc..H..////))K((K??BJ?????J ,++ N';MM{MMM()))j"====   j999  ++s   4E E$rC   c                 ~    t          | t                    rt          j        d|           st	          d|            | S )a  
    Validate that the string is a legal SQL identifier (letters, digits, underscores).

    Args:
        name: Name (schema, table, or column) to validate.

    Returns:
        The validated name.

    Raises:
        ValueError: If the name does not meet format requirements.
    z^[A-Za-z0-9_]+$zUnsupported name format )rG   rZ   rematch
ValueError)rC   s    r    rl   rl     sF     tS!! <bh/A4&H&H <:D::;;;Kr"   c                     | j         }|Ct          }t          |           5 }d| }t          ||           ddd           n# 1 swxY w Y   t	          |           |S )a  
    Retrieve the name of the currently selected schema, or set and ensure the default schema.

    Args:
        db_connection: MySQL connector database connection object.

    Returns:
        Name of the schema (database in use).

    Raises:
        ValueError: If the schema name is not valid
        DatabaseError:
            If a database connection issue occurs.
            If an operational error occurs during execution.
    NzCREATE DATABASE IF NOT EXISTS )databaseDEFAULT_SCHEMAr   r0   rl   )r#   schemar'   create_database_stmts       r    source_schemar     s      #F~.. 	6&#LF#L#L  4555	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 &Ms   AA	Ac                     t          |           t          |           |                     d| d| d           |                                 du S )a+  
    Determine if a given SQL table is empty.

    Args:
        cursor: MySQLCursorAbstract with access to the database.
        schema_name: Name of the schema containing the table.
        table_name: Name of the table to check.

    Returns:
        True if the table has no rows, False otherwise.

    Raises:
        DatabaseError:
            If the table does not exist
            If a database connection issue occurs.
            If an operational error occurs during execution.
        ValueError: If the schema or table name is not valid
    zSELECT 1 FROM rk   z LIMIT 1Nrp   rm   s      r    is_table_emptyr     s[    * +*
NNFKFF*FFFGGG??$$r"   featurearr
col_prefixc                    | dS t          | t          j                  rt          j        |           S t          | t          j                  r|                                 S | j        dk    r|                     dd          } fdt          | j        d                   D             }t          j        | |d          S )a$  
    Convert input data to a pandas DataFrame if necessary.

    Args:
        arr: Input data as a pandas DataFrame, NumPy ndarray, pandas Series, or None.

    Returns:
        If the input is None, returns None.
        Otherwise, returns a DataFrame backed by the same underlying data whenever
        possible (except in cases where pandas or NumPy must copy, such as for
        certain views or non-contiguous arrays).

    Notes:
        - If an ndarray is passed, column names will be integer indices (0, 1, ...).
        - If a DataFrame is passed, column names and indices are preserved.
        - The returned DataFrame is a shallow copy and shares data with the original
          input when possible; however, copies may still occur for certain input
          types or memory layouts.
    NrW   c                     g | ]	} d | 
S )rB   r.   )r{   rc   r   s     r    r|   z!convert_to_df.<locals>.<listcomp>;  s'    FFF3J&&&&FFFr"   F)rY   r   )	rG   r_   r`   Seriesto_framendimreshaper@   shape)r   r   	col_namess    ` r    convert_to_dfr     s    . {t#r|$$ !|C   #ry!! ||~~
x1}}kk"a  FFFF%	!2E2EFFFI<YU;;;;r"   rQ   )r<   )r   ):__doc__r   rK   r7   r   r4   
contextlibr   typingr   r   r   r   r   r	   r
   r   numpynppandasr_   mysql.ai.utils.atomic_cursorr   mysql.connector.abstractsr   mysql.connector.cursorr   mysql.connector.typesr   VAR_NAME_SPACEr9   r   r   rH   r!   rI   tuplerZ   r*   r0   r;   r   intrD   rM   r`   ri   rn   rr   r%   r   r   rl   r   r   r   ndarrayr   r.   r"   r    <module>r      s  8	 	    				  % % % % % % N N N N N N N N N N N N N N N N N N N N         ; ; ; ; ; ; = = = = = = 6 6 6 6 6 6 : : : : : :      
"x~ "$ " " " "  B*Bd5c?#$B B B B> QU( (((+(5M(	( ( ( (0I3 I I I IW Wxt4 W Ws W W W W0C E#tCy.$9    &+E2 +Er| +E +E +E +E\&&.1&?B&\& & & &> ) ).1 )?B )	 )  )  )  )FLL.1L?BL	L L L L> 6:=7 =7=7=7 =7 		=7
 %)cN=7 
=7 =7 =7 =7@:,:,.1:,79|:,
38_:, :, :, :,z     (!8 S    :%%.1%?B%	% % % %<  #< #<	%bi;<	=#<#< bl#< #< #< #< #< #<r"   