
    Vhn                         d dl mZmZmZ eZdZdZd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mZmZmZmZmZmZmZ d
ZdZd Zd Zd Z G d d      Zd Z e!dk(  r e         yy)    )absolute_importdivisionprint_functiona[  
---
module: postgresql_subscription
short_description: Add, update, or remove PostgreSQL subscription
description:
- Add, update, or remove PostgreSQL subscription.
version_added: '0.2.0'

options:
  name:
    description:
    - Name of the subscription to add, update, or remove.
    type: str
    required: true
  login_db:
    description:
    - Name of the database to connect to and where
      the subscription state will be changed.
    - The V(db) alias is deprecated and will be removed in version 5.0.0.
    aliases: [ db ]
    type: str
    required: true
  state:
    description:
    - The subscription state.
    - C(present) implies that if I(name) subscription doesn't exist, it will be created.
    - C(absent) implies that if I(name) subscription exists, it will be removed.
    - C(refresh) implies that if I(name) subscription exists, it will be refreshed.
      Fetch missing table information from publisher. Always returns ``changed`` is ``True``.
      This will start replication of tables that were added to the subscribed-to publications
      since the last invocation of REFRESH PUBLICATION or since CREATE SUBSCRIPTION.
      The existing data in the publications that are being subscribed to
      should be copied once the replication starts.
    - For more information about C(refresh) see U(https://www.postgresql.org/docs/current/sql-altersubscription.html).
    type: str
    choices: [ absent, present, refresh ]
    default: present
  owner:
    description:
    - Subscription owner.
    - If I(owner) is not defined, the owner will be set as I(login_user) or I(session_role).
    - Ignored when I(state) is not C(present).
    type: str
  publications:
    description:
    - The publication names on the publisher to use for the subscription.
    - Ignored when I(state) is not C(present).
    type: list
    elements: str
  connparams:
    description:
    - The connection dict param-value to connect to the publisher.
    - For more information see U(https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING).
    - Ignored when I(state) is not C(present).
    - Ignored when an existing subscription's connection parameters are not available from the server (such as in CloudSQL).
    type: dict
  cascade:
    description:
    - Drop subscription dependencies. Has effect with I(state=absent) only.
    - Ignored when I(state) is not C(absent).
    type: bool
    default: false
  subsparams:
    description:
    - Dictionary of optional parameters for a subscription, e.g. copy_data, enabled, create_slot, etc.
    - For update the subscription allowed keys are C(enabled), C(slot_name), C(synchronous_commit), C(publication_name).
    - See available parameters to create a new subscription
      on U(https://www.postgresql.org/docs/current/sql-createsubscription.html).
    - Ignored when I(state) is not C(present).
    type: dict
  session_role:
    description:
    - Switch to session_role after connecting. The specified session_role must
      be a role that the current login_user is a member of.
    - Permissions checking for SQL commands is carried out as though
      the session_role were the one that had logged in originally.
    type: str
    version_added: '0.2.0'
  trust_input:
    description:
    - If C(false), check whether values of parameters I(name), I(publications), I(owner),
      I(session_role), I(connparams), I(subsparams) are potentially dangerous.
    - It makes sense to use C(true) only when SQL injections via the parameters are possible.
    type: bool
    default: true
    version_added: '0.2.0'
  comment:
    description:
    - Sets a comment on the subscription.
    - To reset the comment, pass an empty string.
    type: str
    version_added: '3.3.0'

notes:
- PostgreSQL version must be 10 or greater.

attributes:
  check_mode:
    support: full

seealso:
- module: community.postgresql.postgresql_publication
- module: community.postgresql.postgresql_info
- name: CREATE SUBSCRIPTION reference
  description: Complete reference of the CREATE SUBSCRIPTION command documentation.
  link: https://www.postgresql.org/docs/current/sql-createsubscription.html
- name: ALTER SUBSCRIPTION reference
  description: Complete reference of the ALTER SUBSCRIPTION command documentation.
  link: https://www.postgresql.org/docs/current/sql-altersubscription.html
- name: DROP SUBSCRIPTION reference
  description: Complete reference of the DROP SUBSCRIPTION command documentation.
  link: https://www.postgresql.org/docs/current/sql-dropsubscription.html

author:
- Andrew Klychkov (@Andersson007) <andrew.a.klychkov@gmail.com>

extends_documentation_fragment:
- community.postgresql.postgres
a+  
- name: >
    Create acme subscription in mydb database using acme_publication and
    the following connection parameters to connect to the publisher.
    Set the subscription owner as alice.
  community.postgresql.postgresql_subscription:
    login_db: mydb
    name: acme
    state: present
    publications: acme_publication
    owner: alice
    connparams:
      host: 127.0.0.1
      port: 5432
      user: repl
      password: replpass
      dbname: mydb
    comment: Made by Ansible

- name: Assuming that acme subscription exists, try to change conn parameters
  community.postgresql.postgresql_subscription:
    login_db: mydb
    name: acme
    connparams:
      host: 127.0.0.1
      port: 5432
      user: repl
      password: replpass
      connect_timeout: 100

- name: Refresh acme publication
  community.postgresql.postgresql_subscription:
    db: mydb
    name: acme
    state: refresh

- name: Drop acme subscription from mydb with dependencies (cascade=true)
  community.postgresql.postgresql_subscription:
    login_db: mydb
    name: acme
    state: absent
    cascade: true

- name: Assuming that acme subscription exists and enabled, disable the subscription
  community.postgresql.postgresql_subscription:
    login_db: mydb
    name: acme
    state: present
    subsparams:
      enabled: false
a0  
name:
  description:
  - Name of the subscription.
  returned: success
  type: str
  sample: acme
exists:
  description:
  - Flag indicates the subscription exists or not at the end of runtime.
  returned: success
  type: bool
  sample: true
queries:
  description: List of executed queries.
  returned: success
  type: str
  sample: [ 'DROP SUBSCRIPTION "mysubscription"' ]
initial_state:
  description: Subscription configuration at the beginning of runtime.
  returned: success
  type: dict
  sample: {"conninfo": {}, "enabled": true, "owner": "postgres", "slotname": "test", "synccommit": true}
final_state:
  description: Subscription configuration at the end of runtime.
  returned: success
  type: dict
  sample: {"conninfo": {}, "enabled": true, "owner": "postgres", "slotname": "test", "synccommit": true}
)deepcopy)AnsibleModule)	iteritems)check_input)connect_to_dbensure_required_libsexec_sqlget_conn_paramsget_server_versionpg_cursor_argspostgres_common_argument_specset_commenti'  )enabledsynchronous_commit	slot_namec                 z    g }t        |       D ]  \  }}|j                  |d|        dj                  |      S )zConverts the passed connection dictionary to string.

    Args:
        conn_dict (list): Dictionary which needs to be converted.

    Returns:
        Connection string.
    = r   appendjoin)	conn_dict	conn_listparamvals       /home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/postgresql/plugins/modules/postgresql_subscription.pyconvert_conn_paramsr       sE     I!), 1E3/01 88I    c                     g }t        |       D ](  \  }}|du rd}n|du rd}|j                  |d|       * dj                  |      S )zConverts the passed params dictionary to string.

    Args:
        params_dict (list): Dictionary which needs to be converted.

    Returns:
        Parameters string.
    FfalseTtrue = , r   )params_dictparams_listr   r   s       r   convert_subscr_paramsr)      s]     K!+. 5%<CD[Cs345 99[!!r!   c                 r    t        |       D ]  \  }}	 t        |      | |<    | S # t        $ r || |<   Y )w xY w)zPCast the passed connparams_dict dictionary

    Returns:
        Dictionary
    )r   int
ValueError)connparams_dictr   r   s      r   cast_connparamsr.     sR     "/2 )	)%(XOE")   	)%(OE"	)s   %66c                       e Zd ZdZd Zd Zd ZddZddZddZ	ddZ
dd	Zdd
ZddZddZddZddZd ZddZy)PgSubscriptiona  Class to work with PostgreSQL subscription.

    Args:
        module (AnsibleModule): Object of AnsibleModule class.
        cursor (cursor): Cursor object of psycopg library to work with PostgreSQL.
        name (str): The name of the subscription.
        db (str): The database name the subscription will be associated with.

    Attributes:
        module (AnsibleModule): Object of AnsibleModule class.
        cursor (cursor): Cursor object of psycopg library to work with PostgreSQL.
        name (str): Name of subscription.
        executed_queries (list): List of executed queries.
        attrs (dict): Dict with subscription attributes.
        exists (bool): Flag indicates the subscription exists or not.
    c                     || _         || _        || _        || _        g | _        d d d i d g d d| _        t        | j
                        | _        | j                         | _	        y )N)ownerr   
synccommitconninfoslotnamepublicationscomment)
modulecursornamedbexecuted_queriesattrsr   empty_attrscheck_subscrexists)selfr8   r9   r:   r;   s        r   __init__zPgSubscription.__init__6  sg    	 "

 $DJJ/'')r!   c                 D    | j                         | _        | j                  S )z\Refresh the subscription information.

        Returns:
            ``self.attrs``.
        )r?   r@   r=   )rA   s    r   get_infozPgSubscription.get_infoH  s     '')zzr!   c                    | j                         }|st        | j                        | _        y|j	                  d      | j                  d<   |j	                  d      | j                  d<   |j	                  d      | j                  d<   |j	                  d      | j                  d<   |j	                  d	      | j                  d
<   |j	                  d      |j	                  d      | j                  d<   nd| j                  d<   |j	                  d      rL|d   j                  d      D ]5  }|j                  d      }	 t        |d         | j                  d   |d   <   7 y# t        $ r |d   | j                  d   |d   <   Y \w xY w)zCheck the subscription and refresh ``self.attrs`` subscription attribute.

        Returns:
            True if the subscription with ``self.name`` exists, False otherwise.
        Frolnamer2   
subenabledr   r3   subslotnamer5   subpublicationsr6   r7    subconninfor   r      r4   r   T)(_PgSubscription__get_general_subscr_infor   r>   r=   getsplitr+   r,   )rA   subscr_infor   tmps       r   r?   zPgSubscription.check_subscrQ  sb    446!$"2"23DJ)ooi8

7 + =

9#.??<#@

< !,!?

:%0__5F%G

>"??9%1$/OOI$>DJJy! %'DJJy!??=)$]399#> <kk#&<58Q[DJJz*3q62<  " <58VDJJz*3q62<s   =!E!!!FFc           
          g }|j                  d| j                  d|ddj                  |             |r|j                  d|z         | j                  dj                  |      |      }|S )a  Create the subscription.

        Args:
            connparams (str): Connection string in libpq style.
            publications (list): Publications on the primary to use.
            subsparams (str): Parameters string in WITH () clause style.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            changed (bool): True if the subscription has been created, otherwise False.
        zCREATE SUBSCRIPTION  CONNECTION 'z' PUBLICATION r&   z	WITH (%s)r   
check_mode)r   r:   r   _PgSubscription__exec_sql)rA   
connparamsr6   
subsparamsrU   query_fragmentschangeds          r   createzPgSubscription.creates  sk     3799j$))T`Ja c 	d "";#;<//#((?";
/Sr!   c                 d   d}|r.|| j                   d   k7  r| j                  t        |      |      }|r7t        | j                   d         t        |      k7  r| j	                  ||      }|rAg }t        |      D ]  \  }}|dk(  rP| j                   d   r|du r| j                  d|      }3| j                   d   rC|du sH| j                  d|      }\|dk(  rV| j                   d	   du r|du r|j                  d
|z         | j                   d	   du s|du s|j                  d|z         |dk(  r:| j                   d   s| j                   d   |k7  s|j                  |d|       | j                  j                  d|dt        d        |r| j                  ||      }|S )a	  Update the subscription.

        Args:
            connparams (dict): Connection dict in libpq style.
            publications (list): Publications on the primary to use.
            subsparams (dict): Dictionary of optional parameters.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            changed (bool): True if subscription has been updated, otherwise False.
        Fr4   rT   r6   r   )r   rU   Tr   r3   z
%s = falsez	%s = truer   r5   r%   zParameter 'z)' is not in params supported for update 'z', ignored...)r=    _PgSubscription__set_conn_paramsr    sorted!_PgSubscription__set_publicationsr   enabler   r8   warnSUBSPARAMS_KEYS_FOR_UPDATE_PgSubscription__set_params)	rA   rW   r6   rX   rU   rZ   params_to_updater   values	            r   updatezPgSubscription.update  s    TZZ
33001DZ1P<F 1 H djj01VL5II11,:1V!"+J"7 jI%zz),%"&++e
+"S!ZZ	2u}"&++dz+"R22zz,/47EUN(//u0DEL1U:u}(//e0CDk)zz*-$**Z2HE2Q(//UE0JK KK$$FKMg&i j%j*  ++,<+Tr!   c                     | j                   rEd| j                  z  g}|r|j                  d       | j                  dj	                  |      |      S y)a  Drop the subscription.

        Kwargs:
            cascade (bool): Flag indicates that the subscription needs to be deleted
                with its dependencies.
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            changed (bool): True if the subscription has been removed, otherwise False.
        zDROP SUBSCRIPTION %sCASCADEr   rT   N)r@   r:   r   rV   r   )rA   cascaderU   rY   s       r   dropzPgSubscription.drop  sN     ;;5		ABO&&y1??388O#<?TT r!   c                 N    d| j                   d|d}| j                  ||      S )ax  Set a subscription owner.

        Args:
            role (str): Role (user) name that needs to be set as a subscription owner.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        ALTER SUBSCRIPTION z OWNER TO ""rT   r:   rV   )rA   rolerU   querys       r   	set_ownerzPgSubscription.set_owner  s%     :>DIu<<r!   c                 `    t        | j                  |d| j                  || j                         y)a  Set a subscription comment.

        Args:
            comment (str): Comment to set on the subscription.

        Kwargs:
            check_mode (bool): If True, don not change anything.

        Returns:
            True if success, False otherwise.
        subscriptionT)r   r9   r:   r<   )rA   r7   rU   s      r   r   zPgSubscription.set_comment  s(     	DKK.$))ZQUQfQfgr!   c                 F    d| j                   z  }| j                  ||      S )aA  Refresh publication.

        Fetches missing table info from publisher.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        z)ALTER SUBSCRIPTION %s REFRESH PUBLICATIONrT   rn   )rA   rU   rp   s      r   refreshzPgSubscription.refresh  s%     <diiGu<<r!   c                 l    d| j                   ddj                  |      d}| j                  ||      S )aw  Update optional subscription parameters.

        Args:
            params_to_update (list): Parameters with values to update.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        rl   z SET (r&   )rT   r:   r   rV   )rA   rd   rU   rp   s       r   __set_paramszPgSubscription.__set_params  s/     59IItyyIY?Z[u<<r!   c                 N    d| j                   d|d}| j                  ||      S )ae  Update connection parameters.

        Args:
            connparams (str): Connection string in libpq style.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        rl   rS   'rT   rn   )rA   rW   rU   rp   s       r   __set_conn_paramsz PgSubscription.__set_conn_params  s&     <@99jQu<<r!   c                 j    d| j                   ddj                  |      }| j                  ||      S )aa  Update publications.

        Args:
            publications (list): Publications on the primary to use.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        rl   z SET PUBLICATION r&   rT   rx   )rA   r6   rU   rp   s       r   __set_publicationsz!PgSubscription.__set_publications&  s0     ?CiiS_I`au<<r!   c                 j    |rd| j                   z  }nd| j                   z  }| j                  ||      S )a  Enable or disable the subscription.

        Kwargs:
            enable (bool): Flag indicates that the subscription needs
                to be enabled or disabled.
            check_mode (bool): If True, don't actually change anything,
                just make SQL, add it to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        zALTER SUBSCRIPTION %s ENABLEzALTER SUBSCRIPTION %s DISABLErT   rn   )rA   r   rU   rp   s       r   r`   zPgSubscription.enable6  s6     2TYY>E3dii?Eu<<r!   c                    d}t        | || j                  | j                  dd      }dj                  |D cg c]
  }d|d   z   c}      }d|z   d	z   }t        | || j                  | j                  dd      }|r|d
   S yc c}w )zGet and return general subscription information.

        Returns:
            Dict with subscription information if successful, False otherwise.
        zSELECT column_name FROM information_schema.columns WHERE table_schema = 'pg_catalog' AND table_name = 'pg_subscription'AND column_name IN ('subenabled','subconninfo','subslotname','subsynccommit','subpublications'))r:   r;   F)query_paramsadd_to_executedr&   zs.%scolumn_namezRSELECT obj_description(s.oid, 'pg_subscription') AS comment, d.datname, r.rolname,z FROM pg_catalog.pg_subscription s JOIN pg_catalog.pg_database d ON s.subdbid = d.oid JOIN pg_catalog.pg_roles AS r ON s.subowner = r.oid WHERE s.subname = %(name)s AND d.datname = %(db)sr   )r   r:   r;   r   )rA   columns_sub_tablecolumns_resultcolumncolumnsrp   results          r   __get_general_subscr_infoz(PgSubscription.__get_general_subscr_infoI  s    
 "$(9QUQZQZbfbibiHj  }B  C)).YVf]&;;YZ)+236EE $TYYdgg4Vhmn!9 Zs   Bc                 Z    |r| j                   j                  |       yt        | |d      S )a  Execute SQL query.

        Note: If we need just to get information from the database,
            we use ``exec_sql`` function directly.

        Args:
            query (str): Query that needs to be executed.

        Kwargs:
            check_mode (bool): If True, don't actually change anything,
                just add ``query`` to ``self.executed_queries`` and return True.

        Returns:
            True if successful, False otherwise.
        T)return_bool)r<   r   r   )rA   rp   rU   s      r   
__exec_sqlzPgSubscription.__exec_sqle  s-      !!((/D%T::r!   N)T)FT)TT)F)__name__
__module____qualname____doc__rB   rD   r?   r[   rf   rj   rq   r   ru   rc   r]   r_   r`   rM   rV    r!   r   r0   r0   $  sU    "*$ D45nU&=  == = = =&8;r!   r0   c                  L	   t               } | j                  t        dd      t        dddgddddg      t        dd	g d
      t        dd      t        d      t        dd      t        d      t        d      t        d      t        dd      t        dd              t        | d      }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   }	|j                  d   }
|j                  d   }|j                  d   }|s2|sd }nt        |      }|	sd }nt        |	      }t        |||||
|||       |d	k(  r|r|j                  d        |d	k7  r_|r|j                  d!       |r|j                  d"       |	r|j                  d#       |r|j                  d$       ||j                  d%       t        |       t        ||j                        }t        ||d&      \  }} |j                  d0i t        }t        |j                        t         k  r|j#                  d'(       d}i }i }t%        ||||      }|j&                  r t)        |j*                        }t)        |      }|d	k(  r |j&                  s:|rt        |      }|	rt        |	      }	|j-                  |	|||j.                  )      }no|j*                  d*   i k7  r-|	rt1        |	      }	|j                  |	|||j.                  )      }n0|j                  d+       |j                  d|||j.                  )      }|r3|j*                  d   |k7  r!|j3                  ||j.                  )      xs |}|||j*                  d   k7  r|j5                  ||j.                  )      xs |}ne|d,k(  r|j7                  ||j.                  )      }nB|d-k(  r=|j&                  s|j#                  d.|z  (       |j9                  |j.                  )      }|j;                         }|j=                          |j=                          |j?                  |||j&                  |j@                  ||/       y )1NstrT)typerequiredr;   z5.0.0zcommunity.postgresql)r:   versioncollection_name)r   r   aliasesdeprecated_aliasespresent)absentr   ru   )r   defaultchoiceslist)r   elementsdict)r   boolF)r   r   )r:   login_dbstater6   rW   ri   r2   rX   session_roletrust_inputr7   )argument_specsupports_check_moder   r:   r   r6   ri   r2   rX   rW   r   r   r7   z7parameter "cascade" is ignored when state is not absentz8parameter 'owner' is ignored when state is not 'present'z?parameter 'publications' is ignored when state is not 'present'z=parameter 'connparams' is ignored when state is not 'present'z=parameter 'subsparams' is ignored when state is not 'present'z:parameter 'comment' is ignored when state is not 'present')
autocommitz3PostgreSQL server version should be 10.0 or greater)msgrT   r4   z_'connparams' is ignored because pg_subscription.subconninfo is not accessible in your instance.r   ru   z0Refresh failed: subscription '%s' does not exist)rZ   r:   r@   queriesinitial_statefinal_stater   )!r   rf   r   r   paramsr)   r    r	   ra   r   r   r
   r9   r   r   
connectionSUPPORTED_PG_VERSION	fail_jsonr0   r@   r   r=   r[   rU   r.   rq   r   rj   ru   rD   close	exit_jsonr<   )r   r8   r;   r:   r   r6   ri   r2   rX   rW   r   r   r7   subsparams_strconnparams_strpg_conn_paramsdb_connectiondummyr9   rZ   r   r   rs   s                          r   mainr     s   13Mut,54$"#9U 
 y:Z[v6V$&%0V$u%fd3%.#  & # F 
z	"B== DMM'"E==0LmmI&GMM'"E|,J|,J==0L--.KmmI&G!N2:>N!N0<NFD,|"NG	= 	gMN	KKRSKKYZKKWXKKWXKKTU  $VV]];N(DQM5!]!!3N3F &++,/CCRS GMK "&&$;L !3!34}-	""2:>
0<
"))**6*45;5F5F * HG !!*-3!0!<J&--j.:.89?9J9J . L
 }~&--e.:.89?9J9J . L
 \''0E9",,Uv?P?P,Q\U\G7l.@.@.K#K"..w6CTCT.U`Y`G	(	##G8I8I#J	)	""!SVZ!Z[ &&&2C2C&D '')K LLN W(//)::#0!,  .r!   __main__N)"
__future__r   r   r   r   __metaclass__DOCUMENTATIONEXAMPLESRETURNcopyr   ansible.module_utils.basicr   ansible.module_utils.sixr   Fansible_collections.community.postgresql.plugins.module_utils.databaser	   Fansible_collections.community.postgresql.plugins.module_utils.postgresr
   r   r   r   r   r   r   r   r   rb   r    r)   r.   r0   r   r   r   r!   r   <module>r      s    A @vp2h
<  4 .	 	 	  K  "*U; U;z
X.v zF r!   