a
    %ßiþ!  ã                   @   sö   d Z ddlmZ ddlmZmZ ddlZddlZddlm	Z	 ddl
Z
ddlmZ e
j deeeƒjjƒ¡ zddlmZ W n ey”   dd	„ ZY n0 edeejj ee d
œdd„ƒZdeejj dœdd„Zeeedœdd„Zeedœdd„ZdS )aÉ  
Database transaction management with row-level locking support.

Provides context manager for safe database transactions with:
- Automatic commit on success
- Automatic rollback on error
- Row-level locking (FOR UPDATE)
- Proper connection management
- Nested transaction handling

Usage:
    from database import transaction

    # Simple transaction
    with transaction() as (conn, cursor):
        cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
        cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
        # Auto-commits on success, auto-rolls back on exception

    # Transaction with row-level locking (use FOR UPDATE in queries)
    with transaction() as (conn, cursor):
        cursor.execute("SELECT balance FROM accounts WHERE id = 1 FOR UPDATE")
        balance = cursor.fetchone()[0]
        if balance >= 100:
            cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
é    )Úcontextmanager)ÚTupleÚOptionalN)ÚError)ÚPath)Úget_database_connectionc                  C   s,   ddl m }  tjj| j| j| j| j| jd�S )zFallback connection getterr   ©Úconfig)ÚhostÚportÚuserÚpasswordÚdatabase)	r	   ÚmysqlÚ	connectorÚconnectÚDB_HOSTÚDB_PORTÚDB_USERÚDB_PASSWORDÚDB_NAMEr   © r   ú6/var/www/lichun.app/lichun/ws/database/transactions.pyr   +   s    ûr   )ÚconnÚisolation_levelc                 c   s,  | du }d}�z|zp|rt ƒ } d| _|  ¡ }|rdg d¢}| ¡ |vrPtd|› �ƒ‚| d| ¡ › �¡ |  ¡  | |fV  |  ¡  W nl tyì } zT| rÖz|  	¡  W n4 tyÔ } zt
 d|› �¡ W Y d}~n
d}~0 0 ‚ W Y d}~n
d}~0 0 W |�r:z| ¡  W n6 t�y8 } zt
 d|› �¡ W Y d}~n
d}~0 0 |�r(| �r(z|  ¡  W n6 t�yˆ } zt
 d|› �¡ W Y d}~n
d}~0 0 nœ|�rÖz| ¡  W n6 t�yÔ } zt
 d|› �¡ W Y d}~n
d}~0 0 |�r&| �r&z|  ¡  W n6 t�y$ } zt
 d|› �¡ W Y d}~n
d}~0 0 0 dS )	a,  
    Context manager for database transactions with automatic commit/rollback.

    Args:
        conn: Optional existing connection. If None, gets from pool.
        isolation_level: Optional transaction isolation level
                       ('READ UNCOMMITTED', 'READ COMMITTED',
                        'REPEATABLE READ', 'SERIALIZABLE')

    Yields:
        Tuple of (connection, cursor)

    Raises:
        Exception: Any exception from transaction operations

    Note:
        For row-level locking, use FOR UPDATE in your SELECT queries:
        cursor.execute("SELECT * FROM table WHERE id = %s FOR UPDATE", (id,))

    Example:
        # Simple transaction
        with transaction() as (conn, cursor):
            cursor.execute("INSERT INTO users (name) VALUES (%s)", ('Alice',))
            cursor.execute("INSERT INTO profiles (user_id) VALUES (LAST_INSERT_ID())")

        # Transaction with row-level locking (use FOR UPDATE in queries)
        with transaction() as (conn, cursor):
            cursor.execute("SELECT balance FROM accounts WHERE id = %s FOR UPDATE", (1,))
            balance = cursor.fetchone()[0]
            if balance >= 100:
                cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = %s", (1,))

        # Transaction with custom isolation level
        with transaction(isolation_level='SERIALIZABLE') as (conn, cursor):
            cursor.execute("SELECT COUNT(*) FROM orders")
            count = cursor.fetchone()[0]
            cursor.execute("INSERT INTO orders (order_num) VALUES (%s)", (count + 1,))
    NF)zREAD UNCOMMITTEDzREAD COMMITTEDzREPEATABLE READÚSERIALIZABLEzInvalid isolation level: z SET TRANSACTION ISOLATION LEVEL zError during rollback: zError closing cursor: zError closing connection: )r   Ú
autocommitÚcursorÚupperÚ
ValueErrorÚexecuteÚstart_transactionÚcommitÚ	ExceptionÚrollbackÚloggingÚerrorÚclose)r   r   Zcreated_connectionr   Zvalid_levelsÚeZrollback_errorr   r   r   Útransaction7   sT    *
&&(ô&r)   ©r   c                 C   s<   t |d��\}}| ||ƒW  d  ƒ S 1 s.0    Y  dS )aO  
    Execute a function within a transaction context.

    Helper function for wrapping operations in a transaction.

    Args:
        func: Function that takes (conn, cursor) as arguments
        conn: Optional existing connection

    Returns:
        Return value of func

    Example:
        def transfer_money(conn, cursor):
            cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
            cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
            return True

        result = execute_in_transaction(transfer_money)
    r*   N)r)   )Úfuncr   r   r   r   r   Úexecute_in_transaction›   s    r,   )Úfrom_account_idÚto_account_idÚamountc                 C   s¬   t ƒ �’\}}| d| f¡ | ¡ }|r2|d |k r:tdƒ‚| d|f¡ | ¡ sXtdƒ‚| d|| f¡ | d||f¡ | d| ||f¡ W d	  ƒ n1 sž0    Y  d	S )
zÖ
    Example: Atomic money transfer between accounts.

    This example demonstrates:
    - Row-level locking with FOR UPDATE
    - Balance validation before update
    - Atomic operations across multiple rows
    z5SELECT balance FROM accounts WHERE id = %s FOR UPDATEr   zInsufficient fundsz0SELECT id FROM accounts WHERE id = %s FOR UPDATEzDestination account not foundz8UPDATE accounts SET balance = balance - %s WHERE id = %sz8UPDATE accounts SET balance = balance + %s WHERE id = %szsINSERT INTO transaction_log (from_account, to_account, amount, timestamp)
               VALUES (%s, %s, %s, NOW())N)r)   r    Úfetchoner   )r-   r.   r/   r   r   Zfrom_balancer   r   r   Úexample_atomic_transferµ   s4    	þþþþýr1   )Úoperation_idÚuser_idc                 C   sr   t ƒ �X\}}| d| f¡ | ¡ r2W d  ƒ dS | d|f¡ | d| f¡ W d  ƒ dS 1 sd0    Y  dS )z¯
    Example: Idempotent operation using database lock.

    Prevents duplicate execution of operations by checking
    a processed operations log with row-level locking.
    zFSELECT id FROM processed_operations WHERE operation_id = %s FOR UPDATENFziINSERT INTO user_credits (user_id, amount) VALUES (%s, 100) ON DUPLICATE KEY UPDATE amount = amount + 100zPINSERT INTO processed_operations (operation_id, processed_at) VALUES (%s, NOW())T)r)   r    r0   )r2   r3   r   r   r   r   r   Úexample_idempotent_operationã   s     þýþr4   )NN)N)Ú__doc__Ú
contextlibr   Útypingr   r   r%   Úmysql.connectorr   r   ÚsysÚpathlibr   ÚpathÚinsertÚstrÚ__file__ÚparentÚ	functionsr   ÚImportErrorr   ÚMySQLConnectionr)   r,   ÚintÚfloatr1   r4   r   r   r   r   Ú<module>   s*     ÿÿc.