
    ۇeh_                     l   d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlZd dlZd dlZd dlZd dlmZ  ej                    j        Zedk    rd Znd Zd ZddZddZ G d	 d
e          Z G d d          Z G d de          Z G d de          Z G d de          ZdS )    N)serverversionWindowsc                 :    |                      d          r| S | dz   S )Nz.exe)endswithnames    A/var/www/api/venv/lib/python3.11/site-packages/asyncpg/cluster.pyplatform_exer
      s%    ==   	Kf}    c                     | S N r   s    r	   r
   r
   #   s    r   c                  X   t          j         t           j        t           j                  } 	 |                     d           |                                 d         |                                  S # t          $ r Y |                                  d S w xY w# |                                  w xY w)N)	127.0.0.1r      )socketAF_INETSOCK_STREAMbindgetsocknameclose	Exception)socks    r	   find_available_portr   '   s    =);<<D		"###!!!$ 	

    

 	

s#   .A. .
B8B BB B)c                 >   d                     t          j        t          j        d                    }|t          j                    }|t          j                    }| d} t          j	                             |||z   | z             }t          j
        |d           |S )N    )ki  )joinrandomchoicesstringascii_lowercasetempfile
gettempdirgettempprefixospathmkdir)suffixprefixdirr   fns        r	   _world_readable_mkdtempr.   2   s    776>&"8A>>>??D
{!##~'))~	c6D=61	2	2BHRIr   c                     t           dk    r0t          j                            d          rt	          | ||          S t          j        | ||          S )Nr   GITHUB_ACTIONS)_systemr'   environgetr.   r$   mkdtempr*   r+   r,   s      r	   _mkdtempr6   ?   sJ    )
/? @ @ 'vvs;;;444r   c                       e Zd ZdS )ClusterErrorN)__name__
__module____qualname__r   r   r	   r8   r8   I   s        Dr   r8   c                       e Zd ZdddZd Zd Zd Zd Zd"dZd	 Z	d#i ddZ
d Zd#dZd Zd Zd Zd ZddddZd ZdddddZd Zd Zd Zd Zd#dZd Zd Zd  Zd! ZdS )$ClusterNpg_config_pathc                    || _         || _        t          j                            d          pt          j                            d          | _        d | _        d | _        d | _        d | _	        d | _
        d S )NPGINSTALLATIONPGBIN)	_data_dir_pg_config_pathr'   r2   r3   _pg_bin_dir_pg_ctl_daemon_pid_daemon_process_connection_addr_connection_spec_override)selfdata_dirr?   s      r	   __init__zCluster.__init__N   sp    !-JNN+,, 'z~~g&& 	 # $)-&&&r   c                     | j         S r   )_pg_versionrK   s    r	   get_pg_versionzCluster.get_pg_version[   s    r   c                     dS )NTr   rP   s    r	   
is_managedzCluster.is_managed^   s    tr   c                     | j         S r   )rC   rP   s    r	   get_data_dirzCluster.get_data_dira   s
    ~r   c                    | j         |                                  t          j        | j         dd| j        gt          j        t          j                  }|j        |j        }}|j        dk    s=t          j
                            | j                  rt          j        | j                  sdS |j        dk    rdS |j        dk    rt          j        d	|                                          }|s4t!          d
                    |                                                    t%          |                    d                    | _        |                     d          S t!          d                    |j        |                    )Nstatus-Dstdoutstderr   not-initialized   stoppedr   z.*PID\s?:\s+(\d+).*z(could not parse pg_ctl status output: {}r   timeoutz)pg_ctl status exited with status {:d}: {})rF   	_init_env
subprocessrunrC   PIPErZ   r[   
returncoder'   r(   existslistdirrematchdecoder8   formatintgrouprG   _test_connection)rK   processrZ   r[   rs        r	   
get_statuszCluster.get_statusd   sf   <NN.\8T4>:?:?< < < !!##27>>$.+I+I#Jt~.. $$$1$$91$$/AAA *">EE) )* * *  #1771::D(((333;BB&0 01 1 1r   c                    K   |                                  }|                    |           t          j        dd|i| d {V S )Nloopr   )get_connection_specupdateasyncpgconnect)rK   rt   kwargs	conn_infos       r	   rx   zCluster.connect   sX      ,,..	   _<<$<)<<<<<<<<<r   c                    |                                  dk    r't          d                    | j                            t	          |          }d|vrd|d<   |r9d |                                D             }dgd                    |          gz   }ng }t          j        | j        d	           t          j
        | j        d
d| j        g|z   t          j        t          j        | j                  }|j        }|j        dk    r:t          d                    |j        |                                                    |                                S )Initialize cluster.r]   ,cluster in {!r} has already been initializedencodingzUTF-8c                 @    g | ]\  }}d                      ||          S z--{}={}rl   .0r   vs      r	   
<listcomp>z Cluster.init.<locals>.<listcomp>   s<     ; ; ;!%A '--a33 ; ; ;r   -o T)exist_okinitrX   rZ   r[   cwdr   z'pg_ctl init exited with status {:d}:
{})rr   r8   rl   rC   dictitemsr   r'   makedirsrc   rd   rF   re   STDOUTrZ   rf   rk   )rK   settingssettings_args
extra_argsrp   outputs         r	   r   zCluster.init   sd   ?? 111>EEN$ $% % % >>X%%#*HZ  	; ;)1)9)9; ; ;M388M#:#:";;JJJ
DNT2222.\648:E?$	
 
 
 "":AA&9 9: : : }}r   <   server_settingsc                b   |                                  }|dk    rdS |dk    r't          d                    | j                            |                    dd          }|dk    rt                      }d |                                D             }|                    d                    |                     |                    d	          }||                    d
          }|t          dk    rt          j                    }|                    d          }|rht          j                            | j        d          }	t          j        ||	           t          j        |	d           |                                }|	|d<   || j        dk     rd
}
nd	}
|||
<   |                                D ]0\  }}|                    dd                    ||          g           1t          dk    r3t          j        d          r_t*          j        }t/          dd                    | j        dd| j        dd                    |          g          t*          j                   nt4          j        }t5          j        | j        dd| j        dd                    |          g|t4          j        | j                  }|j        dk    r^|j        r-d                    |j                                                  }nd}t          d                    |j        |                    nyt          j        d          rt*          j        }nt4          j        }t5          j         | j!        d| j        g||t4          j        | j                  | _"        | j"        j#        | _$        | %                    |           dS )zStart the cluster.runningNr]   z(cluster in {!r} has not been initializedportdynamicc                 @    g | ]\  }}d                      ||          S r   r   r   s      r	   r   z!Cluster.start.<locals>.<listcomp>   s,    FFFAi&&q!,,FFFr   z	--port={}unix_socket_directoriesunix_socket_directoryr   ssl_key_filez
srvkey.pemi  )	   r^   z-c{}={}ASYNCPG_DEBUG_SERVERzasyncpg.cluster: Runningr   startrX   r   filer   r   z:
{}r   z&pg_ctl start exited with status {:d}{}r`   )&rr   r8   rl   rC   popr   r   appendr3   r1   r$   r%   r'   r(   r   shutilcopychmodrO   extendgetenvsysrZ   printrF   r[   rc   DEVNULLrd   r   rf   rk   Popen	_postgresrH   pidrG   ro   )rK   waitr   optsrW   r   r   sockdirssl_keykeyfilesockdir_optr   r   rZ   rp   r[   s                   r	   r   zCluster.start   s   ""YF(((:AAN$ $% % % xx%%9&((DFFFFF
+,,T22333!%%&?@@?%))*ABBG?w)33)++G!%%n55 	6gll4><@@GK)))HWe$$$-2244O.5ON+&((57+2OK(#))++ 	< 	<DAqtW^^Aq%9%9:;;;;i
 y/00 ,.HHgtT^chhz22        $+ nwdnsxx
++-!(N  G !Q&&>  $^^GN,A,A,C,CDDFFF"<CC*F4 45 5 5 ' y/00 ,#+  ^T4>GJG!%,	      $37Dd+++++r   c                 n   |                                  }|dk    rt          d          t          j        | j        dd| j        gt          j        t          j        | j                  }|j        }|j        dk    r:t          d	                    |j        |
                                                    dS )	zReload server configuration.r   z%cannot reload: cluster is not runningreloadrX   r   r   'pg_ctl stop exited with status {:d}: {}N)rr   r8   rc   rd   rF   rC   re   r[   rf   rl   rk   )rK   rW   rp   r[   s       r	   r   zCluster.reload  s    ""YFGGG.\8T4>:??	
 
 
 ""9@@&9 9: : : #"r   c           
         t          j        | j        dd| j        dt	          |          ddgt           j        t           j        | j                  }|j        }|j        dk    r:t          d	                    |j        |
                                                    | j        '| j        j        | j                                         d S d S d S )	NstoprX   z-tz-mfastr   r   r   )rc   rd   rF   rC   strre   r[   rf   r8   rl   rk   rH   kill)rK   r   rp   r[   s       r	   r   zCluster.stop  s    .\64s4yy6??
 
 
 ""9@@&9 9: : :  ,$/7 %%''''' -,77r   c                     |                                  }|dk    s|dk    rt          j        | j                   d S t	          d                    |                    )Nr_   r]   zcannot destroy {} cluster)rr   r   rmtreerC   r8   rl   rK   rW   s     r	   destroyzCluster.destroy.  s\    ""Y&,="="=M$.))))):AA&IIJJJr   c                     | j         |                                 | _         | j         C| j        r5| j                                         }|                    | j                   |S | j         S d S r   )rI   _connection_addr_from_pidfilerJ   r   rv   )rK   argss     r	   _get_connection_speczCluster._get_connection_spec5  sr     ($($F$F$H$HD! ,- -,1133D:;;;,, -,r   c                 |    |                                  }|dk    rt          d          |                                 S )Nr   zcluster is not running)rr   r8   r   r   s     r	   ru   zCluster.get_connection_specA  s=    ""Y7888((***r   c                     || _         d S r   )rJ   rK   ry   s     r	   override_connection_specz Cluster.override_connection_specH  s    )/&&&r   )oidxidc                   |                                  }|dk    rt          d          |dk    rt          d          g }|$|                    dt          |          g           |$|                    dt          |          g           |sd S |                    | j                   	 |                     d          }n%# t          $ r |                     d          }Y nw xY wt          j        |g|z   t          j	        t          j	        	          }|j
        }|j        d
k    r:t          d                    |j        |                                                    d S )Nr]   z4cannot modify WAL status: cluster is not initializedr   z,cannot modify WAL status: cluster is runningr   z-xpg_resetwalpg_resetxlogrY   r   z'pg_resetwal exited with status {:d}: {})rr   r8   r   r   r   rC   _find_pg_binaryrc   rd   re   r[   rf   rl   rk   )rK   r   r   rW   r   	reset_walrp   r[   s           r	   r   zCluster.reset_walK  s   ""&&&FH H H Y>@ @ @ ?KKs3xx()))?KKs3xx())) 	FDN###	=,,];;II 	= 	= 	=,,^<<III	= .K$?:?< < < ""9@@&9 9: : : #"s   ,C C$#C$c                 b   |                                  }|dk    rt          d          t          j                            | j        d          }	 t          |d          5  	 ddd           dS # 1 swxY w Y   dS # t          $ r(}t          d                    |                    |d}~ww xY w)z$Remove all records from pg_hba.conf.r]   5cannot modify HBA records: cluster is not initializedpg_hba.confwNcannot modify HBA records: {})	rr   r8   r'   r(   r   rC   openIOErrorrl   )rK   rW   pg_hbaes       r	   	reset_hbazCluster.reset_hbao  s   ""&&&GI I I dnm<<	Bfc""                    	B 	B 	B/66q99; ;@AB	Bs<   A<  A/"A< /A33A< 6A37A< <
B.#B))B.hosttypeaddressauth_optionsc                   |                                  }|dk    rt          d          |dvr"t          d                    |                    t          j                            | j        d          }d                    |||          }	|dk    r<|"t          d	                    |                    |	d
                    |          z  }	|	d
                    |          z  }	|%|	dd                    d |D                       z   z  }		 t          |d          5 }
t          |	|
           ddd           dS # 1 swxY w Y   dS # t          $ r(}t          d                    |                    |d}~ww xY w)zAdd a record to pg_hba.conf.r]   r   >   r   localhostssl	hostnosslzinvalid HBA record type: {!r}r   z{} {} {}r   Nz#{!r} entry requires a valid addressz {}r   c              3   H   K   | ]\  }}d                      ||          V  dS )r   Nr   r   s      r	   	<genexpr>z(Cluster.add_hba_entry.<locals>.<genexpr>  sE       %? %?)-Aq!$$%? %? %? %? %? %?r   ar   r   )rr   r8   
ValueErrorrl   r'   r(   r   rC   r   r   r   )rK   r   databaseuserr   auth_methodr   rW   r   recordfr   s               r	   add_hba_entryzCluster.add_hba_entry  s	    ""&&&GI I I @@@<CCDIIJJJdnm<<""44887?? 9@@FFH H H %,,w///%,,{+++#cCHH %? %?1=%? %? %? ? ? ? ?F	Bfc"" &af1%%%%& & & & & & & & & & & & & & & & & & 	B 	B 	B/66q99; ;@AB	Bs<   E
 D=0E
 =EE
 EE
 

E<#E77E<c                 >   |                                   t          dk    r|                     dddd           |                     ddddd           |                     dd	ddd           |                                 }|d
k    r|                                  d S d S )Nr   r   alltrustr   r   r   r   r   127.0.0.1/32r   r   r   r   r   ::1/128r   )r   r1   r   rr   r   r   s     r	   trust_local_connectionszCluster.trust_local_connections  s    iGe$)w  @ @ @$)'. 	 	0 	0 	0 		$)'. 	 	0 	0 	0 ""YKKMMMMM r   c                    t           dk    r|                     dd|d           |                     ddd|d           |                     dd	d|d           |                                 }|d
k    r|                                  d S d S )Nr   r   replicationr   r   r   r   r   r   r   )r1   r   rr   r   )rK   r   rW   s      r	   trust_local_replication_byz"Cluster.trust_local_replication_by  s    iGm$(g  ? ? ?$1'. 	 	0 	0 	0 		$1'. 	 	0 	0 	0 ""YKKMMMMM r   c                 l   | j         s_|                     | j                  }|                     |          }|                    d          | _         | j         st          d          |                     d          | _        |                     d          | _        | 	                                | _
        d S )Nbindirz1pg_config output did not provide the BINDIR valuepg_ctlpostgres)rE   _find_pg_configrD   _run_pg_configr3   r8   r   rF   r   _get_pg_versionrO   )rK   	pg_configpg_config_datas      r	   rb   zCluster._init_env  s     	I,,T-ABBI!00;;N-11(;;D# I"GI I I ++H55--j99//11r   c                    t           j                            | j        d          }	 t	          |d          5 }|                                }d d d            n# 1 swxY w Y   n# t          $ r Y d S w xY w|                                }t          |          dk     rd S t          |d                   }| j
        r|| j
        k    rd S |d         }|d         }|d         }|rQ|d         dk    rBt           j                            t           j                            | j        |                    }|}	n|}	|	d	k    rd
}	n|	dk    rd}	n|	dk    rd}	|	|dS )Nzpostmaster.pidrt   r   r^   r\      /*	localhostz0.0.0.0r   z::z::1)r   r   )r'   r(   r   rC   r   readFileNotFoundError
splitlineslenrm   rG   normpath)
rK   pidfiler   piddatalinespmpidportnumr   hostaddrhost_strs
             r	   r   z%Cluster._connection_addr_from_pidfile  s   ',,t~/?@@	gt$$ #&&((# # # # # # # # # # # # # # #  	 	 	44	 ""$$u::>>4E!H 	)9 9 9 4((8 	 qzS  '**GLL99; ;HHHs??"HH"""HHH 
 
 	
s4   A$ AA$ AA$ A A$ $
A21A2c                 |   d | _         t          j                    }	 t          |          D ]}| j         +|                                 }|t          j        d           4	 |                    t          j	        dddd|d| j                   }|                    |
                                            nW# t          t          j        t          j        t          j        f$ r t          j        d           Y t          j        $ r Y  nw xY w|
                                 n# |
                                 w xY wdS )Nr   r   r  )r   r   ra   rt   r   r   )rI   asyncionew_event_loopranger   timesleeprun_until_completerw   rx   r   OSErrorTimeoutErrorCannotConnectNowErrorPostgresConnectionErrorPostgresError)rK   ra   rt   i	conn_speccons         r	   ro   zCluster._test_connection  sc    $%''	7^^  (0 $ 9 9 ; ;I (
1 11 A-701A A +/*?A AB BC  ++CIIKK888E  !5579    JqMMMH,    EE	 JJLLLLDJJLLLLys=   AD#  .B7)D# 7A D7D# 9DD# 
DD# #D9c                    t          j        |t           j        t           j                  }|j        |j        }}|j        dk    r(t          d                    |j        |                    i }|                                D ]k}|	                    d          
                    d          \  }}}	|r;|	                                ||                                                                <   l|S )NrY   r   z%pg_config exited with status {:d}: {}utf-8=)rc   rd   re   rZ   r[   rf   r8   rl   r  rk   	partitionstriplower)
rK   r?   rp   rZ   r[   configliner   eqr   s
             r	   r   zCluster._run_pg_config!  s    .:?:?L L L ""FMM"F ,  , - - - F))++ : :;;w//99#>>2q :01		F17799??,,-Mr   c                    |t           j                            d          pt           j                            d          }|r.t          t           j                            |d                    }nt           j                            d                              t           j                  }|D ]P}t          t           j                            |d                    }t           j                            |          r nQd }|st          d          t           j        
                    |          s"t          d                    |                    |S )NrA   rB   r  PATHz#could not find pg_config executablez{!r} is not an executable)r'   r2   r3   r
   r(   r   splitpathseprg   r8   isfilerl   )rK   r?   
pg_installpathenvr(   s        r	   r   zCluster._find_pg_config3  s>   !
/00 +:>>'**   *!-GLL[99"; "; *..0066rzBB# * *D%1T;77&9 &9Nw~~n55  &*N 	FDEEEw~~n-- 	!:AA      ! ! ! r   c                    t          t          j                            | j        |                    }t          j                            |          s8t          d                    |          d                    |          z             |S )Nzcould not find {} executable: z${!r} does not exist or is not a file)r
   r'   r(   r   rE   r3  r8   rl   )rK   binarybpaths      r	   r   zCluster._find_pg_binaryO  s}    RW\\$*:FCCDDw~~e$$ 	F077??6==eDDEF F F r   c                     t          j        | j        dgt           j        t           j                  }|j        |j        }}|j        dk    r(t          d                    |j        |                    |	                    d          
                    d          }d}|                    |          s"t          d                    |                    |t          |          d          }t          j        |          S )	Nz	--versionrY   r   z.postgres --version exited with status {:d}: {}r'  z 
zpostgres (PostgreSQL) z,could not determine server version from {!r})rc   rd   r   re   rZ   r[   rf   r8   rl   rk   r*  
startswithr  r   split_server_version_string)rK   rp   rZ   r[   version_stringr+   s         r	   r  zCluster._get_pg_versionY  s    .^[)?:?< < < !""@GG&0 01 1 1  w//55e<<)((00 	%>EE"$ $% % % (F58HHHr   r   r   )r9   r:   r;   rM   rQ   rS   rU   rr   rx   r   r   r   r   r   r   ru   r   r   r   r   r   r   rb   r   ro   r   r   r   r  r   r   r	   r=   r=   M   s       37 . . . . .         1 1 16= = = =
! ! !F], ], ], ], ], ],~: : :(( ( ( ((K K K
- 
- 
-+ + +0 0 0  $ ": ": ": ": ":HB B B  %+D04!B !B !B !B !BF     2 2 2,
 ,
 ,
\# # # #J  $  8  I I I I Ir   r=   c                   *     e Zd Zddddd fd
Z xZS )TempClusterNdata_dir_suffixdata_dir_prefixdata_dir_parentr?   c                    t          |||          | _        t                                          | j        |           d S )Nr5   r>   )r6   rC   superrM   )rK   rA  rB  rC  r?   	__class__s        r	   rM   zTempCluster.__init__p  sJ     ")8&57 7 7 	GGGGGr   )r9   r:   r;   rM   __classcell__rF  s   @r	   r?  r?  o  sW        !%t!%dH H H H H H H H H H Hr   r?  c                   L     e Zd Zddddd fd
Z fdZd Zd	i d fdZ xZS )
HotStandbyClusterNr@  c                n    || _         || _        t                                          ||||           d S )Nr@  )_master
_repl_userrE  rM   )rK   masterreplication_userrA  rB  rC  r?   rF  s          r	   rM   zHotStandbyCluster.__init__z  sJ     *+++)	 	 	+ 	+ 	+ 	+ 	+r   c                 |    t                                                       |                     d          | _        d S )Npg_basebackup)rE  rb   r   _pg_basebackup)rK   rF  s    r	   rb   zHotStandbyCluster._init_env  s5    "22?CCr   c                    |                                  dk    r't          d                    | j                            t	          j        | j        d| j        d         d| j        d         d| j        d| j        g	t          j	        t          j
        	          }|j        }|j        d
k    r:t          d                    |j        |                                                    | j        dk     rt          t           j                            | j        d          d          5 }|                    t)          j        d                    | j        d         | j        d         | j                                       ddd           n# 1 swxY w Y   nGt          t           j                            | j        d          d          }|                                 |                                S )r|   r]   r}   z-hr   z-pr   rX   z-UrY   r   z.pg_basebackup init exited with status {:d}:
{}   r   zrecovery.confr   z                    standby_mode = 'on'
                    primary_conninfo = 'host={host} port={port} user={user}'
                r   r   r   Nzstandby.signal)rr   r8   rl   rC   rc   rd   rR  rL  rM  re   r   rZ   rf   rk   rO   r   r'   r(   r   writetextwrapdedentr   )rK   r   rp   r   r   s        r	   r   zHotStandbyCluster.init  s   ?? 111>EEN$ $% % % . $V(<4<'t~4?$ ?:+<	> > > ""AHH&9 9: : : g%%bgll4>?CCSII ,Q ) Ff-f-  * *+ + , , ,, , , , , , , , , , , , , , , RW\\$.2BCCSIIAGGIII}}s   #AFFFr   r   c                    | j         dk    rJ|                                }d                    | j        d         | j        d         | j                  |d<    t                      j        d||d| d S )	NrT  z%"host={host} port={port} user={user}"r   r   rV  primary_conninfo)r   r   r   )rO   r   rl   rL  rM  rE  r   )rK   r   r   r   rF  s       r	   r   zHotStandbyCluster.start  s    w&&-2244O7>>f-f- ?   ./ 	I4IIDIIIIIr   r=  )r9   r:   r;   rM   rb   r   r   rG  rH  s   @r	   rJ  rJ  y  s         "&t!%d
+ 
+ 
+ 
+ 
+ 
+ 
+D D D D D! ! !FJ J J J J J J J J J J Jr   rJ  c                   X    e Zd Zd Zd Zd Zd Zd ZddZddZ	d	 Z
d
 ZdddddZdS )RunningClusterc                     || _         d S r   )r$  r   s     r	   rM   zRunningCluster.__init__  s    r   c                     dS )NFr   rP   s    r	   rS   zRunningCluster.is_managed  s    ur   c                 *    t          | j                  S r   )r   r$  rP   s    r	   ru   z"RunningCluster.get_connection_spec  s    DN###r   c                     dS )Nr   r   rP   s    r	   rr   zRunningCluster.get_status  s    yr   c                     d S r   r   )rK   r   s     r	   r   zRunningCluster.init      r   r   c                     d S r   r   )rK   r   r   s      r	   r   zRunningCluster.start  rc  r   c                     d S r   r   )rK   r   s     r	   r   zRunningCluster.stop  rc  r   c                     d S r   r   rP   s    r	   r   zRunningCluster.destroy  rc  r   c                      t          d          Nz.cannot modify HBA records of unmanaged clusterr8   rP   s    r	   r   zRunningCluster.reset_hba  s    KLLLr   r   Nr   c                     t          d          rh  ri  )rK   r   r   r   r   r   r   s          r	   r   zRunningCluster.add_hba_entry  s    KLLLr   r=  )r9   r:   r;   rM   rS   ru   rr   r   r   r   r   r   r   r   r   r	   r]  r]    s               $ $ $            M M M %+D04M M M M M M Mr   r]  )NNN)r  r'   os.pathplatformr    ri   r   r   r"   rc   r   r$   rX  r  rw   r   unamesystemr1   r
   r   r.   r6   r   r8   r=   r?  rJ  r]  r   r   r	   <module>ro     s"    				    				        



     ! ! ! ! ! ! (.


!
i   
    
 
 
 
5 5 5 5	 	 	 	 	9 	 	 	_I _I _I _I _I _I _I _IDH H H H H' H H H?J ?J ?J ?J ?J ?J ?J ?JDM M M M MW M M M M Mr   