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| rz| 	  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|rz|  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 s0    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.