
    $ja1                         d Z ddl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 G d	 d
                      Z G d de          ZdS )u:  Abstract base class for backend-specific data access operations.

SQLDialect handles SQL *fragment* generation (param placeholders, JSON ops, vector
distance, etc.) — stateless, no I/O.

DataAccessOps handles multi-statement *execution* patterns that differ between
backends (unnest batch insert vs executemany, LATERAL fan-out vs per-row query,
DISTINCT ON vs GROUP BY workarounds, etc.).  Methods receive a DatabaseConnection
and execute complete operations.

This eliminates scattered ``if backend_type == "postgresql"`` conditionals from
business logic.  Adding a new backend (e.g. Neon, Databricks) means implementing
this ABC — consumer code never checks the backend directly.

Follows the Strategy pattern (Fowler's "Replace Conditional with Polymorphism")
and mirrors Django's ``DatabaseOperations`` architecture.
    )ABCabstractmethod)	dataclass)Any)UUID   )DatabaseConnection)	ResultRowc                   <    e Zd ZU dZeed<   eed<   eed<   eed<   dS )TagListingPartsz9Backend-specific SQL fragments for the tag listing query.
tag_sourcenon_empty_checktag_colbank_prefixN)__name__
__module____qualname____doc__str__annotations__     c/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/db/ops.pyr   r      sB         CCOOOLLLr   r   c            '          e Zd ZdZedefd            Zedede	de
e	         de
e	         de
e	         d	e
e	         d
e
e         de
e	         ddfd            Ze	 dbdede	de
e	         de
e	         de
de
de
de
de
e	         de
e	         de
e	         de
de
de
e	         de
de
de	de
e	         f$d            Ze	 dcdede	de
e         de	d e	d!e	d"eddfd#            Zedede	de	d$e
e	         d%e
dee	e	f         fd&            Zedede	de	d'e
e	         de
e         f
d(            Zedede	d)e
d*e
ddf
d+            Zeded,e	d-e
e         d.ede
e         f
d/            Zeded0e	d)e
e	         de
e         fd1            Ze	 ddded0e	de	d3e
d4e
d5e
d6ed7ede
e         fd8            Zed0e	d,e	d9ede	fd:            Zed;e	d0e	de	fd<            Zeded0e	d,e	d;e	d=e
d>ed9edee
e         e
e         e
e         f         fd?            Zed0e	defd@            Zedede	de	dAe	dBe	dee	e	f         ddfdC            ZededDe	dAe	dee	e	f         ddf
dE            Zede	fdF            Z edede	dGe!de	dHe	dIe	dz  dJe
e	         dKedLe	dedz  fdM            Z"edede	de	de
e         fdN            Z#ededOe	de	de
e         fdP            Z$edede	dGe!de	dQe
e	         dRe
e!         dedz  fdS            Z%edede	dGe!de	def
dT            Z&ededUe	dGe	de	dVedWe	dz  de
e         fdX            Z'ededUe	dYe!de	dZe	d[e!ddfd\            Z(edede	d]e	d^ee	ef         d_ede
e         fd`            Z)de	fdaZ*dS )eDataAccessOpszBackend-specific multi-statement data access operations.

    Each method encapsulates a complete DB operation that differs
    in execution strategy between backends.
    returnc                     dS )zWhether this backend uses the observation_sources junction table.

        PG uses native array ops (source_memory_ids column) for reads and
        skips junction table writes.  Oracle uses the junction table for both.
        Tr   selfs    r   uses_observation_sources_tablez,DataAccessOps.uses_observation_sources_table-   s	     tr   conntable	chunk_idsdocument_idsbank_idschunk_textschunk_indicescontent_hashesNc	                 
   K   dS )zBulk upsert chunks with ON CONFLICT handling.

        PG uses INSERT ... SELECT FROM unnest() with ON CONFLICT DO UPDATE.
        Non-PG uses bulk_insert_from_arrays (executemany).
        Nr   )	r   r!   r"   r#   r$   r%   r&   r'   r(   s	            r   bulk_upsert_chunksz DataAccessOps.bulk_upsert_chunks8         " 	r   nativebank_id
fact_texts
embeddingsevent_datesoccurred_startsoccurred_endsmentioned_atscontexts
fact_typesmetadata_jsons	tags_listobservation_scopes_listtext_signals_listtext_search_extensionc                 
   K   dS )zBatch-insert facts, returning IDs.

        PG uses INSERT ... SELECT FROM unnest() with RETURNING.
        Non-PG inserts row-by-row with individual RETURNING.
        Nr   )r   r!   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r#   r$   r7   r8   r9   r:   s                     r   insert_facts_batchz DataAccessOps.insert_facts_batchK   s      4 	r     sorted_linksnil_entity_uuidexists_clause
chunk_sizec                 
   K   dS )zBulk insert memory_links with conflict handling.

        PG uses INSERT ... SELECT FROM unnest() with chunking.
        Non-PG uses executemany.
        Nr   )r   r!   r"   r>   r-   r?   r@   rA   s           r   bulk_insert_linkszDataAccessOps.bulk_insert_linksg           	r   entity_namesentity_datesc                 
   K   dS )zBulk insert entities with ON CONFLICT DO NOTHING, returning id-by-lowercase-name.

        PG uses INSERT ... SELECT FROM unnest() with RETURNING.
        Non-PG inserts row-by-row then SELECTs.
        Nr   )r   r!   r"   r-   rE   rF   s         r   bulk_insert_entitiesz"DataAccessOps.bulk_insert_entitiesy          	r   missing_namesc                 
   K   dS )zFetch entity IDs for names that conflicted during insert.

        PG uses unnest + JOIN.
        Non-PG queries each name individually.
        Nr   )r   r!   r"   r-   rJ   s        r   fetch_missing_entity_idsz&DataAccessOps.fetch_missing_entity_ids          	r   unit_ids
entity_idsc                 
   K   dS )zBulk insert unit_entities links with ON CONFLICT DO NOTHING.

        PG uses INSERT ... SELECT FROM unnest().
        Non-PG uses executemany.
        Nr   )r   r!   r"   rN   rO   s        r   bulk_insert_unit_entitiesz'DataAccessOps.bulk_insert_unit_entities   rM   r   ue_tableentity_id_listlimit_per_entityc                 
   K   dS )zFetch unit_ids for a list of entities with per-entity row cap.

        PG uses unnest + CROSS JOIN LATERAL with LIMIT.
        Non-PG queries each entity individually.
        Nr   )r   r!   rR   rS   rT   s        r   fetch_entity_unit_fanoutz&DataAccessOps.fetch_entity_unit_fanout   rM   r   mu_tablec                 
   K   dS )zFetch event_date/fact_type for a list of unit IDs.

        PG uses ANY($1) array binding.
        Non-PG queries each unit individually.
        Nr   )r   r!   rW   rN   s       r   fetch_unit_dateszDataAccessOps.fetch_unit_dates   s       	r     lateral_unit_idslateral_event_dateslateral_fact_types
half_limit
batch_sizec	                 
   K   dS )zFetch temporal neighbors using bidirectional index scan.

        PG uses unnest + CROSS JOIN LATERAL for batched bidirectional scan.
        Non-PG queries each unit individually with backward/forward scans.
        Nr   )	r   r!   rW   r-   r[   r\   r]   r^   r_   s	            r   fetch_temporal_neighborsz&DataAccessOps.fetch_temporal_neighbors   r+   r   per_entity_limitc                     dS )zBuild entity expansion CTE for link expansion retrieval.

        PG uses DISTINCT ON with CROSS JOIN LATERAL and GROUP BY.
        Non-PG splits into entity_scores subquery then JOINs for full columns
        (can't GROUP BY CLOB).
        Nr   )r   rW   rR   rb   s       r   build_entity_expansion_ctez(DataAccessOps.build_entity_expansion_cte   s	     	r   ml_tablec                     dS )zBuild semantic + causal expansion CTEs.

        PG uses DISTINCT ON for deduplication.
        Non-PG computes MAX(weight) in subquery then JOINs for full columns.
        Nr   )r   re   rW   s      r   build_semantic_causal_ctez'DataAccessOps.build_semantic_causal_cte   s	     	r   seed_idsbudgetc                 
   K   dS )zObservation-specific graph expansion.

        PG uses native array ops (source_memory_ids column) for performance.
        Oracle uses the observation_sources junction table.
        Nr   )r   r!   rW   rR   re   rh   ri   rb   s           r   expand_observationsz!DataAccessOps.expand_observations   rD   r   c                     dS )zBuild SQL fragments for the tag listing query.

        PG uses unnest(tags) to expand the VARCHAR[] column.
        Non-PG uses CROSS APPLY JSON_TABLE on the CLOB column.
        Nr   )r   rW   s     r   build_tag_listing_partsz%DataAccessOps.build_tag_listing_parts  	     	r   internal_idindex_clausec                 
   K   dS )zCreate per-bank partial vector indexes.

        PG creates per-(bank, fact_type) partial indexes.
        Non-PG is a no-op (uses global index).
        Nr   )r   r!   r"   r-   ro   rp   r5   s          r   create_bank_vector_indexesz(DataAccessOps.create_bank_vector_indexes  s       	r   schemac                 
   K   dS )zzDrop per-bank partial vector indexes.

        PG drops per-(bank, fact_type) indexes.
        Non-PG is a no-op.
        Nr   )r   r!   rs   ro   r5   s        r   drop_bank_vector_indexesz&DataAccessOps.drop_bank_vector_indexes'  rM   r   c                     dS )zReturn the fuzzy entity matching strategy name.

        PG uses "trigram" (pg_trgm).
        Non-PG uses "oracle_fuzzy" (UTL_MATCH) or falls back to "full".
        Nr   r   s    r   get_entity_resolution_strategyz,DataAccessOps.get_entity_resolution_strategy8  rn   r   
webhook_idurlsecretevent_typesenabledhttp_config_jsonc
                 
   K   dS )z0Insert a webhook row and return the created row.Nr   )
r   r!   r"   rx   r-   ry   rz   r{   r|   r}   s
             r   create_webhookzDataAccessOps.create_webhookC  rI   r   c                 
   K   dS )z4List all webhooks for a bank, ordered by created_at.Nr   )r   r!   r"   r-   s       r   list_webhooks_for_bankz$DataAccessOps.list_webhooks_for_bankS         	r   webhook_tablec                 
   K   dS )zHGet enabled webhooks matching a bank (bank-specific + global NULL rows).Nr   )r   r!   r   r-   s       r   get_webhooks_for_dispatchz'DataAccessOps.get_webhooks_for_dispatch]  r   r   set_clausesparamsc                 
   K   dS )zBUpdate a webhook and return the updated row, or None if not found.Nr   )r   r!   r"   rx   r-   r   r   s          r   update_webhookzDataAccessOps.update_webhookg         	r   c                 
   K   dS )z4Delete a webhook. Returns True if a row was deleted.Nr   )r   r!   r"   rx   r-   s        r   delete_webhookzDataAccessOps.delete_webhookt  s       	r   	ops_tablelimitcursorc                 
   K   dS )zFList webhook delivery operations for a specific webhook, newest first.Nr   )r   r!   r   rx   r-   r   r   s          r   list_webhook_deliveriesz%DataAccessOps.list_webhook_deliveries  r   r   operation_idpayload_json	timestampc                 
   K   dS )z5Insert a webhook delivery task into async_operations.Nr   )r   r!   r   r   r-   r   r   s          r   insert_webhook_delivery_taskz*DataAccessOps.insert_webhook_delivery_task  r   r   	worker_idreserved_limitsshared_limitc                 
   K   dS )a  Claim pending tasks from the async_operations table.

        PG implementation can use NOT EXISTS + FOR UPDATE SKIP LOCKED in one query.
        Oracle implementation uses two-step claims (query busy banks first, then
        claim excluding them) to avoid ORA-02014.

        Returns claimed rows with operation_id, operation_type, task_payload, retry_count.
        The caller is responsible for building ClaimedTask objects.
        Nr   )r   r!   r"   r   r   r   s         r   claim_taskszDataAccessOps.claim_tasks  s      $ 	r   c                 $    ddl m}  |d          S )z0Get the fully-qualified memory_units table name.   )fq_tablememory_units)rs   r   )r   r   s     r   _get_mu_tablezDataAccessOps._get_mu_table  s$    %%%%%%x'''r   )r,   )r=   )rZ   )+r   r   r   r   propertyboolr    r   r	   r   listintr*   r<   tuplerC   dictrH   r
   rL   rQ   r   rV   rY   ra   rd   rg   rk   r   rm   rr   ru   rw   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   &   s	             X    9	
 3i s) #Y Cy S	 
   ^$ & &.%    I	
 I     s) I S	   9  "&!"  #$  #%& 
c'   ^6      5k	
     
   ^"    	
 3i  
c3h   ^    	
 Cy 
i   ^    	
  
   ^     T
	
  
i   ^    s)	
 
i   ^      	
  " !   
i   ^(   	
 
   ^ 

 
 
	
 
 
 ^
    	
     
tIYi@	A   ^&      ^    	
   cN 
   ^     	
 cN 
   ^      ^    	
   d
 #Y   
T	   ^    	
 
i   ^    	
 
i   ^ 
 
 
 	

 
 #Y
 S	
 
T	
 
 
 ^
    	
  
   ^ 
 
 
 	

 
 
 d

 
i
 
 
 ^
 
 
 
 	

 
 
 
 

 
 
 ^
    	
 c3h  
i   ^*(s ( ( ( ( ( (r   r   N)r   abcr   r   dataclassesr   typingr   uuidr   baser	   resultr
   r   r   r   r   r   <module>r      s    $ $ # # # # # # # ! ! ! ! ! !             $ $ $ $ $ $              O( O( O( O( O(C O( O( O( O( O(r   