
    $j/                         d Z ddlZddlmZmZ ddlmZ ddlmZ ddl	m
Z
mZ ddlmZ e
rdd	lmZ  G d
 de          Z G d de          ZdS )zAbstract base classes for database backend abstraction.

Defines the interfaces that all database backends (PostgreSQL, Oracle, etc.)
must implement. Business logic depends only on these interfaces.
    N)ABCabstractmethod)AsyncIterator)asynccontextmanager)TYPE_CHECKINGAny   )	ResultRow)DataAccessOpsc                   h   e Zd ZdZedefd            ZdedefdZdddded	e	e         d
e	e	         de	e         dz  dedz  de	e
         ez  fdZeeded          fd                        Zedddedededz  defd            Zedddede	eedf                  dedz  ddfd            Zedddedededz  de	e
         fd            Zedddedededz  de
dz  fd            Zeddddededededz  def
d            Zdddede	eedf                  d	e	e         dedz  ddf
dZdS )DatabaseConnectionzWraps a single connection from the pool.

    Provides a uniform interface over asyncpg.Connection, oracledb cursor, etc.
    Methods mirror asyncpg's connection API for minimal migration friction.
    returnc                     dS z(Return ``"postgresql"`` or ``"oracle"``.
postgresql selfs    d/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/db/base.pybackend_typezDatabaseConnection.backend_type   	     |    valuec                     |dS t          |t                    r3	 t          j        |          S # t          j        t
          f$ r |cY S w xY w|S )a  Parse a JSON column value into a Python object.

        PG (asyncpg) returns JSON columns as strings that need json.loads().
        Oracle returns them as pre-parsed dicts/lists (via OracleConnection
        row conversion). This method normalizes both to Python objects.
        N)
isinstancestrjsonloadsJSONDecodeError	TypeError)r   r   s     r   
parse_jsonzDatabaseConnection.parse_json#   sh     =4eS!! 	z%(((()4    s   / A
	A
N)column_types	returningtablecolumnsarraysr"   r#   c                <  K   d                     |          }t          |          }|pdg|z  d                     fdt          |          D                       }d| d| d| d}	|r|	d| z  }	 | j        |	g|R   d	{V S  | j        |	g|R   d	{V }
|
S )
a  Insert multiple rows from parallel arrays.

        Default implementation uses ``INSERT ... SELECT * FROM unnest(...)``
        (PostgreSQL).  Oracle overrides this with ``executemany``.

        Args:
            table: Fully-qualified table name.
            columns: Column names matching the arrays.
            arrays: Parallel lists of values, one per column.
            column_types: PG type suffixes for unnest casting (e.g. ``["text[]", "uuid[]"]``).
                          Ignored by backends that don't use unnest.
            returning: Optional column expression for a RETURNING clause.

        Returns:
            If *returning* is set, a list of ResultRow; otherwise a status string.
        , ztext[]c              3   :   K   | ]}d |dz    d|          V  dS )$r	   z::Nr   ).0itypess     r   	<genexpr>z=DatabaseConnection.bulk_insert_from_arrays.<locals>.<genexpr>Q   s<      OOA 7AE 7 7U1X 7 7OOOOOOr   INSERT INTO  (z) SELECT * FROM unnest()z RETURNING N)joinlenrangefetchexecute)r   r$   r%   r&   r"   r#   col_listn_colsunnest_argsqueryresultr-   s              @r   bulk_insert_from_arraysz*DatabaseConnection.bulk_insert_from_arrays4   s      4 99W%%W3
V 3iiOOOOvOOOOOWuWWWWWWW 	4.9...E#E3F3333333333#t|E3F333333333r   c                   K   	 dW V  dS )u   Start a transaction (or savepoint if already in a transaction).

        Yields:
            Self — the same connection, now inside a transaction scope.
            On clean exit the transaction is committed; on exception it is rolled back.
        Nr   r   s    r   transactionzDatabaseConnection.transactionY   s       	r   )timeoutr:   argsr?   c                
   K   dS )a;  Execute a query and return a status string (e.g. 'INSERT 0 1').

        Args:
            query: SQL query with dialect-appropriate placeholders.
            *args: Positional bind parameters.
            timeout: Optional statement timeout in seconds.

        Returns:
            Command status string.
        Nr   r   r:   r?   r@   s       r   r6   zDatabaseConnection.executee          	r   .c                
   K   dS )a   Execute a query for each set of arguments.

        Args:
            query: SQL query with dialect-appropriate placeholders.
            args: List of argument tuples, one per execution.
            timeout: Optional statement timeout in seconds.
        Nr   )r   r:   r@   r?   s       r   executemanyzDatabaseConnection.executemanys   s       	r   c                
   K   dS )a$  Execute a query and return all rows.

        Args:
            query: SQL query with dialect-appropriate placeholders.
            *args: Positional bind parameters.
            timeout: Optional statement timeout in seconds.

        Returns:
            List of ResultRow objects.
        Nr   rB   s       r   r5   zDatabaseConnection.fetch~   rC   r   c                
   K   dS )aE  Execute a query and return a single row (or None).

        Args:
            query: SQL query with dialect-appropriate placeholders.
            *args: Positional bind parameters.
            timeout: Optional statement timeout in seconds.

        Returns:
            A single ResultRow, or None if no rows match.
        Nr   rB   s       r   fetchrowzDatabaseConnection.fetchrow   rC   r   r   )columnr?   rI   c                
   K   dS )a  Execute a query and return a single value from the first row.

        Args:
            query: SQL query with dialect-appropriate placeholders.
            *args: Positional bind parameters.
            column: Column index to return (default 0).
            timeout: Optional statement timeout in seconds.

        Returns:
            The value from the specified column of the first row, or None.
        Nr   )r   r:   rI   r?   r@   s        r   fetchvalzDatabaseConnection.fetchval   s       	r   
table_namerecordsc                  K   d                     |          }d                     d t          t          |                    D                       }d| d| d| d}|                     |t	          |                     d{V  dS )zBulk-load records into a table.

        Default implementation uses executemany INSERT. Backends with native
        bulk-load support (e.g. asyncpg COPY) should override for performance.
        r(   c              3   &   K   | ]}d |dz    V  dS )r*   r	   Nr   )r+   r,   s     r   r.   z;DatabaseConnection.copy_records_to_table.<locals>.<genexpr>   s,       J JQU J J J J J Jr   r/   r0   z
) VALUES (r1   N)r2   r4   r3   rE   list)r   rL   rM   r%   r?   colsplaceholdersr:   s           r   copy_records_to_tablez(DatabaseConnection.copy_records_to_table   s       yy!!yy J JeCLL6I6I J J JJJLzLLTLL\LLLud7mm44444444444r   )__name__
__module____qualname____doc__propertyr   r   r   r!   rP   r
   r<   r   r   r   r>   floatr6   tuplerE   r5   rH   intrK   rS   r   r   r   r   r      s3         c    X     . *. $# # ## c# T
	# 3i$&# :# 
i3	# # # #J =1E#F     ^ LP   3 s UT\ UX    ^ dh   s $uS#X2G UZ]aUa mq    ^ JN    S 54< SWXaSb    ^ MQ   C  edl V_bfVf    ^ CD^b   C  S uW[| gj    ^( !%5 5 55 eCHo&	5
 c5 5 
5 5 5 5 5 5r   r   c                   ,   e Zd ZU dZdZded<   edefd            Zed(d            Z	ede
fd	            Zede
fd
            Zede
fd            Zede
fd            Zede
fd            Zdedz  dedz  fdZdddededz  ddfdZddddededefdZeddddddddedededed ed!ed"edz  ddfd#            Zed)d$            Zeedee         fd%                        Zeedee         fd&                        Zedefd'            ZdS )*DatabaseBackendu  Database pool lifecycle and connection acquisition.

    Manages the connection pool and provides context managers for
    acquiring connections and running transactions.

    The ``ops`` property provides backend-specific data access operations
    (the Strategy pattern — like Django's ``connection.ops``). All business
    logic should use ``backend.ops`` instead of creating DataAccessOps
    instances directly.
    NzDataAccessOps | None_ops_instancer   c                     dS r   r   r   s    r   r   zDatabaseBackend.backend_type   r   r   r   c                 T    | j         ddlm}  || j                  | _         | j         S )a  Backend-specific data access operations (cached).

        Follows the Django pattern: ``connection.ops`` provides the
        operations handler for the current backend. Created lazily on
        first access and cached for the lifetime of the backend.
        Nr	   )create_data_access_ops)r^    ra   r   )r   ra   s     r   opszDatabaseBackend.ops   s>     %000000!7!78I!J!JD!!r   c                     dS )u'   Can CREATE INDEX … WHERE <predicate>.Tr   r   s    r   supports_partial_indexesz(DatabaseBackend.supports_partial_indexes   	     tr   c                     dS )z%Has BM25 / tsvector full-text search.Tr   r   s    r   supports_bm25zDatabaseBackend.supports_bm25   rf   r   c                     dS )z5Supports ``unnest()`` for expanding arrays into rows.Tr   r   s    r   supports_unnestzDatabaseBackend.supports_unnest   rf   r   c                     dS )zAPlatform *might* have pg_trgm (must still be checked at runtime).Tr   r   s    r   supports_pg_trgmz DatabaseBackend.supports_pg_trgm   rf   r   c                     dS )zWhether this backend supports the async WorkerPoller.

        WorkerPoller is backend-agnostic (uses DatabaseBackend.acquire()).
        All current backends (PostgreSQL, Oracle) support it.
        Tr   r   s    r   supports_worker_pollerz&DatabaseBackend.supports_worker_poller   s	     tr   schemac                     |S )zNormalize a schema name for this backend.

        Returns the schema as-is by default. Oracle overrides this to
        convert ``"public"`` (a PG-specific default) to ``None`` (use the
        connecting user's default schema).
        r   )r   ro   s     r   normalize_schemaz DatabaseBackend.normalize_schema   s	     r   )ro   dsnc                J    t          t          |           j         d          )zRun database migrations for this backend.

        PG uses Alembic migrations. Oracle uses its own idempotent DDL runner.
        Subclasses must override this method.
        z  must implement run_migrations())NotImplementedErrortyperT   )r   rr   ro   s      r   run_migrationszDatabaseBackend.run_migrations  s$     "T$ZZ%8"Z"Z"Z[[[r   pool_getterschema_getterrx   ry   c                (    ddl m}  |||          S )zCreate the task backend for this database.

        All backends use BrokerTaskBackend for async worker/poller execution.
           )BrokerTaskBackendrw   )task_backendr|   )r   rx   ry   r|   s       r   create_task_backendz#DatabaseBackend.create_task_backend  s,    
 	544444  [VVVVr         i,     r   )min_sizemax_sizecommand_timeoutacquire_timeoutstatement_cache_sizeinit_callbackr   r   r   r   r   r   c                
   K   dS )a  Create the connection pool.

        Args:
            dsn: Database connection string.
            min_size: Minimum number of connections in the pool.
            max_size: Maximum number of connections in the pool.
            command_timeout: Default command timeout in seconds.
            acquire_timeout: Timeout for acquiring a connection from the pool.
            statement_cache_size: Size of the prepared-statement cache (0 to disable).
            init_callback: Optional async callback invoked on each new connection.
        Nr   )r   rr   r   r   r   r   r   r   s           r   
initializezDatabaseBackend.initialize  s      . 	r   c                 
   K   dS )z4Close the connection pool and release all resources.Nr   r   s    r   shutdownzDatabaseBackend.shutdown1  s       	r   c                   K   	 dW V  dS )zgAcquire a connection from the pool.

        Yields:
            A DatabaseConnection wrapper.
        Nr   r   s    r   acquirezDatabaseBackend.acquire6  s       	r   c                   K   	 dW V  dS )zAcquire a connection and start a transaction.

        The transaction is committed on clean exit, rolled back on exception.

        Yields:
            A DatabaseConnection wrapper inside a transaction.
        Nr   r   s    r   r>   zDatabaseBackend.transactionA  s       	r   c                     dS )u   Return the underlying raw pool object.

        Escape hatch for gradual migration — callers that still need direct
        pool access (e.g. asyncpg-specific features) can use this during
        the transition period.
        Nr   r   s    r   get_poolzDatabaseBackend.get_poolN  s	     	r   )r   r   )r   N)rT   rU   rV   rW   r^   __annotations__rX   r   r   rc   boolre   rh   rj   rl   rn   rq   rv   r   r~   r   r[   rY   r   r   r   r   r   r   r>   r   r   r   r   r]   r]      s:        	 	 -1M)000 c    X " " " X" $    X t    X     X $    X     XsTz cDj     @D \ \ \# \#* \ \ \ \ \ 9=SW W W W# WS W\_ W W W W 
 !$!#$%$(   	
    " Tz 
   ^0    ^ }-?@     ^ 	=1C#D 	 	 	  ^	 #    ^  r   r]   )rW   r   abcr   r   collections.abcr   
contextlibr   typingr   r   r;   r
   rc   r   r   r]   r   r   r   <module>r      s     # # # # # # # # ) ) ) ) ) ) * * * * * *
 & % % % % % % %       #""""""b5 b5 b5 b5 b5 b5 b5 b5JZ Z Z Z Zc Z Z Z Z Zr   