m7GW@M27&~^^H;uj-`wQATa39)56Uwi>2zeOp%Q
zcvlEE%zuZ*AQh!XZrK1pY<7kId6H@v)+*w*R<4z&@&>bnXn(N8dO+|ons5+wI`ZFn
zCs?)gyzE6sAu?!ui+u#i5ku6_Gl}(se1ssY<%eEaK?af=EsK;GleaqBw8NX>t(7{B
zI`P?(qYi9x`ufvpi|Aa+grMbh*GX2MC;Px7Ha!*~GWgbxQf
zsqIe0#84;YA@4NQQ`@KM*smKb%^h!vgt!2-{C|ZdHb@iAnaU5xgN@YNS`Jm^zf7U0
zI5BDwk55UaPXnymztsRn%xyfBiEHT;-B=BL-
z80KucT+XGmeBO;AUNzdJu?byFl9bz@y(y_UxJq>oCMRBgzC1oGFVyvgls6f7N%iH+5t)*ja6;FrSj9*oztQlS~r5n}n0uyK4USh?H1nO+k0COj*ObiI8
zg6Oc;FFBGmKuX821>a}H2|Axi-r~r&$^g8q-(}$o5S^yqQ$HkFQ?6RGgiJ0YUF}x9
z@PbY-NQIrKqpp1n^@;}j)H@vbg_FZEVoYGus*y){#Q2@`v+dZUh^j1Bx+u+|wQfq0
zXARfS5|C%d=Cp1Ak|--#*a)5=ER_n31_WPZ=j+C?L$ZKtPSo
zjLv)46yoI?wkKWv`InLl*MYLCaq2&onRq*ufIBU|A9K6VboLIxfC3q5e{3H+*87=}DCSM4(%BCuxKhp2<9rKXx`~s8EYk1vEj@V#gIzj~E4@>fbeFP!+u=AeK4&i9J
zvu=bcoc)hzXG3JmHTo%y{m7jZw)5hDS=
z0Y|_Y{`{*z0GI;ATf04gae7-?V9l9TFz|g-%iYB~P0z=d;$-M9O3Yue4n!@H;6B6u
Kz5)KPgZ~4hm{FAg
delta 2680
zcmV-;3WxRl7KRmnFoFt#0s#Xsf(l3m2`Yw2hW8Bt2LYgh3ONLV3N&LNQU>0Dyu7hz*Jj-vQMaMibj80I{Gfx_n80za~nTBO`v%n
z>yZm2kdYt;5}+jMgW?N@Za^(Q1BBl+S*V98Mj0U~`vhScY@hM|Amb9?-c07@n{2TN
z4ZjPd3mcwsoh(R
zKPelKvJ%UCk<1bCYN6wkFW)2kp#0|`8?4py^5r(FYRbzsDULW%3CdQVeWN&TdEG9n
zt;i*M=DcueEFuv(f>N!%!V!hFe)=8}BI=Ax3vCD$qT;v|b+~5q)2h+phjJHx`ckt;
zA9enueXX!kHrvC&_8u!9H;77J&316uSS;QH*o;!UB$TarVP+mMxhe*^gnzzKq03Yz
z=CFqs%4RqJ3E!NE$?an@WZL+eX_7P&;qjd^UoT0e*Gv1%t$eTu67&
zDU}K0;BO`17V_2++2S@rJfv#)&m#a%o;O$QlsS6(um+JD7pix8T=186vbi}D`F{U*
zI45K*aX9IOQl}!@Xo#kSw+N4+ExST99|%i}Hf*3HBXX~_9<#UH0~;`Zf(2Rz2`Yw2
zhW8Bt2LYgh1xo~i1xGM~1w$}`1wRH0Duzgg_YDCI3IPJ3f&}g`f&}U?90m$1hDe6@
z4FL=R127H*2)-n))xkB;wE_YN00e>r$Te5QcDn(^4E9S@$#cHy&rsex$4XR3A!2Fq
zqQs{UtezOLr~CPC@@lew6%tTn5WX(gDa*(gjDv~nYN!Jct@-R--_k9WzZ<8f{Qa1E
z*!=4;O0(Qb_Tr%)&pgA%-hKa2KG})tgc_S@R9%|S_{zY(e~&(bLTEH!44uUC$zuaU
z9j`B3-!?P>HO;F08*Gf*f!~iQ+b($3XezhU)j7t|18sD|2axZ7_}3@XXqrzSU#dQP
zjvLo~nIJAUbiMe-fP~>0h>KteIlri~j=F%}-kb=oBAqDV^ig9x;l8PFf|RN&l#CsL
z5}Bn|PbWuY4Xf&UXXiB|=h{ToG=#xd%RY|TG2^JZkU0nIfCjGE|N1Q9^QzGWoWMjh
z4=R?(h>-A~PJ_yS+(d=S`0i=8uAQ>BE_VYtV!B^~p%VH5AicU-dero3sNc9YoRatM
zk#jN+Tg}Ooc2|=)qWU7NSldtcokAy_n^-~N4cpo8Pwz|u=vjfipw&7o?t%{p%10C0
z2EOQQ4HaC*2A~F$09YV%R0{N041@LK;!PQQf`7WG*tYL~iMH7!DDP1z9wHvU4>}9x
z|0dQit^*PEdGt!1WLJ>T2@hAj5=}-y`1FnMzn_&JuYE{gb-q=eJ-I2Jd;+b{anbd(
zmHIQwg~ZS(RLz%CJY1xdS=qZ6)6+R?%MC2oN56D&S2F~Dno8<(3m?O*Bi<3#2669E
z$b7X=h{x}LmE2aU_1g&asiVgzVXX^Y8W;G#W(8?zBLnsgKN^!|7`Vs8Ci+CPA1g*E
z{GpQ0(%XxlN`H)I791U9%NB0}ARF>ePt(9E5{nPhYVg{V>zxdb{1fiAyvhW@j^k3j
z2xGBn%^R1Y6nQBJSUOJ|ML45m$S5G9mu!_>%Y0~mt`T*X6ad536Z*)#2(>;
zGpQD|coOdNjo^me>?i{18x8M|A`cV*BI|lxjxFT!sy?B=m
zXpAQzaL)aGHtdC<4iv0o@bllax!n_CnVQE2XwbE7BFYWyBT-l{DELEalUevdn5&{W
z*M6pdXL`5+tv_da7Y5PEi&)9JUZxI%6zAQIlOe0u>E
znieT)N)$H6bi;?R=nSnk^ll%<%8+eo>!aO)yh3XqVmbn$muTuUn^ZzOiN6r<)0ku|
zmGJBAfIPFM`o(XGMI^VrP^EWRvU~0=4@{VU^|FmjC)BaH7kTYaD#+-f6_DXoZcA1iOfByp|G;Yeb#5Tnh-5SOE0+qb
z3wODO91t9dJGun3%f83`32@7IbVp^V?k@^F@On3|&8&yR5^umXjmHUi1HR^_sKmQ}
z#DXslC4nH)3^(r)A|WFj<{+WYOOqK(dnrZbvJbI`GD`eY+l$T=gcE;wrC%%jyOY!q
zYna37@@+Xt8%(N|W&@TM^+$44BaOz)6AfKQD0tf)|K8wLp~hDe6@4FLrMFb)I=4$OvKtE%kwlv`zOxJgi-!|Vt9Hr!RXLIPOB-F^#z|I+}FLvXAjW#q;uaB8o
zHp}Pok^^K?&v9z>?;7ix|6^0;0u%(5^0s4NoW4sj{i_jSH@Q|I%L7cPFwuuV@{mct%^4w=Jv(TVQYeh
zOsgTa_0y8l^sg=~jwl$cZUw9Mc8O?!r?dk9gjoq*t}apW5YDFl;$n%0i7c2GN8YeL
zpF;+Y!KT%Ml?#Nd|2_q9pHN5~A$xq2?~x*AG8!acrBIQqkB&rC2|Rw%eV_#;AI`Vx
z?)0@$~olDz?VErLW0yYK!YxtUbl*retMEk`Z-;J+TIcpH$X!|(-KkirD%sfq_I_dFyv4pf&8Pv~
zklkNT582t&v+3dv$>naHDltv-Lg)sgP8l&`pRTwU28`v{WV#Gm6R?xD;^3SE-|}O1
z@lhP0`KrR_CdF_WUn2sBP;gp90}>}epnY~}_UuM#Ie$>0d(KbJCT*yr7W`GMq|@;L
zx_hbbgz{gV@%`UT$h#Q9S);z*TYP9~k&6gP8y?lW=FW0+^j~gShFGO{pEQ6yM21W!
zQaE2yl$mSKaoG8s>E+1z*RgAN8r-Akk^u}_MkMvILOH@&8gREJC>
zo56+NuOYKdCN(f#60g9^8Vb}oDxoI`Ukq21dh|Z(V(=zHtMo~o=~~S(S6M?h`z-}3
z(nqDmE)Sl>znBO)U|&g8l#=5lsw3Bk0@T!->DrBOd0*->!WJ8EQZ_$d0G5NK<#|%~
zy=v0xiQfC!ya!BX0jSTUHfVtnQoVhU-Z+^sCy{9r_GZAQ#@ndgFp&N=azaIQP<*2d
zC%axXM7F_@=MeS^0##rA8od5I6>LYC7Q%RLDLQNCV8qF>r%%
z44mgR8v+M${)4EqaS+q<3Jn4R&X?{V0z_E;4iG3pAA$T6FCcD$8Na`nx4-9qbuS1!
zR5@RYZUlj70f-CKwEw*dqJsbsT-0<{At<076&NTCW@MRF?=57h%0|bD>Bo&KqKF`o}*Fo6Jb0x{ZEYerf*&^
z^3hDXpo7JIV}($^Bny*FrR@mK8Azs9d6{w%lWxE|Ak8&K>5j}KXbYZ|r*JKZr0OKg
zwYw`5iXI^Pq#DI_@LfF6#d0*_1ugF1<1TP^VV=3Fi1$6!DSO!9w!9So3Ui#gK%C14
z)yl$7+w`-g9$cR?mSe)=gM6gk_w4`Ey?)Vcar%ms<@T1Y7fkAYp-Vjv4bo8I$%ibe
zpiN$+3k?;0visF858G-2dpErez_+*t2bzyM7=O_hz&+
z%eYX2I{T6y4WUkblWaaLvKD|4Bp#1QG|6{CyYJ5P7;u1=Gt*qgZz5Xx`Q^H}c1F|Z9
z6)#!Z1{BW_jwc7YE9i}$^&K&v!o5RHy?azpgh|@QWihSp9(t1}!;YpDa=5B5;+WPW
z-F1fDtP^fttQn>tJFXMS-N76l!Ai5PmU#>1a#0`*?PJW5--ZKdi=7tbWyAbRt8_cvY
zoayb+?I9`*tE{AwG=Q0)eAzOqOj83Vxc69fe;1-m(
zOHqz>@Akhc$YW6_w2b&JGu6dCEadRaqm|er|Zb`DIl3g18YA~R%~2;Dg^Zzjj>q%
z!j>i@L_RaKWv_A!4mMdMh(r}Gy70GhFfXhk>f|8IGfQT?sAA7Hcbe(8
zVCokdjxj^mXxg;Gr|l2#+Ttyt$edd`xrC?|<(Mz_mprQ?zihLy?HyUE^DedDo;`Q!
zyWigYSHuuJ7zcTKUcWh?2x^A^Ir;)U5OfaZ&r9k5Ctjy3`SyVd2Q+y`T-{4t(5?R~
z-Xn9zU&aCfr`_$#<}s(2RKPg!VB)pd9689d9s9#j9~uP*+Ea!BAN3yW>3h<3BK#$#
zE_b?s*#x5IZ2y!tj|%nnvv0)nz|SK?L8~i+@4aDqB)rjM%N8s)r$%>2-6Lp=|Hb`8
zaf5*V-QbXqfS=#10J+j6!TKE7$aLt@4IBB;nS;J6!7^rQ@eB*OPKUN{<|i-x%NmmQ
zTP>|5zAgW)?;%F3%P}GJ$8b
z?&K+aS{K&vXU*gDyOuaJBB^>y1hOv`Z^nsUmD|iC9yiff6?1L!9ac#;i;sp*GQHDp
zwwant-67S;VF%65a-c(6_HfGId>xzJp~?|b124YyG_`MSz_c9bn&amQ1)Zr(j>sQB
z%?=q=qEtux(0{Efy)yQ)U4!z~u=DKMPDXfC>Ad8!k4kYY;{ui}qIUdbV-4jIqTRrU
zItSbsvR`8WpNIaAMm~2378Q8rqKJWX)ET&R^Q)mHxxJgN4kuSLtVY*3Dt+oZuqEhi
zKCJ9>%W}Z7EruZylMltD?=EjNH(fPl2`|B8H7ka~4o!+3Vkx~D5Ryc}mQJPq
z0*dFDz33(uMK3izfVn{javc$lw~E+V!+1e@>^`GMzDmey&|Z^Pffl)
z!JxDS9(Wz~R|t8!%VPKS=qLCOm{8saT%hE@Lc_fa#J0TleZQB0Sn0v7g&}rFmn_qp2T(I{=;B4=2Ozms}7+1#<|>6iP5=ONq8UjI7Ca%rOb$xdzLQbH_Ny
zK-?#wMoTKKrNpl-yl#=!k2?%qBLkSNYTx9~P9x$r?7-@yiA5kb1T#Lk%I1S2;UhOH
z*0XTkz`ylS)l|XqSm1>_dx5E_vyLM@z55Ti5%q(j*eFvFX}wi#@Po^ps^!^VPjB3&
zT*oA~w2-U*3j03xWv;rpgOeSQ?$b0yp>#pae5-%in6-0cJ-p(lv?8_iH;#2791v&i
zrrq~bQ1O0js?Yu5W(>9{1S`KdcdZGnYsVt|Z)^=Du;&`A0CG#(15YMQ<({naKq)
zH~y{?hP)cmErg}k%0ZqFA*3?MhV;&*VLnK3FDY7)oUVA-v=m>r_uv^vBuHa%W1Kd-
zD~a?fsUk^LQpC8qU8)NC=14Kgev~9V=bbAB%u`LhD`I3ne-)b~ogoZO4OY9RKDk0c@i3nxy?HUIBx8r8;k`d}ddGKU*3mM@2_^DEY#dDM^QFuG
LZ<;9me;xcEsMc-9
literal 2694
zcmY+^cQhM}8U}D7B2rtd+B(osQG2vnqp^35Qq-;zDr&^~k{YpRZDL1_QW~nYXDeD7
zYOhe#sJ7Y|MP1)H_ulW`Ki>16^E~G~fBxWD2$mK=2ggEmL5x>obYu3J0Stg*EJOu}
zg(#fGvT!VL;_KN{}XO15w+X@FCFN|pTN;cWss$yF*rT<{
ztu>kioks?m5qius=tMbuZ}s_)=s5~GUNC~n>$6B}`B-dkZgWLfugXFIPNT@1;aQSu
z7KVdh5VYe4KU3yzI2z+RMufWkII|;;I~voUPu(Wg*gGA+SAXJmLyNxZK!7zfpqzJh
zppRNst?^)D3wO)=*|&;W(9oTy`(f?-eb%@TMYyz#7pw1_*^41eliHIhwViY9CS@k64I5fGejex&B>Lwv
zb7QGtq(-w!QTe)qvv>_KqI_{Jq+=7g#Q?>Xz)NhoFo@v%(ksEs387P(d%bH-v
zEX}7*x~?uLD9SNkG^8Ec|0f6{{Sxc)^DkE4ZEsC2lkhK?_?2?aj|55?#Wrs;WDtq`
zWo=lO=bOVW44c0{okqy14kt=4-0|%&)m5?0h*Bmo#zz|E_zZ&^y`Z&hc4(ZD)SP1v
zMr~%ZfE}sHVk7PlgW9rhLXNdmqg?%>Hl8X7<9k?hETEDtf;eWovdnH>mZWGuA)do8
z%TYVg-}p(kcD%&zk@G^3M{SE+ru|`cmX6DI)9ov1g!fl}?C*U`u91gLz}#nIs@@3n
zK8!03MHR|{%+hIcOU$~9Vz(#{d(_S1f*tV)K&y@Nqr>f5M`!T}=eGG<^6qYxNVJnD
zq!Y()?6$rpnwr(Ua#;y+y3nF8hHzIDT|4!)m&&-NU9VXGC;wW?KEnkZZ^!|c8QrSU
zZ|C~US@Xb)MBwdFRgtVZkA07132nk?E&FTg<*Ael-tC$wKEQd)QKMIu{x6(AchJSY
z{m>L5mT`L54bA&9C4o_n_bq31g`)~b$~UsGNPd7a$vSz-+FX}sG@JPopE&%bzuIf%
zRx5iiS#wsVqa+h)FO^h%m)qn%XFbDs=@>;V?v*&<43l@U5fTN=t3I}F8dfUB&)7<0
z%*tCyv++9woh~L+wRgLdK_0+zg_!lUzK>ch2?THFL=k?67S-gF{DZtW7u+cj-;ss*
z@Q_JB1*52!cmgF#x`40s*8UM3##&%WS6P}M^1Q$&*>%4Jh+sX@w;6hzHobuvRu!t@
z@(Q5J>Z==cKGTj{?do*e)WJm%;3GN8N3xM%y*6`nx;(hG*=GcqgEcXA>#@l4hMs)hFfPSo*vF16MJY-T;WDN1Vl4XQu%=_aDX}0NOL}cFv&N{6F33
z{?<)L;{J{KG|9!kbpv97Z?i#$O!x1-FiX|(&go`azIgZwGxCW+0CnOqEaBSmIZaL(
zR*~T3I<9oS>t;z7S7q2c7G!e4WW-{)q2f+6Ub(;2fq5tHGr4Sl_r%?+q18$M-CjLwb>i*JF7r~;8&cZ?5YC^unmx)wps{WA}YoCx{Nf164#2wlz
zCfHIzxGj@UriJY)sj;=h+Bp~*h;t24tX1?*GydQOdU7=nNWu7%ua$gBjBKq8ezJQP
zyPdd_h?zIt5tGV3U!zJo;G~#SFe>1B-=db>?{xNhrF6?9@E)me!jaFvo7nPDkK7
z=%}SPpLHOb)1B4R9DFnEGx*5&fwyEr)zO9Hc;!d>gM?kRl;iVi4Rb1^kDDro*_PU9
zOO!XokU#Om+0iS$Q#LjipmKCoqQ)OU*5qg!4q*>IX#_L#uz8pU;7Sc~)%AztE*D2j
z25^MmRrtrm?Lnxd_(25sPTq#l$ttWFJ`Za*6K)RL-o-zvLGwbN&Y0RSGNd0K9Q?Av
zNRO>SQm=>C$MCS=&0**sp1Mg-Y4pNGMMX`IMXU#C5o{+RBzfVKbCuVP=-N|SC_!#A
zAxNXubYEbXh}mlEvcED1ugiaxIo7Th3kWqM3sSg1k&c;{TNNXhM)CES(vXdZ>IHnl
z(^GQmkHqZDk0Vh!yo8
z6;B)wlY+WWT0Vrh7{J<$#mvPVvI5yW0_yapCww1_K(JH_VR8ceX0xU+t=+J^SxIfi
zYT3Q^GGaeoy*wfpR^VNKv0myeaAjcIa&}rl>U*`<3H_}QMS*lTR-OrY;3v=D-`OX9
zRt`oH)t!&WpWOnp3=_mD!S$ESR&_FxeqR!;1etJz<-rmdawC1MaY4o|`D9G*96xsv
z8j>vRo>=K)Q>CxvG7PzD?2|EQP{-pxM~AH)A&V<6Arn9@jZ3?Q-ON_HFVs>ukaW+g
z(y?lGl10HHYv4z5%=&vba{Hw6G!ZhdY#?8Od^+Kzox$bLcDcx?hH?XG3Neg7QsGs#
zrr9_Bb8W9#o6CQ$1$GyX6+OuqMDvfoq^cP=ceNch1j=EeDlHY=ngE(e>(IFtd`z8k
z@v>Z~)m|;5J(Z5Pb{Bx3gK%FiHWqVE__UuUI&-?QO5Lx=&Kl5W7Rl_ys?*WG)*%QU
zJU{K^vc>fqQsBbCzMfCBl1wG0V@CF(HrijMsDQ)-ww+3fNe=xT^@)zabLnDjV9dnT
zgYVtFEao-7Gk+YiI9EmDan=d+GF&9#NtWt3?nIXDFCJsZ2ZZzah4%DR*eyJjyQ$wN
zs-5>GgGFM{agge=cB|3X)CZ2g29Bn=-W2b85rcvTk?qWgMEhrZ+R?h9Hxk>&wvD)<
zqg_t{>dmdG|jsA?!Xn(c`(!RLOBg37=i;CWv4L;HTCx}b(|t)Z@_u}t0N;+79s?u
zc}#<%aij5vU-+lslH~?MQ&4NtQoQe}7@iZwT$pEY+*K(bxGY>0&IqE1vCz?;r>6lz
l6R6@-fxB}47+D_gUOjmPu6yh`5PU=)7h!us0RGMAzW`!{@sj`m
diff --git a/certdir/goodclient.pk8 b/certdir/goodclient.pk8
deleted file mode 100644
index 7a75b411cfbe280f5a97cb28b8d93b6b2b154ced..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1261
zcmV8wLp~hDe6@4FLrMFb)I=mR2306|`~W0s;sC1cC&}pIoa1JYpIG
zYSRK<^ODV;-gB3dq^2fhsxY7s+2JWD^maJL1c}Gr&K}~Tg&Y8rQY`Y{9XrZRBcuPtRgf9leasjrSHp^BjL35;gPQ5Wpl--Q^jUqk+6syd0wd4wO!6*sPS$(sj>g`^2I*dXh;
zk~F;lXXK!OSyd?)_)l7~F)3wUozJ-YC5gv?UALau7C;ZutPoy+Cqg+fD!A``RMoDa
zBmNdAWuk>N-hT7uBpDoy!Q9If^SJWskrh8<&Pn$HV%;rxQv^yCD~*<-V)hl{8t$gL
z>K71WPk(VP_dv(1*eA&SeHaIaWvvs>xfCX#*r$KGChd8Ji%N*$TojM_>rb{Ml_Z`<
zvn5a^jl#lJ*=An-)5D;UHWkrrE0nd_PP08iX;Zsmyu>eVx;zm)xM>z|
z&F>#F5LnsPf%(kO&G>K-!*QWmw
zLXar}nF>|r0?_{>3GU##kw`c#bFL`G0f+r?nqsW8e@BQ6ougPO)1{E{I*~xHrcr8>*~NOM)pU=9@N6FIx;*AG#uKRR
zywipVbRFcoR1Kl4?(+BwI8iAsR4I=W6Lq&f1<9gfZqPIdgm>Sh6D678<`eVH|tG
z+0R#1asQ`{6JHtGd+rcTB^(TB4j;^q8nCb~4~SvJC01_~0LRKZ(0
zo)pg<5$T<{QhKfA{n;Zf$|=!z;&zfDuqad4=HDGt(mGQW1i?^(aDeB-D$y$YxVs
z<5M*KwT7_dVdC#|flBmfBr79W)_)7)jVi=tiM~@+iE|%sGk%wNtU~KBVXSqeRi6P0
XPnqs*F|`t`? Wed, 29 Apr 2026 11:08:43 +0200
+
+libpgjava (42.7.10-1) unstable; urgency=medium
+
+ * New upstream version 42.7.10.
+
+ -- Christoph Berg Wed, 11 Feb 2026 20:38:12 +0100
+
+libpgjava (42.7.9-1) unstable; urgency=medium
+
+ * New upstream version 42.7.9.
+
+ -- Christoph Berg Sat, 17 Jan 2026 14:52:24 +0100
+
+libpgjava (42.7.8-2) unstable; urgency=medium
+
+ * Team upload.
+ * Ignore org.junit:junit-bom to avoid propagating it as a transitive
+ dependency to reverse build-deps through the pom. (Closes: #1121881)
+ * Bump Standards-Version to 4.7.2
+ * Update d/copyright Format URL
+ * Update debian/watch to version 5
+ * Remove Rules-Requires-Root from debian/control
+
+ -- tony mancill Wed, 03 Dec 2025 21:19:04 -0800
+
+libpgjava (42.7.8-1) unstable; urgency=medium
+
+ * New upstream version 42.7.8.
+
+ -- Christoph Berg Tue, 23 Sep 2025 20:18:01 +0000
+
+libpgjava (42.7.7-2) unstable; urgency=medium
+
+ * Rebuild against libscram-java 3.2-1. (CVE-2025-59432)
+
+ -- Christoph Berg Tue, 23 Sep 2025 18:14:27 +0000
+
+libpgjava (42.7.7-1) unstable; urgency=medium
+
+ * New upstream version 42.7.7.
+ Fixes CVE-2025-49146: When the PostgreSQL JDBC driver is configured with
+ channel binding set to required (default value is prefer), the driver
+ would incorrectly allow connections to proceed with authentication methods
+ that do not support channel binding (such as password, MD5, GSS, or SSPI
+ authentication). This could allow a man-in-the-middle attacker to
+ intercept connections that users believed were protected by channel
+ binding requirements.
+
+ -- Christoph Berg Fri, 13 Jun 2025 15:26:53 +0200
+
+libpgjava (42.7.6-1) experimental; urgency=medium
+
+ * New upstream version 42.7.6.
+
+ -- Christoph Berg Mon, 02 Jun 2025 17:05:52 +0200
+
+libpgjava (42.7.5-2) unstable; urgency=medium
+
+ * Fix org.postgresql.util.PSQLException: Unable to convert bytea parameter
+ at position 1 to literal. (Closes: #1098830)
+
+ -- Christoph Berg Thu, 17 Apr 2025 11:14:38 +0000
+
+libpgjava (42.7.5-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream release
+ - Refreshed the patches
+ * Removed the unused dependency on libxerces2-java
+
+ -- Emmanuel Bourg Mon, 10 Feb 2025 10:15:21 +0100
+
+libpgjava (42.7.3-2) unstable; urgency=medium
+
+ * Team upload.
+ * Removed the -java-doc package (Closes: #1088125)
+ * Standards-Version updated to 4.7.0
+
+ -- Emmanuel Bourg Mon, 09 Dec 2024 14:05:50 +0100
+
+libpgjava (42.7.3-1) unstable; urgency=medium
+
+ * New upstream version 42.7.3.
+
+ -- Christoph Berg Fri, 15 Mar 2024 15:13:39 +0100
+
+libpgjava (42.7.2-1) unstable; urgency=medium
+
+ * New upstream version 42.7.2. (CVE-2024-1597)
+
+ -- Christoph Berg Thu, 22 Feb 2024 11:46:47 +0100
+
+libpgjava (42.7.1-1) unstable; urgency=medium
+
+ * New upstream version 42.7.1.
+
+ -- Christoph Berg Thu, 07 Dec 2023 23:27:15 +0100
+
+libpgjava (42.7.0-1) unstable; urgency=medium
+
+ * New upstream version 42.7.0.
+
+ -- Christoph Berg Tue, 21 Nov 2023 10:31:51 +0100
+
+libpgjava (42.6.0-2) unstable; urgency=medium
+
+ * Remove ancient Replaces/Conflicts. Thanks Helmut Grohne for the report.
+
+ -- Christoph Berg Wed, 18 Oct 2023 14:05:07 +0200
+
+libpgjava (42.6.0-1) experimental; urgency=medium
+
+ * New upstream version 42.6.0.
+
+ -- Christoph Berg Mon, 27 Mar 2023 09:36:55 +0200
+
+libpgjava (42.5.4-1) unstable; urgency=medium
+
+ * New upstream version 42.5.4.
+
+ -- Christoph Berg Fri, 17 Feb 2023 18:19:35 +0100
+
+libpgjava (42.5.3-1) unstable; urgency=medium
+
+ * New upstream version 42.5.3.
+
+ -- Christoph Berg Thu, 09 Feb 2023 11:26:33 +0100
+
+libpgjava (42.5.1-1) unstable; urgency=medium
+
+ * New upstream version 42.5.1, fixes CVE-2022-41946.
+
+ -- Christoph Berg Thu, 24 Nov 2022 12:54:21 +0100
+
+libpgjava (42.5.0-1) unstable; urgency=medium
+
+ * New upstream version 42.5.0.
+
+ -- Christoph Berg Fri, 26 Aug 2022 12:06:57 +0200
+
+libpgjava (42.4.2-1) unstable; urgency=medium
+
+ * New upstream version 42.4.2.
+
+ -- Christoph Berg Mon, 22 Aug 2022 14:24:18 +0200
+
+libpgjava (42.4.1-1) unstable; urgency=medium
+
+ * New upstream version 42.4.1
+
+ Fixes SQL generated in PgResultSet.refresh() to escape column identifiers
+ so as to prevent SQL injection.
+ (Closes: #1016662, CVE-2022-31197, reported by Sho Kato)
+
+ Previously, the column names for both key and data columns in the table
+ were copied as-is into the generated SQL. This allowed a malicious table
+ with column names that include statement terminator to be parsed and
+ executed as multiple separate commands.
+
+ -- Christoph Berg Mon, 08 Aug 2022 14:53:28 +0200
+
+libpgjava (42.4.0-1) unstable; urgency=medium
+
+ * New upstream version 42.4.0.
+
+ -- Christoph Berg Tue, 14 Jun 2022 15:18:49 +0200
+
+libpgjava (42.3.6-1) unstable; urgency=medium
+
+ * New upstream version 42.3.6.
+
+ -- Christoph Berg Fri, 27 May 2022 14:56:40 +0200
+
+libpgjava (42.3.5-1) unstable; urgency=medium
+
+ * New upstream version 42.3.5.
+
+ -- Christoph Berg Fri, 06 May 2022 16:51:03 +0200
+
+libpgjava (42.3.4-1) unstable; urgency=medium
+
+ * New upstream version 42.3.4.
+
+ -- Christoph Berg Mon, 02 May 2022 15:56:41 +0200
+
+libpgjava (42.3.3-1) unstable; urgency=medium
+
+ * New upstream version 42.3.3.
+ https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-673j-qm5f-xpv8
+
+ -- Christoph Berg Thu, 17 Feb 2022 13:08:38 +0100
+
+libpgjava (42.3.2-1) unstable; urgency=medium
+
+ * New upstream version 42.3.2.
+ https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-v7wg-cpwc-24m4
+
+ -- Christoph Berg Fri, 04 Feb 2022 10:58:43 +0100
+
libpgjava (42.3.1-1) unstable; urgency=medium
* New upstream version 42.3.1.
@@ -292,7 +504,7 @@ libpgjava (8.0-312-1) unstable; urgency=low
* New upstream release
+ Supports Postgresql 7.2 up to 8.0 releases
- + Upstream supplies its own source packages for the jdbc driver
+ + Upstream supplies its own source packages for the jdbc driver
starting with release 8.0 - no longer extracted from postgresql
* Changed library name to comply with java policy (closes: #275417)
+ Added conflicts, replaces with old binary
@@ -332,15 +544,15 @@ libpgjava (7.4.7-2) unstable; urgency=low
* Added patch to fix jikes compile error in JDBC2 optional classes
(02_jikes_jdbc2-optional_compilefix.patch)
* Build JDBC2 with optional classes but without SSL support
- Building with SSL support would prevent running the JDBC2 driver on
- jdk1.3 runtimes without SSL extensions or using the -Xverify:none flag
+ Building with SSL support would prevent running the JDBC2 driver on
+ jdk1.3 runtimes without SSL extensions or using the -Xverify:none flag
* Build.xml (03_BuildXml.patch):
- Patched to allow explicit selection of JDBC specification
with -Djdbc2=true or -Djdbc3=true arguments during build
- Patched to allow explicit selection of SSL usage during
compile time with -Dssl=true (JDBC2 build has to be built without SSL)
- Patched to allow explicit selection of compile target version with
- -Dtarget=1.3 for JDBC2 and -Dtarget=1.4 for JDBC3
+ -Dtarget=1.3 for JDBC2 and -Dtarget=1.4 for JDBC3
-- Wolfgang Baer Tue, 19 Apr 2005 20:28:25 +0200
@@ -362,7 +574,7 @@ libpgjava (7.4.2-1) unstable; urgency=low
(closes: #238961)
* Updated debian/copyright
* Use libant1.6-java instead of libant1.5-java for building
- *
+ *
-- Stefan Gybas Mon, 19 Apr 2004 19:40:03 +0200
diff --git a/debian/control b/debian/control
index 302ee25..3f3fda5 100644
--- a/debian/control
+++ b/debian/control
@@ -12,16 +12,13 @@ Build-Depends:
libbuild-helper-maven-plugin-java,
libcomment-preprocessor-java,
libmaven-bundle-plugin-java,
- libmaven-javadoc-plugin-java,
libmaven-shade-plugin-java,
libproperties-maven-plugin-java,
libscram-java (>= 2.1),
maven-debian-helper
Build-Depends-Indep:
- default-jdk,
- default-jdk-doc,
- libxerces2-java
-Standards-Version: 4.5.0
+ default-jdk
+Standards-Version: 4.7.2
Vcs-Git: https://salsa.debian.org/java-team/libpostgresql-jdbc-java.git
Vcs-Browser: https://salsa.debian.org/java-team/libpostgresql-jdbc-java
Homepage: https://jdbc.postgresql.org/
@@ -32,29 +29,10 @@ Multi-Arch: foreign
Depends: ${maven:Depends}, ${misc:Depends}
# No "Recommends: ${maven:OptionalDepends}" here because that would point at
# libscram-java which we bundle
-Conflicts: libpgjava (<= 7.4.7-3), libpg-java (<= 9.1-901-1)
Provides: libpgjava, libpg-java
-Replaces: libpgjava, libpg-java (<= 9.1-901-1)
Built-Using: ${builtUsing}
Description: Java database (JDBC) driver for PostgreSQL
PostgreSQL JDBC Driver allows Java programs to connect to a PostgreSQL
database (8.4 or later) using standard, database independent Java code.
It is an open source JDBC driver written in Pure Java (Type 4), and
communicates in the PostgreSQL native network protocol.
-
-Package: libpostgresql-jdbc-java-doc
-Section: doc
-Architecture: all
-Multi-Arch: foreign
-Depends: ${maven:DocDepends}, ${misc:Depends}
-Recommends: ${maven:DocOptionalDepends}
-Conflicts: libpg-java-doc (<= 8.4-702-1)
-Provides: libpg-java-doc
-Replaces: libpg-java-doc
-Description: Java database (JDBC) driver for PostgreSQL (documentation)
- PostgreSQL JDBC Driver allows Java programs to connect to a PostgreSQL
- database (8.4 or later) using standard, database independent Java code.
- It is an open source JDBC driver written in Pure Java (Type 4), and
- communicates in the PostgreSQL native network protocol.
- .
- This package contains the documentation.
diff --git a/debian/copyright b/debian/copyright
index 16ddf36..23b37a3 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,4 +1,4 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Postgresql JDBC Driver
Source: https://github.com/pgjdbc/pgjdbc/
diff --git a/debian/libpostgresql-jdbc-java-doc.install b/debian/libpostgresql-jdbc-java-doc.install
deleted file mode 100644
index 71b6aec..0000000
--- a/debian/libpostgresql-jdbc-java-doc.install
+++ /dev/null
@@ -1 +0,0 @@
-target/site/apidocs/* usr/share/doc/libpostgresql-jdbc-java/api
diff --git a/debian/maven.ignoreRules b/debian/maven.ignoreRules
new file mode 100644
index 0000000..507d093
--- /dev/null
+++ b/debian/maven.ignoreRules
@@ -0,0 +1,2 @@
+* * * * * test
+org.junit junit-bom * * * *
diff --git a/debian/patches/02-scram-optional.patch b/debian/patches/02-scram-optional.patch
index 016f4f9..18fc61e 100644
--- a/debian/patches/02-scram-optional.patch
+++ b/debian/patches/02-scram-optional.patch
@@ -5,13 +5,13 @@ Bug: #900615
--- a/pom.xml
+++ b/pom.xml
-@@ -43,7 +43,8 @@
+@@ -57,7 +57,8 @@
com.ongres.scram
- client
-- 2.1
+ scram-client
+- 3.2
+ debian
+ true
- se.jiderhamn
+ org.junit.jupiter
diff --git a/debian/patches/missing-test-deps b/debian/patches/missing-test-deps
deleted file mode 100644
index 755c90c..0000000
--- a/debian/patches/missing-test-deps
+++ /dev/null
@@ -1,50 +0,0 @@
-Remove missing test dependencies
-
-classloader-leak-test-framework: Not packaged
-junit: Packaged, but mvn doesn't find it
-
---- a/pom.xml
-+++ b/pom.xml
-@@ -46,42 +46,6 @@
- debian
- true
-
--
-- se.jiderhamn
-- classloader-leak-test-framework
-- 1.1.1
-- test
--
--
-- junit
-- junit
-- 4.13
-- test
--
--
-- org.junit.jupiter
-- junit-jupiter-api
-- 5.6.0
-- test
--
--
-- org.junit.jupiter
-- junit-jupiter-params
-- 5.6.0
-- test
--
--
-- org.junit.jupiter
-- junit-jupiter-engine
-- 5.6.0
-- test
--
--
-- org.junit.vintage
-- junit-vintage-engine
-- 5.6.0
-- test
--
-
-
-
diff --git a/debian/patches/series b/debian/patches/series
index a747363..f179736 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1 @@
02-scram-optional.patch
-missing-test-deps
diff --git a/debian/rules b/debian/rules
index 8f10a21..41d00ea 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,14 +1,10 @@
#!/usr/bin/make -f
-# force doc build to be in English
-export LC_ALL=C.UTF-8
-
%:
dh $@
override_dh_auto_build:
dh_auto_build -- package
- dh_auto_build -- javadoc:javadoc
# defang package-contains-ancient-file
touch README.md
diff --git a/debian/watch b/debian/watch
index 384bb86..901c47c 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,2 +1,4 @@
-version=4
-https://repo1.maven.org/maven2/org/postgresql/postgresql/(\d[\d.]+)/postgresql-([\d.]+)-jdbc-src.tar.gz
+Version: 5
+
+Source: https://repo1.maven.org/maven2/org/postgresql/postgresql/(\d[\d.]+)/
+Matching-Pattern: postgresql-([\d.]+)-jdbc-src.tar.gz
diff --git a/pom.xml b/pom.xml
index a956792..c0b0192 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,12 +8,17 @@
org.postgresqlpostgresql
+ 42.7.11jarPostgreSQL JDBC Driver - JDBC 4.2
- 42.3.1Java JDBC 4.2 (JRE 8+) driver for PostgreSQL databasehttps://github.com/pgjdbc/pgjdbc
+
+ PostgreSQL Global Development Group
+ https://jdbc.postgresql.org/
+
+
BSD-2-Clause
@@ -21,64 +26,68 @@
-
- PostgreSQL Global Development Group
- https://jdbc.postgresql.org/
-
-
1.8
+ 8UTF-8${encoding}${encoding}${encoding}
- 3.8.1
+ 3.12.12.22.2
- 2.6
+ 3.3.03.0.1
+ true
+
+
+
+ org.junit
+ junit-bom
+ 5.14.3
+ pom
+ import
+
+
+
+
com.ongres.scram
- client
- 2.1
-
-
- se.jiderhamn
- classloader-leak-test-framework
- 1.1.1
- test
-
-
- junit
- junit
- 4.13
- test
+ scram-client
+ 3.2org.junit.jupiterjunit-jupiter-api
- 5.6.0testorg.junit.jupiterjunit-jupiter-params
- 5.6.0
+ test
+
+
+ org.junit.platform
+ junit-platform-launchertestorg.junit.jupiterjunit-jupiter-engine
- 5.6.0testorg.junit.vintagejunit-vintage-engine
- 5.6.0
+ test
+
+
+ uk.org.webcompere
+ system-stubs-jupiter
+ 2.1.8test
@@ -89,10 +98,6 @@
org.apache.maven.pluginsmaven-compiler-plugin${maven-compiler-plugin.version}
-
- ${javac.target}
- ${javac.target}
- maven-surefire-plugin
@@ -102,6 +107,12 @@
.
+
+
+ junit.jupiter.extensions.autodetection.enabled=true
+ junit.jupiter.execution.timeout.default=5 m
+
+
@@ -118,6 +129,53 @@
+
+ jdk8
+
+ 1.8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${javac.target}
+ ${javac.target}
+
+
+ org/postgresql/test/jdbc2/DriverTest.java
+ org/postgresql/util/OSUtilTest.java
+ org/postgresql/util/StubEnvironmentAndProperties.java
+ org/postgresql/jdbcurlresolver/PgPassParserTest.java
+ org/postgresql/jdbcurlresolver/PgServiceConfParserTest.java
+
+
+
+
+
+
+
+
+ jdkge11
+
+ [11,)
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java.target.release}
+
+
+
+
+
+
section-one is selected
+ # in case of duplicate key - first entry counts
+ # Line: "[service-one]"
+ # Line: "host=host-one"
+ # Line: "host=host-two"
+ # --> host-one is selected
+ # service name is case sensitive
+ # Line: "[service-one]"
+ # Line: "[service-ONE]"
+ # --> these are unique service names
+ # whatever is between brackets is considered as service name (including space)
+ # Line: "[ service-ONE]"
+ # Line: "[service-ONE ]"
+ # Line: "[service ONE]"
+ # --> these are unique service names
+ */
+ @SuppressWarnings("RedundantControlFlow")
+ private /* @Nullable */ Properties parseInputStream(InputStream inputStream) throws IOException {
+ // build set of allowed keys
+ Set allowedServiceKeys = Arrays.stream(PGProperty.values())
+ .map(PGProperty::getName)
+ .map(PGPropertyUtil::translatePGPropertyToPGService)
+ .collect(Collectors.toSet());
+
+ //
+ Properties result = new Properties();
+ boolean isFound = false;
+ try (
+ Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+ BufferedReader br = new BufferedReader(reader)) {
+ //
+ String originalLine;
+ String line;
+ int lineNumber = 0;
+ while ((originalLine = br.readLine()) != null) {
+ lineNumber++;
+ // remove spaces around it
+ line = originalLine.trim();
+ // skip if empty line or starts with comment sign
+ if (line.isEmpty() || line.startsWith("#")) {
+ continue;
+ }
+ // find first equal sign
+ int indexOfEqualSign = line.indexOf("=");
+ // is it section start?
+ if (line.startsWith("[") && line.endsWith("]")) {
+ // stop processing if section with correct name was found already
+ if (isFound) {
+ break;
+ }
+ // get name of section
+ String sectionName = line.substring(1, line.length() - 1);
+ // if match then mark it as section is found
+ if (serviceName.equals(sectionName)) {
+ isFound = true;
+ }
+ } else if (!isFound) {
+ // skip further processing until section is found
+ // TODO: avoid continue here to resolve https://errorprone.info/bugpattern/RedundantControlFlow
+ //noinspection UnnecessaryContinue
+ continue;
+ } else if (indexOfEqualSign > 1) {
+ // get key and value
+ String key = line.substring(0, indexOfEqualSign);
+ String value = line.substring(indexOfEqualSign + 1);
+ // check key against set of allowed keys
+ if (!allowedServiceKeys.contains(key)) {
+ // log list of allowed keys
+ String allowedValuesCommaSeparated =
+ allowedServiceKeys.stream().sorted().collect(Collectors.joining(","));
+ LOGGER.log(Level.SEVERE, "Got invalid key: line number [{0}], value [{1}], allowed "
+ + "values [{2}]",
+ new Object[]{lineNumber, originalLine, allowedValuesCommaSeparated});
+ // stop processing because of invalid key
+ return null;
+ }
+ // ignore line if value is missing
+ if (!value.isEmpty()) {
+ // ignore line having duplicate key, otherwise store key-value pair
+ result.putIfAbsent(PGPropertyUtil.translatePGServiceToPGProperty(key), value);
+ }
+ } else {
+ // if not equal sign then stop processing because of invalid syntax
+ LOGGER.log(Level.WARNING, "Not valid line: line number [{0}], value [{1}]",
+ new Object[]{lineNumber, originalLine});
+ return null;
+ }
+ }
+ }
+ // null means failure - service is not found
+ return isFound ? result : null;
+ }
+
+}
diff --git a/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java b/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
deleted file mode 100644
index 55d8ff7..0000000
--- a/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2017, PostgreSQL Global Development Group
- * See the LICENSE file in the project root for more information.
- */
-
-package org.postgresql.jre7.sasl;
-
-import static org.postgresql.util.internal.Nullness.castNonNull;
-
-import org.postgresql.core.PGStream;
-import org.postgresql.util.GT;
-import org.postgresql.util.PSQLException;
-import org.postgresql.util.PSQLState;
-
-import com.ongres.scram.client.ScramClient;
-import com.ongres.scram.client.ScramSession;
-import com.ongres.scram.common.exception.ScramException;
-import com.ongres.scram.common.exception.ScramInvalidServerSignatureException;
-import com.ongres.scram.common.exception.ScramParseException;
-import com.ongres.scram.common.exception.ScramServerErrorException;
-import com.ongres.scram.common.stringprep.StringPreparations;
-// import org.checkerframework.checker.nullness.qual.Nullable;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class ScramAuthenticator {
- private static final Logger LOGGER = Logger.getLogger(ScramAuthenticator.class.getName());
-
- private final String user;
- private final String password;
- private final PGStream pgStream;
- private /* @Nullable */ ScramClient scramClient;
- private /* @Nullable */ ScramSession scramSession;
- private /* @Nullable */ ScramSession.ClientFinalProcessor clientFinalProcessor;
-
- private interface BodySender {
- void sendBody(PGStream pgStream) throws IOException;
- }
-
- private void sendAuthenticationMessage(int bodyLength, BodySender bodySender)
- throws IOException {
- pgStream.sendChar('p');
- pgStream.sendInteger4(Integer.SIZE / Byte.SIZE + bodyLength);
- bodySender.sendBody(pgStream);
- pgStream.flush();
- }
-
- public ScramAuthenticator(String user, String password, PGStream pgStream) {
- this.user = user;
- this.password = password;
- this.pgStream = pgStream;
- }
-
- public void processServerMechanismsAndInit() throws IOException, PSQLException {
- List mechanisms = new ArrayList<>();
- do {
- mechanisms.add(pgStream.receiveString());
- } while (pgStream.peekChar() != 0);
- int c = pgStream.receiveChar();
- assert c == 0;
- if (mechanisms.size() < 1) {
- throw new PSQLException(
- GT.tr("No SCRAM mechanism(s) advertised by the server"),
- PSQLState.CONNECTION_REJECTED
- );
- }
-
- ScramClient scramClient;
- try {
- scramClient = ScramClient
- .channelBinding(ScramClient.ChannelBinding.NO)
- .stringPreparation(StringPreparations.SASL_PREPARATION)
- .selectMechanismBasedOnServerAdvertised(mechanisms.toArray(new String[]{}))
- .setup();
- } catch (IllegalArgumentException e) {
- throw new PSQLException(
- GT.tr("Invalid or unsupported by client SCRAM mechanisms", e),
- PSQLState.CONNECTION_REJECTED
- );
- }
- if (LOGGER.isLoggable(Level.FINEST)) {
- LOGGER.log(Level.FINEST, " Using SCRAM mechanism {0}", scramClient.getScramMechanism().getName());
- }
-
- this.scramClient = scramClient;
- scramSession =
- scramClient.scramSession("*"); // Real username is ignored by server, uses startup one
- }
-
- public void sendScramClientFirstMessage() throws IOException {
- ScramSession scramSession = this.scramSession;
- String clientFirstMessage = castNonNull(scramSession).clientFirstMessage();
- LOGGER.log(Level.FINEST, " FE=> SASLInitialResponse( {0} )", clientFirstMessage);
-
- ScramClient scramClient = this.scramClient;
- String scramMechanismName = castNonNull(scramClient).getScramMechanism().getName();
- final byte[] scramMechanismNameBytes = scramMechanismName.getBytes(StandardCharsets.UTF_8);
- final byte[] clientFirstMessageBytes = clientFirstMessage.getBytes(StandardCharsets.UTF_8);
- sendAuthenticationMessage(
- (scramMechanismNameBytes.length + 1) + 4 + clientFirstMessageBytes.length,
- new BodySender() {
- @Override
- public void sendBody(PGStream pgStream) throws IOException {
- pgStream.send(scramMechanismNameBytes);
- pgStream.sendChar(0); // List terminated in '\0'
- pgStream.sendInteger4(clientFirstMessageBytes.length);
- pgStream.send(clientFirstMessageBytes);
- }
- }
- );
- }
-
- public void processServerFirstMessage(int length) throws IOException, PSQLException {
- String serverFirstMessage = pgStream.receiveString(length);
- LOGGER.log(Level.FINEST, " <=BE AuthenticationSASLContinue( {0} )", serverFirstMessage);
-
- ScramSession scramSession = this.scramSession;
- if (scramSession == null) {
- throw new PSQLException(
- GT.tr("SCRAM session does not exist"),
- PSQLState.UNKNOWN_STATE
- );
- }
-
- ScramSession.ServerFirstProcessor serverFirstProcessor;
- try {
- serverFirstProcessor = scramSession.receiveServerFirstMessage(serverFirstMessage);
- } catch (ScramException e) {
- throw new PSQLException(
- GT.tr("Invalid server-first-message: {0}", serverFirstMessage),
- PSQLState.CONNECTION_REJECTED,
- e
- );
- }
- if (LOGGER.isLoggable(Level.FINEST)) {
- LOGGER.log(Level.FINEST,
- " <=BE AuthenticationSASLContinue(salt={0}, iterations={1})",
- new Object[] { serverFirstProcessor.getSalt(), serverFirstProcessor.getIteration() }
- );
- }
-
- clientFinalProcessor = serverFirstProcessor.clientFinalProcessor(password);
-
- String clientFinalMessage = clientFinalProcessor.clientFinalMessage();
- LOGGER.log(Level.FINEST, " FE=> SASLResponse( {0} )", clientFinalMessage);
-
- final byte[] clientFinalMessageBytes = clientFinalMessage.getBytes(StandardCharsets.UTF_8);
- sendAuthenticationMessage(
- clientFinalMessageBytes.length,
- new BodySender() {
- @Override
- public void sendBody(PGStream pgStream) throws IOException {
- pgStream.send(clientFinalMessageBytes);
- }
- }
- );
- }
-
- public void verifyServerSignature(int length) throws IOException, PSQLException {
- String serverFinalMessage = pgStream.receiveString(length);
- LOGGER.log(Level.FINEST, " <=BE AuthenticationSASLFinal( {0} )", serverFinalMessage);
-
- ScramSession.ClientFinalProcessor clientFinalProcessor = this.clientFinalProcessor;
- if (clientFinalProcessor == null) {
- throw new PSQLException(
- GT.tr("SCRAM client final processor does not exist"),
- PSQLState.UNKNOWN_STATE
- );
- }
- try {
- clientFinalProcessor.receiveServerFinalMessage(serverFinalMessage);
- } catch (ScramParseException e) {
- throw new PSQLException(
- GT.tr("Invalid server-final-message: {0}", serverFinalMessage),
- PSQLState.CONNECTION_REJECTED,
- e
- );
- } catch (ScramServerErrorException e) {
- throw new PSQLException(
- GT.tr("SCRAM authentication failed, server returned error: {0}",
- e.getError().getErrorMessage()),
- PSQLState.CONNECTION_REJECTED,
- e
- );
- } catch (ScramInvalidServerSignatureException e) {
- throw new PSQLException(
- GT.tr("Invalid server SCRAM signature"),
- PSQLState.CONNECTION_REJECTED,
- e
- );
- }
- }
-}
diff --git a/src/main/java/org/postgresql/largeobject/BlobInputStream.java b/src/main/java/org/postgresql/largeobject/BlobInputStream.java
index 314da8c..13fe16b 100644
--- a/src/main/java/org/postgresql/largeobject/BlobInputStream.java
+++ b/src/main/java/org/postgresql/largeobject/BlobInputStream.java
@@ -5,6 +5,9 @@
package org.postgresql.largeobject;
+import org.postgresql.jdbc.ResourceLock;
+import org.postgresql.util.GT;
+
// import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
@@ -15,15 +18,19 @@
* This is an implementation of an InputStream from a large object.
*/
public class BlobInputStream extends InputStream {
+ static final int DEFAULT_MAX_BUFFER_SIZE = 512 * 1024;
+ static final int INITIAL_BUFFER_SIZE = 64 * 1024;
+
/**
* The parent LargeObject.
*/
private /* @Nullable */ LargeObject lo;
+ private final ResourceLock lock = new ResourceLock();
/**
* The absolute position.
*/
- private long apos;
+ private long absolutePosition;
/**
* Buffer used to improve performance.
@@ -33,28 +40,34 @@ public class BlobInputStream extends InputStream {
/**
* Position within buffer.
*/
- private int bpos;
+ private int bufferPosition;
+
+ /**
+ * The amount of bytes to read on the next read.
+ * Currently, we nullify {@link #buffer}, so we can't use {@code buffer.length}.
+ */
+ private int lastBufferSize;
/**
* The buffer size.
*/
- private int bsize;
+ private final int maxBufferSize;
/**
* The mark position.
*/
- private long mpos = 0;
+ private long markPosition;
/**
* The limit.
*/
- private long limit = -1;
+ private final long limit;
/**
* @param lo LargeObject to read from
*/
public BlobInputStream(LargeObject lo) {
- this(lo, 1024);
+ this(lo, DEFAULT_MAX_BUFFER_SIZE);
}
/**
@@ -63,7 +76,7 @@ public BlobInputStream(LargeObject lo) {
*/
public BlobInputStream(LargeObject lo, int bsize) {
- this(lo, bsize, -1);
+ this(lo, bsize, Long.MAX_VALUE);
}
/**
@@ -73,68 +86,188 @@ public BlobInputStream(LargeObject lo, int bsize) {
*/
public BlobInputStream(LargeObject lo, int bsize, long limit) {
this.lo = lo;
- buffer = null;
- bpos = 0;
- apos = 0;
- this.bsize = bsize;
- this.limit = limit;
+ this.maxBufferSize = bsize;
+ // The very first read multiplies the last buffer size by two, so we divide by two to get
+ // the first read to be exactly the initial buffer size
+ this.lastBufferSize = INITIAL_BUFFER_SIZE / 2;
+ // Treat -1 as no limit for backward compatibility
+ this.limit = limit == -1 ? Long.MAX_VALUE : limit;
}
/**
* The minimum required to implement input stream.
*/
- public int read() throws java.io.IOException {
- LargeObject lo = getLo();
- try {
- if (limit > 0 && apos >= limit) {
+ @Override
+ public int read() throws IOException {
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = getLo();
+ if (absolutePosition >= limit) {
+ buffer = null;
+ bufferPosition = 0;
return -1;
}
- if (buffer == null || bpos >= buffer.length) {
- buffer = lo.read(bsize);
- bpos = 0;
+ // read more in if necessary
+ if (buffer == null || bufferPosition >= buffer.length) {
+ // Don't hold the buffer while waiting for DB to respond
+ // Note: lo.read(...) does not support "fetching the response into the user-provided buffer"
+ // See https://github.com/pgjdbc/pgjdbc/issues/3043
+ int nextBufferSize = getNextBufferSize(1);
+ buffer = lo.read(nextBufferSize);
+ bufferPosition = 0;
+
+ if (buffer.length == 0) {
+ // The lob does not produce any more data, so we are at the end of the stream
+ return -1;
+ }
}
- // Handle EOF
- if (buffer == null || bpos >= buffer.length) {
- return -1;
+ int ret = buffer[bufferPosition] & 0xFF;
+
+ bufferPosition++;
+ absolutePosition++;
+ if (bufferPosition >= buffer.length) {
+ // TODO: support buffer reuse in mark/reset
+ buffer = null;
+ bufferPosition = 0;
}
- int ret = (buffer[bpos] & 0x7F);
- if ((buffer[bpos] & 0x80) == 0x80) {
- ret |= 0x80;
+ return ret;
+ } catch (SQLException e) {
+ long loId = lo == null ? -1 : lo.getLongOID();
+ throw new IOException(
+ GT.tr("Can not read data from large object {0}, position: {1}, buffer size: {2}",
+ loId, absolutePosition, lastBufferSize),
+ e);
+ }
+ }
+
+ /**
+ * Computes the next buffer size to use for reading data from the large object.
+ * The idea is to avoid allocating too much memory, especially if the user will use just a few
+ * bytes of the data.
+ * @param len estimated read request
+ * @return next buffer size or {@link #maxBufferSize} if the buffer should not be increased
+ */
+ private int getNextBufferSize(int len) {
+ int nextBufferSize = Math.min(maxBufferSize, this.lastBufferSize * 2);
+ if (len > nextBufferSize) {
+ nextBufferSize = Math.min(maxBufferSize, Integer.highestOneBit(len * 2));
+ }
+ this.lastBufferSize = nextBufferSize;
+ return nextBufferSize;
+ }
+
+ @Override
+ public int read(byte[] dest, int off, int len) throws IOException {
+ if (len == 0) {
+ return 0;
+ }
+ try (ResourceLock ignore = lock.obtain()) {
+ int bytesCopied = 0;
+ LargeObject lo = getLo();
+
+ // Check to make sure we aren't at the limit.
+ if (absolutePosition >= limit) {
+ return -1;
}
- bpos++;
- apos++;
+ // Check to make sure we are not going to read past the limit
+ len = Math.min(len, (int) Math.min(limit - absolutePosition, Integer.MAX_VALUE));
- return ret;
- } catch (SQLException se) {
- throw new IOException(se.toString());
+ // have we read anything into the buffer
+ if (buffer != null) {
+ // now figure out how much data is in the buffer
+ int bytesInBuffer = buffer.length - bufferPosition;
+ // figure out how many bytes the user wants
+ int bytesToCopy = Math.min(len, bytesInBuffer);
+ // copy them in
+ System.arraycopy(buffer, bufferPosition, dest, off, bytesToCopy);
+ // move the buffer position
+ bufferPosition += bytesToCopy;
+ if (bufferPosition >= buffer.length) {
+ // TODO: support buffer reuse in mark/reset
+ buffer = null;
+ bufferPosition = 0;
+ }
+ // position in the blob
+ absolutePosition += bytesToCopy;
+ // increment offset
+ off += bytesToCopy;
+ // decrement the length
+ len -= bytesToCopy;
+ bytesCopied = bytesToCopy;
+ }
+
+ if (len > 0) {
+ int nextBufferSize = getNextBufferSize(len);
+ // We are going to read data past the existing buffer, so we release the memory
+ // before making a DB call
+ buffer = null;
+ bufferPosition = 0;
+ int bytesRead;
+ try {
+ if (len >= nextBufferSize) {
+ // Read directly into the user's buffer
+ bytesRead = lo.read(dest, off, len);
+ } else {
+ // Refill the buffer and copy from it
+ buffer = lo.read(nextBufferSize);
+ // Note that actual number of bytes read may be less than requested
+ bytesRead = Math.min(len, buffer.length);
+ System.arraycopy(buffer, 0, dest, off, bytesRead);
+ // If we at the end of the stream, and we just copied the last bytes,
+ // we can release the buffer
+ if (bytesRead == buffer.length) {
+ // TODO: if we want to reuse the buffer in mark/reset we should not release the
+ // buffer here
+ buffer = null;
+ bufferPosition = 0;
+ } else {
+ bufferPosition = bytesRead;
+ }
+ }
+ } catch (SQLException ex) {
+ throw new IOException(
+ GT.tr("Can not read data from large object {0}, position: {1}, buffer size: {2}",
+ lo.getLongOID(), absolutePosition, len),
+ ex);
+ }
+ bytesCopied += bytesRead;
+ absolutePosition += bytesRead;
+ }
+ return bytesCopied == 0 ? -1 : bytesCopied;
}
}
/**
- *
Closes this input stream and releases any system resources associated with the stream.
+ * Closes this input stream and releases any system resources associated with the stream.
*
*
The close method of InputStream does nothing.
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
public void close() throws IOException {
- if (lo != null) {
- try {
+ long loId = 0;
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = this.lo;
+ if (lo != null) {
+ loId = lo.getLongOID();
lo.close();
- lo = null;
- } catch (SQLException se) {
- throw new IOException(se.toString());
}
+ this.lo = null;
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not close large object {0}",
+ loId),
+ e);
}
}
/**
- *
Marks the current position in this input stream. A subsequent call to the reset
+ * Marks the current position in this input stream. A subsequent call to the reset
* method repositions this stream at the last marked position so that subsequent reads re-read the
- * same bytes.
+ * same bytes.
*
*
The readlimit arguments tells this input stream to allow that many bytes to be
* read before the mark position gets invalidated.
@@ -152,8 +285,11 @@ public void close() throws IOException {
* invalid.
* @see java.io.InputStream#reset()
*/
- public synchronized void mark(int readlimit) {
- mpos = apos;
+ @Override
+ public void mark(int readlimit) {
+ try (ResourceLock ignore = lock.obtain()) {
+ markPosition = absolutePosition;
+ }
}
/**
@@ -163,18 +299,25 @@ public synchronized void mark(int readlimit) {
* @see java.io.InputStream#mark(int)
* @see java.io.IOException
*/
- public synchronized void reset() throws IOException {
- LargeObject lo = getLo();
- try {
- if (mpos <= Integer.MAX_VALUE) {
- lo.seek((int)mpos);
- } else {
- lo.seek64(mpos, LargeObject.SEEK_SET);
+ @Override
+ public void reset() throws IOException {
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = getLo();
+ long loId = lo.getLongOID();
+ try {
+ if (markPosition <= Integer.MAX_VALUE) {
+ lo.seek((int) markPosition);
+ } else {
+ lo.seek64(markPosition, LargeObject.SEEK_SET);
+ }
+ buffer = null;
+ absolutePosition = markPosition;
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not reset stream for large object {0} to position {1}",
+ loId, markPosition),
+ e);
}
- buffer = null;
- apos = mpos;
- } catch (SQLException se) {
- throw new IOException(se.toString());
}
}
@@ -187,6 +330,7 @@ public synchronized void reset() throws IOException {
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
+ @Override
public boolean markSupported() {
return true;
}
diff --git a/src/main/java/org/postgresql/largeobject/BlobOutputStream.java b/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
index ad81b43..dcb6f50 100644
--- a/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
+++ b/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
@@ -5,35 +5,44 @@
package org.postgresql.largeobject;
+import org.postgresql.jdbc.ResourceLock;
+import org.postgresql.util.ByteStreamWriter;
+import org.postgresql.util.GT;
+
+// import org.checkerframework.checker.index.qual.Positive;
// import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.sql.SQLException;
/**
* This implements a basic output stream that writes to a LargeObject.
*/
public class BlobOutputStream extends OutputStream {
+ static final int DEFAULT_MAX_BUFFER_SIZE = 512 * 1024;
+
/**
* The parent LargeObject.
*/
private /* @Nullable */ LargeObject lo;
+ private final ResourceLock lock = new ResourceLock();
/**
* Buffer.
*/
- private byte[] buf;
+ private byte /* @Nullable */ [] buf;
/**
* Size of the buffer (default 1K).
*/
- private int bsize;
+ private final /* @Positive */ int maxBufferSize;
/**
* Position within the buffer.
*/
- private int bpos;
+ private int bufferPosition;
/**
* Create an OutputStream to a large object.
@@ -41,50 +50,146 @@ public class BlobOutputStream extends OutputStream {
* @param lo LargeObject
*/
public BlobOutputStream(LargeObject lo) {
- this(lo, 1024);
+ this(lo, DEFAULT_MAX_BUFFER_SIZE);
}
/**
* Create an OutputStream to a large object.
*
* @param lo LargeObject
- * @param bsize The size of the buffer used to improve performance
+ * @param bufferSize The size of the buffer for single-byte writes
*/
- public BlobOutputStream(LargeObject lo, int bsize) {
+ public BlobOutputStream(LargeObject lo, int bufferSize) {
this.lo = lo;
- this.bsize = bsize;
- buf = new byte[bsize];
- bpos = 0;
+ // Avoid "0" buffer size, and ensure the bufferSize will always be a power of two
+ this.maxBufferSize = Integer.highestOneBit(Math.max(bufferSize, 1));
+ }
+
+ /**
+ * Grows an internal buffer to ensure the extra bytes fit in the buffer.
+ * @param extraBytes the number of extra bytes that should fit in the buffer
+ * @return new buffer
+ */
+ private byte[] growBuffer(int extraBytes) {
+ byte[] buf = this.buf;
+ if (buf != null && (buf.length == maxBufferSize || buf.length - bufferPosition >= extraBytes)) {
+ // Buffer is already large enough
+ return buf;
+ }
+ // We use power-of-two buffers, so they align nicely with PostgreSQL's LargeObject slicing
+ // By default PostgreSQL slices the data in 2KiB chunks
+ int newSize = Math.min(maxBufferSize, Integer.highestOneBit(bufferPosition + extraBytes) * 2);
+ byte[] newBuffer = new byte[newSize];
+ if (buf != null && bufferPosition != 0) {
+ // There was some data in the old buffer, copy it over
+ System.arraycopy(buf, 0, newBuffer, 0, bufferPosition);
+ }
+ this.buf = newBuffer;
+ return newBuffer;
}
- public void write(int b) throws java.io.IOException {
- LargeObject lo = checkClosed();
- try {
- if (bpos >= bsize) {
+ @Override
+ public void write(int b) throws IOException {
+ long loId = 0;
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = checkClosed();
+ loId = lo.getLongOID();
+ byte[] buf = growBuffer(16);
+ if (bufferPosition >= buf.length) {
lo.write(buf);
- bpos = 0;
+ bufferPosition = 0;
}
- buf[bpos++] = (byte) b;
- } catch (SQLException se) {
- throw new IOException(se.toString());
+ buf[bufferPosition++] = (byte) b;
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not write data to large object {0}, requested write length: {1}",
+ loId, 1),
+ e);
}
}
- public void write(byte[] buf, int off, int len) throws java.io.IOException {
- LargeObject lo = checkClosed();
- try {
- // If we have any internally buffered data, send it first
- if (bpos > 0) {
- flush();
- }
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ long loId = 0;
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = checkClosed();
+ loId = lo.getLongOID();
+ byte[] buf = this.buf;
+ int totalData = bufferPosition + len;
+ // We have two parts of the data (it goes sequentially):
+ // 1) Data in buf at positions [0, bufferPosition)
+ // 2) Data in b at positions [off, off + len)
+ // If the new data fits into the buffer, we just copy it there.
+ // Otherwise, it might sound nice idea to just write them to the database, unfortunately,
+ // it is not optimal, as PostgreSQL chunks LargeObjects into 2KiB rows.
+ // That is why we would like to avoid writing a part of 2KiB chunk, and then issue overwrite
+ // causing DB to load and update the row.
+ //
+ // In fact, LOBLKSIZE is BLCKSZ/4, so users might have different values, so we use
+ // 8KiB write alignment for larger buffer sizes just in case.
+ //
+ // | buf[0] ... buf[bufferPosition] | b[off] ... b[off + len] |
+ // |<----------------- totalData ---------------------------->|
+ // If the total data does not align with 2048, we might have some remainder that we will
+ // copy to the beginning of the buffer and write later.
+ // The remainder can fall into either b (e.g. if the requested len is big enough):
+ //
+ // | buf[0] ... buf[bufferPosition] | b[off] ........ b[off + len] |
+ // |<----------------- totalData --------------------------------->|
+ // |<-------writeFromBuf----------->|<-writeFromB->|<--tailLength->|
+ //
+ // or
+ // buf (e.g. if the requested write len is small yet it does not fit into the max buffer size):
+ // | buf[0] .................... buf[bufferPosition] | b[off] .. b[off + len] |
+ // |<----------------- totalData -------------------------------------------->|
+ // |<-------writeFromBuf---------------->|<--------tailLength---------------->|
+ // "writeFromB" will be zero in that case
+
+ // We want aligned writes, so the write requests chunk nicely into large object rows
+ int tailLength =
+ maxBufferSize >= 8192 ? totalData % 8192 : (
+ maxBufferSize >= 2048 ? totalData % 2048 : 0
+ );
- if (off == 0 && len == buf.length) {
- lo.write(buf); // save a buffer creation and copy since full buffer written
- } else {
- lo.write(buf, off, len);
+ if (totalData >= maxBufferSize) {
+ // The resulting data won't fit into the buffer, so we flush the data to the database
+ int writeFromBuffer = Math.min(bufferPosition, totalData - tailLength);
+ int writeFromB = Math.max(0, totalData - writeFromBuffer - tailLength);
+ if (buf == null || bufferPosition <= 0) {
+ // The buffer is empty, so we can write the data directly
+ lo.write(b, off, writeFromB);
+ } else {
+ if (writeFromB == 0) {
+ lo.write(buf, 0, writeFromBuffer);
+ } else {
+ lo.write(
+ ByteStreamWriter.of(
+ ByteBuffer.wrap(buf, 0, writeFromBuffer),
+ ByteBuffer.wrap(b, off, writeFromB)));
+ }
+ // There might be some data left in the buffer since we keep the tail
+ if (writeFromBuffer >= bufferPosition) {
+ // The buffer was fully written to the database
+ bufferPosition = 0;
+ } else {
+ // Copy the rest to the beginning
+ System.arraycopy(buf, writeFromBuffer, buf, 0, bufferPosition - writeFromBuffer);
+ bufferPosition -= writeFromBuffer;
+ }
+ }
+ len -= writeFromB;
+ off += writeFromB;
+ }
+ if (len > 0) {
+ buf = growBuffer(len);
+ System.arraycopy(b, off, buf, bufferPosition, len);
+ bufferPosition += len;
}
- } catch (SQLException se) {
- throw new IOException(se.toString());
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not write data to large object {0}, requested write length: {1}",
+ loId, len),
+ e);
}
}
@@ -96,28 +201,41 @@ public void write(byte[] buf, int off, int len) throws java.io.IOException {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
public void flush() throws IOException {
- LargeObject lo = checkClosed();
- try {
- if (bpos > 0) {
- lo.write(buf, 0, bpos);
+ long loId = 0;
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = checkClosed();
+ loId = lo.getLongOID();
+ byte[] buf = this.buf;
+ if (buf != null && bufferPosition > 0) {
+ lo.write(buf, 0, bufferPosition);
}
- bpos = 0;
- } catch (SQLException se) {
- throw new IOException(se.toString());
+ bufferPosition = 0;
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not flush large object {0}",
+ loId),
+ e);
}
}
+ @Override
public void close() throws IOException {
- LargeObject lo = this.lo;
- if (lo != null) {
- try {
+ long loId = 0;
+ try (ResourceLock ignore = lock.obtain()) {
+ LargeObject lo = this.lo;
+ if (lo != null) {
+ loId = lo.getLongOID();
flush();
lo.close();
this.lo = null;
- } catch (SQLException se) {
- throw new IOException(se.toString());
}
+ } catch (SQLException e) {
+ throw new IOException(
+ GT.tr("Can not close large object {0}",
+ loId),
+ e);
}
}
diff --git a/src/main/java/org/postgresql/largeobject/LargeObject.java b/src/main/java/org/postgresql/largeobject/LargeObject.java
index 646da3a..e4b06a1 100644
--- a/src/main/java/org/postgresql/largeobject/LargeObject.java
+++ b/src/main/java/org/postgresql/largeobject/LargeObject.java
@@ -5,11 +5,11 @@
package org.postgresql.largeobject;
-import static org.postgresql.util.internal.Nullness.castNonNull;
-
import org.postgresql.core.BaseConnection;
import org.postgresql.fastpath.Fastpath;
import org.postgresql.fastpath.FastpathArg;
+import org.postgresql.util.ByteStreamWriter;
+import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
@@ -21,8 +21,8 @@
import java.sql.SQLException;
/**
- *
This class provides the basic methods required to run the interface, plus a pair of methods that
- * provide InputStream and OutputStream classes for this object.
+ * This class provides the basic methods required to run the interface, plus a pair of methods that
+ * provide InputStream and OutputStream classes for this object.
*
*
Normally, client code would use the getAsciiStream, getBinaryStream, or getUnicodeStream methods
* in ResultSet, or setAsciiStream, setBinaryStream, or setUnicodeStream methods in
@@ -42,9 +42,10 @@
* @see java.sql.PreparedStatement#setBinaryStream
* @see java.sql.PreparedStatement#setUnicodeStream
*/
+@SuppressWarnings("deprecation") // support for deprecated Fastpath API
public class LargeObject
- implements AutoCloseable
- /* hi, checkstyle */ {
+ implements AutoCloseable {
+
/**
* Indicates a seek from the beginning of a file.
*/
@@ -60,6 +61,8 @@ public class LargeObject
*/
public static final int SEEK_END = 2;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
private final Fastpath fp; // Fastpath API to use
private final long oid; // OID of this object
private final int mode; // read/write mode of this object
@@ -67,13 +70,25 @@ public class LargeObject
private /* @Nullable */ BlobOutputStream os; // The current output stream
- private boolean closed = false; // true when we are closed
+ private boolean closed; // true when we are closed
private /* @Nullable */ BaseConnection conn; // Only initialized when open a LOB with CommitOnClose
private final boolean commitOnClose; // Only initialized when open a LOB with CommitOnClose
/**
- *
This opens a large object.
+ * Checks if this LargeObject is closed and throws an exception if it is.
+ *
+ * @throws SQLException if this LargeObject has been closed
+ */
+ private void checkClosed() throws SQLException {
+ if (closed) {
+ throw new PSQLException(GT.tr("This large object has been closed."),
+ PSQLState.OBJECT_NOT_IN_STATE);
+ }
+ }
+
+ /**
+ * This opens a large object.
*
*
If the object does not exist, then an SQLException is thrown.
*
@@ -106,7 +121,7 @@ protected LargeObject(Fastpath fp, long oid, int mode,
}
/**
- *
This opens a large object.
+ * This opens a large object.
*
*
If the object does not exist, then an SQLException is thrown.
*
@@ -121,20 +136,10 @@ protected LargeObject(Fastpath fp, long oid, int mode) throws SQLException {
}
public LargeObject copy() throws SQLException {
+ checkClosed();
return new LargeObject(fp, oid, mode);
}
- /*
- * Release large object resources during garbage cleanup.
- *
- * This code used to call close() however that was problematic because the scope of the fd is a
- * transaction, thus if commit or rollback was called before garbage collection ran then the call
- * to close would error out with an invalid large object handle. So this method now does nothing
- * and lets the server handle cleanup when it ends the transaction.
- *
- * protected void finalize() throws SQLException { }
- */
-
/**
* @return the OID of this LargeObject
* @deprecated As of 8.3, replaced by {@link #getLongOID()}
@@ -156,29 +161,31 @@ public long getLongOID() {
*
* @throws SQLException if a database-access error occurs.
*/
+ @Override
public void close() throws SQLException {
- if (!closed) {
- // flush any open output streams
- if (os != null) {
- try {
- // we can't call os.close() otherwise we go into an infinite loop!
- os.flush();
- } catch (IOException ioe) {
- throw new PSQLException("Exception flushing output stream", PSQLState.DATA_ERROR, ioe);
- } finally {
- os = null;
- }
+ if (closed) {
+ return;
+ }
+ closed = true;
+ // flush any open output streams
+ if (os != null) {
+ try {
+ // we can't call os.close() otherwise we go into an infinite loop!
+ os.flush();
+ } catch (IOException ioe) {
+ throw new PSQLException("Exception flushing output stream", PSQLState.DATA_ERROR, ioe);
+ } finally {
+ os = null;
}
+ }
- // finally close
- FastpathArg[] args = new FastpathArg[1];
- args[0] = new FastpathArg(fd);
- fp.fastpath("lo_close", args); // true here as we dont care!!
- closed = true;
- BaseConnection conn = this.conn;
- if (this.commitOnClose && conn != null) {
- conn.commit();
- }
+ // finally close
+ FastpathArg[] args = new FastpathArg[1];
+ args[0] = new FastpathArg(fd);
+ fp.fastpath("lo_close", args); // true here as we dont care!!
+ BaseConnection conn = this.conn;
+ if (this.commitOnClose && conn != null) {
+ conn.commit();
}
}
@@ -190,12 +197,17 @@ public void close() throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public byte[] read(int len) throws SQLException {
+ checkClosed();
// This is the original method, where the entire block (len bytes)
// is retrieved in one go.
FastpathArg[] args = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(len);
- return castNonNull(fp.getData("loread", args));
+ byte[] bytes = fp.getData("loread", args);
+ if (bytes == null) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ return bytes;
}
/**
@@ -208,13 +220,12 @@ public byte[] read(int len) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public int read(byte[] buf, int off, int len) throws SQLException {
+ checkClosed();
byte[] b = read(len);
- if (b == null) {
+ if (b.length == 0) {
return 0;
}
- if (b.length < len) {
- len = b.length;
- }
+ len = Math.min(len, b.length);
System.arraycopy(b, 0, buf, off, len);
return len;
}
@@ -226,6 +237,7 @@ public int read(byte[] buf, int off, int len) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public void write(byte[] buf) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(buf);
@@ -241,6 +253,7 @@ public void write(byte[] buf) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public void write(byte[] buf, int off, int len) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(buf, off, len);
@@ -248,7 +261,21 @@ public void write(byte[] buf, int off, int len) throws SQLException {
}
/**
- *
Sets the current position within the object.
+ * Writes some data from a given writer to the object.
+ *
+ * @param writer the source of the data to write
+ * @throws SQLException if a database-access error occurs.
+ */
+ public void write(ByteStreamWriter writer) throws SQLException {
+ checkClosed();
+ FastpathArg[] args = new FastpathArg[2];
+ args[0] = new FastpathArg(fd);
+ args[1] = FastpathArg.of(writer);
+ fp.fastpath("lowrite", args);
+ }
+
+ /**
+ * Sets the current position within the object.
*
*
This is similar to the fseek() call in the standard C library. It allows you to have random
* access to the large object.
@@ -258,6 +285,7 @@ public void write(byte[] buf, int off, int len) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public void seek(int pos, int ref) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[3];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(pos);
@@ -273,6 +301,7 @@ public void seek(int pos, int ref) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public void seek64(long pos, int ref) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[3];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(pos);
@@ -281,7 +310,7 @@ public void seek64(long pos, int ref) throws SQLException {
}
/**
- *
Sets the current position within the object.
+ * Sets the current position within the object.
*
*
This is similar to the fseek() call in the standard C library. It allows you to have random
* access to the large object.
@@ -290,6 +319,7 @@ public void seek64(long pos, int ref) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public void seek(int pos) throws SQLException {
+ checkClosed();
seek(pos, SEEK_SET);
}
@@ -298,6 +328,7 @@ public void seek(int pos) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public int tell() throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[1];
args[0] = new FastpathArg(fd);
return fp.getInteger("lo_tell", args);
@@ -308,14 +339,15 @@ public int tell() throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public long tell64() throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[1];
args[0] = new FastpathArg(fd);
return fp.getLong("lo_tell64", args);
}
/**
- *
This method is inefficient, as the only way to find out the size of the object is to seek to
- * the end, record the current position, then return to the original position.
+ * This method is inefficient, as the only way to find out the size of the object is to seek to
+ * the end, record the current position, then return to the original position.
*
*
A better method will be found in the future.
*
@@ -323,6 +355,7 @@ public long tell64() throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public int size() throws SQLException {
+ checkClosed();
int cp = tell();
seek(0, SEEK_END);
int sz = tell();
@@ -337,6 +370,7 @@ public int size() throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public long size64() throws SQLException {
+ checkClosed();
long cp = tell64();
seek64(0, SEEK_END);
long sz = tell64();
@@ -353,6 +387,7 @@ public long size64() throws SQLException {
* @throws SQLException if something goes wrong
*/
public void truncate(int len) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(len);
@@ -368,6 +403,7 @@ public void truncate(int len) throws SQLException {
* @throws SQLException if something goes wrong
*/
public void truncate64(long len) throws SQLException {
+ checkClosed();
FastpathArg[] args = new FastpathArg[2];
args[0] = new FastpathArg(fd);
args[1] = new FastpathArg(len);
@@ -375,7 +411,7 @@ public void truncate64(long len) throws SQLException {
}
/**
- *
Returns an {@link InputStream} from this object.
+ * Returns an {@link InputStream} from this object.
*
*
This {@link InputStream} can then be used in any method that requires an InputStream.
*
@@ -383,7 +419,8 @@ public void truncate64(long len) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public InputStream getInputStream() throws SQLException {
- return new BlobInputStream(this, 4096);
+ checkClosed();
+ return new BlobInputStream(this);
}
/**
@@ -395,11 +432,27 @@ public InputStream getInputStream() throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public InputStream getInputStream(long limit) throws SQLException {
- return new BlobInputStream(this, 4096, limit);
+ checkClosed();
+ return new BlobInputStream(this, BlobInputStream.DEFAULT_MAX_BUFFER_SIZE, limit);
+ }
+
+ /**
+ * Returns an {@link InputStream} from this object, that will limit the amount of data that is
+ * visible.
+ * Added mostly for testing
+ *
+ * @param bufferSize buffer size for the stream
+ * @param limit maximum number of bytes the resulting stream will serve
+ * @return {@link InputStream} from this object
+ * @throws SQLException if a database-access error occurs.
+ */
+ public InputStream getInputStream(int bufferSize, long limit) throws SQLException {
+ checkClosed();
+ return new BlobInputStream(this, bufferSize, limit);
}
/**
- *
Returns an {@link OutputStream} to this object.
+ * Returns an {@link OutputStream} to this object.
*
*
This OutputStream can then be used in any method that requires an OutputStream.
*
@@ -407,8 +460,9 @@ public InputStream getInputStream(long limit) throws SQLException {
* @throws SQLException if a database-access error occurs.
*/
public OutputStream getOutputStream() throws SQLException {
+ checkClosed();
if (os == null) {
- os = new BlobOutputStream(this, 4096);
+ os = new BlobOutputStream(this);
}
return os;
}
diff --git a/src/main/java/org/postgresql/largeobject/LargeObjectManager.java b/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
index 1278096..3fa2cba 100644
--- a/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
+++ b/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
@@ -56,6 +56,7 @@
* @see java.sql.PreparedStatement#setBinaryStream
* @see java.sql.PreparedStatement#setUnicodeStream
*/
+@SuppressWarnings("deprecation") // support for deprecated Fastpath API
public class LargeObjectManager {
// the fastpath api for this connection
private Fastpath fp;
@@ -77,7 +78,7 @@ public class LargeObjectManager {
public static final int READWRITE = READ | WRITE;
/**
- *
Constructs the LargeObject API.
+ * Constructs the LargeObject API.
*
*
Important Notice
* This method should only be called by {@link BaseConnection}
It is identical to the delete method, and is supplied as the C API uses unlink.
*
diff --git a/src/main/java/org/postgresql/plugin/AuthenticationPlugin.java b/src/main/java/org/postgresql/plugin/AuthenticationPlugin.java
new file mode 100644
index 0000000..398e56a
--- /dev/null
+++ b/src/main/java/org/postgresql/plugin/AuthenticationPlugin.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.plugin;
+
+import org.postgresql.util.PSQLException;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+public interface AuthenticationPlugin {
+
+ /**
+ * Callback method to provide the password to use for authentication.
+ *
+ *
Implementers can also check the authentication type to ensure that the
+ * authentication handshake is using a specific authentication method (e.g. SASL)
+ * or avoiding a specific one (e.g. cleartext).
+ *
+ *
For security reasons, the driver will wipe the contents of the array returned
+ * by this method after it has been used for authentication.
+ *
+ *
Implementers must provide a new array each time this method is invoked as
+ * the previous contents will have been wiped.
+ *
+ * @param type The authentication method that the server is requesting
+ * @return The password to use or null if no password is available
+ * @throws PSQLException if something goes wrong supplying the password
+ */
+ char /* @Nullable */ [] getPassword(AuthenticationRequestType type) throws PSQLException;
+
+}
diff --git a/src/main/java/org/postgresql/plugin/AuthenticationRequestType.java b/src/main/java/org/postgresql/plugin/AuthenticationRequestType.java
new file mode 100644
index 0000000..f62bb11
--- /dev/null
+++ b/src/main/java/org/postgresql/plugin/AuthenticationRequestType.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.plugin;
+
+public enum AuthenticationRequestType {
+ CLEARTEXT_PASSWORD,
+ GSS,
+ MD5_PASSWORD,
+ SASL,
+}
diff --git a/src/main/java/org/postgresql/replication/LogSequenceNumber.java b/src/main/java/org/postgresql/replication/LogSequenceNumber.java
index f1ed18e..459696a 100644
--- a/src/main/java/org/postgresql/replication/LogSequenceNumber.java
+++ b/src/main/java/org/postgresql/replication/LogSequenceNumber.java
@@ -100,7 +100,7 @@ public boolean equals(/* @Nullable */ Object o) {
@Override
public int hashCode() {
- return (int) (value ^ (value >>> 32));
+ return Long.hashCode(value);
}
@Override
@@ -110,11 +110,7 @@ public String toString() {
@Override
public int compareTo(LogSequenceNumber o) {
- if (value == o.value) {
- return 0;
- }
- //Unsigned comparison
- return value + Long.MIN_VALUE < o.value + Long.MIN_VALUE ? -1 : 1;
+ return Long.compareUnsigned(value, o.value);
}
}
diff --git a/src/main/java/org/postgresql/replication/PGReplicationConnection.java b/src/main/java/org/postgresql/replication/PGReplicationConnection.java
index 6148f49..c38f47f 100644
--- a/src/main/java/org/postgresql/replication/PGReplicationConnection.java
+++ b/src/main/java/org/postgresql/replication/PGReplicationConnection.java
@@ -27,7 +27,7 @@ public interface PGReplicationConnection {
ChainedStreamBuilder replicationStream();
/**
- *
Create replication slot, that can be next use in {@link PGReplicationConnection#replicationStream()}
+ * Create replication slot, that can be next use in {@link PGReplicationConnection#replicationStream()}
*
*
Replication slots provide an automated way to ensure that the master does not remove WAL
* segments until they have been received by all standbys, and that the master does not remove
diff --git a/src/main/java/org/postgresql/replication/PGReplicationConnectionImpl.java b/src/main/java/org/postgresql/replication/PGReplicationConnectionImpl.java
index 5503391..350526e 100644
--- a/src/main/java/org/postgresql/replication/PGReplicationConnectionImpl.java
+++ b/src/main/java/org/postgresql/replication/PGReplicationConnectionImpl.java
@@ -15,7 +15,7 @@
import java.sql.Statement;
public class PGReplicationConnectionImpl implements PGReplicationConnection {
- private BaseConnection connection;
+ private final BaseConnection connection;
public PGReplicationConnectionImpl(BaseConnection connection) {
this.connection = connection;
diff --git a/src/main/java/org/postgresql/replication/PGReplicationStream.java b/src/main/java/org/postgresql/replication/PGReplicationStream.java
index 1fef540..5e3566f 100644
--- a/src/main/java/org/postgresql/replication/PGReplicationStream.java
+++ b/src/main/java/org/postgresql/replication/PGReplicationStream.java
@@ -14,19 +14,18 @@
import java.sql.SQLException;
/**
- * Not tread safe replication stream (though certain methods can be safely called by different
+ * Not thread safe replication stream (though certain methods can be safely called by different
* threads). After complete streaming should be close, for free resource on backend. Periodical
* status update work only when use {@link PGReplicationStream#read()} method. It means that
* process wal record should be fast as possible, because during process wal record lead to
* disconnect by timeout from server.
*/
public interface PGReplicationStream
- extends AutoCloseable
- /* hi, checkstyle */ {
+ extends AutoCloseable {
/**
- *
Read next wal record from backend. It method can be block until new message will not get
- * from server.
+ * Read next wal record from backend. It method can be block until new message will not get
+ * from server.
*
*
A single WAL record is never split across two XLogData messages. When a WAL record crosses a
* WAL page boundary, and is therefore already split using continuation records, it can be split
@@ -40,10 +39,10 @@ public interface PGReplicationStream
/* @Nullable */ ByteBuffer read() throws SQLException;
/**
- *
Read next WAL record from backend. This method does not block and in contrast to {@link
+ * Read next WAL record from backend. This method does not block and in contrast to {@link
* PGReplicationStream#read()}. If message from backend absent return null. It allow periodically
* check message in stream and if they absent sleep some time, but it time should be less than
- * {@link CommonOptions#getStatusInterval()} to avoid disconnect from the server.
+ * {@link CommonOptions#getStatusInterval()} to avoid disconnect from the server.
*
*
A single WAL record is never split across two XLogData messages. When a WAL record crosses a
* WAL page boundary, and is therefore already split using continuation records, it can be split
@@ -58,7 +57,7 @@ public interface PGReplicationStream
/* @Nullable */ ByteBuffer readPending() throws SQLException;
/**
- *
Parameter updates by execute {@link PGReplicationStream#read()} method.
It is safe to call this method in a thread different than the main thread. However, usually this
* method is called in the main thread after a successful {@link PGReplicationStream#read()} or
@@ -70,8 +69,8 @@ public interface PGReplicationStream
LogSequenceNumber getLastReceiveLSN();
/**
- *
Last flushed LSN sent in update message to backend. Parameter updates only via {@link
- * PGReplicationStream#setFlushedLSN(LogSequenceNumber)}
+ * Last flushed LSN sent in update message to backend. Parameter updates only via {@link
+ * PGReplicationStream#setFlushedLSN(LogSequenceNumber)}
*
*
It is safe to call this method in a thread different than the main thread.
Set flushed LSN. This parameter will be sent to backend on next update status iteration. Flushed
- * LSN position help backend define which WAL can be recycled.
+ * Set flushed LSN. This parameter will be sent to backend on next update status iteration. Flushed
+ * LSN position help backend define which WAL can be recycled.
*
*
It is safe to call this method in a thread different than the main thread. The updated value
* will be sent to the backend in the next status update run.
Inform backend which LSN has been applied on standby.
- * Feedback will send to backend on next update status iteration.
+ * Inform backend which LSN has been applied on standby.
+ * Feedback will send to backend on next update status iteration.
*
*
It is safe to call this method in a thread different than the main thread. The updated value
* will be sent to the backend in the next status update run.
Stop replication changes from server and free resources. After that connection can be reuse
- * to another queries. Also after close current stream they cannot be used anymore.
+ * Stop replication changes from server and free resources. After that connection can be reuse
+ * to another queries. Also after close current stream they cannot be used anymore.
*
*
Note: This method can spend much time for logical replication stream on postgresql
* version 9.6 and lower, because postgresql have bug - during decode big transaction to logical
@@ -141,5 +140,6 @@ public interface PGReplicationStream
*
* @throws SQLException when some internal exception occurs during end streaming
*/
+ @Override
void close() throws SQLException;
}
diff --git a/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java b/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
index 4d930e6..21e4a19 100644
--- a/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
@@ -17,7 +17,7 @@ public abstract class AbstractCreateSlotBuilder {
protected /* @Nullable */ String slotName;
- protected boolean temporaryOption = false;
+ protected boolean temporaryOption;
protected BaseConnection connection;
protected AbstractCreateSlotBuilder(BaseConnection connection) {
diff --git a/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
index 36c2bf5..dfa524d 100644
--- a/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
@@ -17,6 +17,7 @@ public abstract class AbstractStreamBuilderTemporary slots are not saved to disk and are automatically dropped on error or when
- * the session has finished.
+ * Temporary slots are not saved to disk and are automatically dropped on error or when
+ * the session has finished.
*
*
This feature is only supported by PostgreSQL versions >= 10.
*
diff --git a/src/main/java/org/postgresql/replication/fluent/ChainedCommonStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/ChainedCommonStreamBuilder.java
index 2a41246..d2114eb 100644
--- a/src/main/java/org/postgresql/replication/fluent/ChainedCommonStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/ChainedCommonStreamBuilder.java
@@ -45,4 +45,12 @@ public interface ChainedCommonStreamBuilderCreate physical replication stream for process wal logs in binary form.
+ * Create physical replication stream for process wal logs in binary form.
*
*
Create logical replication stream that decode raw wal logs by output plugin to logical form.
+ * Create logical replication stream that decode raw wal logs by output plugin to logical form.
* Default about logical decoding you can see by following link
*
* Logical Decoding Concepts
* .
- *
Create physical replication stream for process wal logs in binary form.
+ * Create physical replication stream for process wal logs in binary form.
*
*
Example usage:
*
@@ -62,6 +61,11 @@ public interface ChainedStreamBuilder {
* .replicationStream()
* .physical()
* .withStartPosition(lsn)
+ * .withSlotName("test_decoding")
+ * .withSlotOption("include-xids", false)
+ * .withSlotOption("skip-empty-xacts", true)
+ * .withStatusInterval(5, TimeUnit.SECONDS)
+ * .withAutomaticFlush(true)
* .start();
*
* while (true) {
diff --git a/src/main/java/org/postgresql/replication/fluent/CommonOptions.java b/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
index ebd3ef3..653133a 100644
--- a/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
+++ b/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
@@ -36,4 +36,6 @@ public interface CommonOptions {
* @return the current status interval
*/
int getStatusInterval();
+
+ boolean getAutomaticFlush();
}
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/ChainedLogicalCreateSlotBuilder.java b/src/main/java/org/postgresql/replication/fluent/logical/ChainedLogicalCreateSlotBuilder.java
index cae77bb..affd565 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/ChainedLogicalCreateSlotBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/ChainedLogicalCreateSlotBuilder.java
@@ -14,8 +14,8 @@ public interface ChainedLogicalCreateSlotBuilder
extends ChainedCommonCreateSlotBuilder {
/**
- *
Output plugin that should be use for decode physical represent WAL to some logical form.
- * Output plugin should be installed on server(exists in shared_preload_libraries).
+ * Output plugin that should be use for decode physical represent WAL to some logical form.
+ * Output plugin should be installed on server(exists in shared_preload_libraries).
*
*
Package postgresql-contrib provides sample output plugin test_decoding that can be
* use for test logical replication api
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java b/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
index 7019e18..8bcf0fe 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
@@ -17,6 +17,7 @@ public interface LogicalReplicationOptions extends CommonOptions {
*
* @return not null logical replication slot name that already exists on server and free.
*/
+ @Override
/* @Nullable */ String getSlotName();
/**
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
index e73d086..e8c104a 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
@@ -20,7 +20,7 @@ public class LogicalStreamBuilder extends AbstractStreamBuilder 0) {
+ for (String kt : keyType) {
+ if (kt.equalsIgnoreCase(certKeyType)) {
+ keyTypeFound = true;
+ }
+ }
+ } else {
+ // If no key types were passed in, assume we don't care
+ // about checking that the cert uses a particular key type.
+ keyTypeFound = true;
+ }
+ if (keyTypeFound) {
+ for (Principal issuer : principals) {
+ if (ourissuer.equals(issuer)) {
+ found = keyTypeFound;
+ }
+ }
+ }
+ return found ? "user" : null;
+ }
+ }
+ }
+
+ @Override
+ public String /* @Nullable */ [] getServerAliases(String s, Principal /* @Nullable */ [] principals) {
+ return new String[]{};
+ }
+
+ @Override
+ public /* @Nullable */ String chooseServerAlias(String s, Principal /* @Nullable */ [] principals,
+ /* @Nullable */ Socket socket) {
+ // we are not a server
+ return null;
+ }
+
+ /**
+ * Validates that the private key file has secure permissions, matching libpq behavior.
+ * On POSIX systems, root-owned files are allowed group-read access (up to 0640), since
+ * it's common for root to own certs and grant read access via group membership. Files
+ * owned by anyone else must be 0600 or stricter.
+ * On Windows, ACLs are checked to ensure only the owner and trusted system accounts have access.
+ *
+ * @param keyPath the path to the private key file
+ * @throws PSQLException if the file has insecure permissions
+ */
+ public static void validateKeyFilePermissions(Path keyPath) throws PSQLException {
+ // Try POSIX permissions first (Linux, macOS, Unix)
+ if (validatePosixPermissions(keyPath)) {
+ return;
+ }
+
+ // If POSIX is not supported, try Windows ACL permissions
+ if (validateWindowsAclPermissions(keyPath)) {
+ return;
+ }
+ throw new PSQLException(
+ GT.tr("Unable to retrieve the permissions of the private key file \"{0}\"",
+ keyPath.toString()),
+ PSQLState.CONNECTION_FAILURE);
+ }
+
+ /**
+ * Validates POSIX file permissions of key, matching libpq behavior.
+ * Root-owned files (uid 0) allow GROUP_READ (up to 0640).
+ * Non-root-owned files require 0600 or less (no group or other permissions).
+ *
+ * @param keyPath the path to the private key file
+ * @return true if validation succeeded (permissions are secure), false if POSIX is not supported
+ * @throws PSQLException if the file has insecure permissions
+ */
+ private static boolean validatePosixPermissions(Path keyPath) throws PSQLException {
+ try {
+ Set permissions = Files.getPosixFilePermissions(keyPath);
+ boolean isOwnedByRoot = isFileOwnedByRoot(keyPath);
+
+ if (hasInsecurePosixPermissions(permissions, isOwnedByRoot)) {
+ throw new PSQLException(
+ GT.tr("private key file \"{0}\" has group or world access; "
+ + "file must have permissions u=rw (0600) or less if owned by the current user, "
+ + "or permissions u=rw,g=r (0640) or less if owned by root. "
+ + "Current permissions: {1}",
+ keyPath.toString(),
+ PosixFilePermissions.toString(permissions)),
+ PSQLState.CONNECTION_FAILURE);
+ }
+ return true;
+ } catch (UnsupportedOperationException e) {
+ return false;
+ } catch (IOException e) {
+ throw new PSQLException(
+ GT.tr("Could not read permissions for private key file \"{0}\"", keyPath.toString()),
+ PSQLState.CONNECTION_FAILURE, e);
+ }
+ }
+
+ /**
+ * Checks whether the file is owned by root (uid 0).
+ * Falls back to false if the unix:uid attribute is not available.
+ */
+ private static boolean isFileOwnedByRoot(Path keyPath) throws IOException {
+ try {
+ Object uid = Files.getAttribute(keyPath, "unix:uid");
+ return Integer.valueOf(ROOT_UID).equals(uid);
+ } catch (UnsupportedOperationException | IllegalArgumentException e) {
+ // unix:uid not available
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether the given POSIX permissions are insecure for a private key file.
+ * Matches libpq behavior: root-owned files allow GROUP_READ (0640),
+ * while non-root-owned files reject all group and other permissions (0600).
+ */
+ private static boolean hasInsecurePosixPermissions(
+ Set permissions, boolean isOwnedByRoot) {
+ boolean hasOtherPerms = permissions.contains(PosixFilePermission.OTHERS_READ)
+ || permissions.contains(PosixFilePermission.OTHERS_WRITE)
+ || permissions.contains(PosixFilePermission.OTHERS_EXECUTE);
+
+ if (isOwnedByRoot) {
+ boolean hasGroupWriteOrExecute = permissions.contains(PosixFilePermission.GROUP_WRITE)
+ || permissions.contains(PosixFilePermission.GROUP_EXECUTE);
+ return hasGroupWriteOrExecute || hasOtherPerms;
+ }
+
+ boolean hasGroupPerms = permissions.contains(PosixFilePermission.GROUP_READ)
+ || permissions.contains(PosixFilePermission.GROUP_WRITE)
+ || permissions.contains(PosixFilePermission.GROUP_EXECUTE);
+ return hasGroupPerms || hasOtherPerms;
+ }
+
+ /**
+ * Validates Windows ACL permissions of the key file.
+ *
+ * @param keyPath the path to the private key file
+ * @return true if validation succeeded (permissions are secure), false if ACL is not supported
+ * @throws PSQLException if the file has insecure permissions
+ */
+ private static boolean validateWindowsAclPermissions(Path keyPath) throws PSQLException {
+ try {
+ AclFileAttributeView aclView = Files.getFileAttributeView(keyPath, AclFileAttributeView.class);
+ if (aclView == null) {
+ return false;
+ }
+
+ UserPrincipal owner = aclView.getOwner();
+ List aclEntries = aclView.getAcl();
+
+ for (AclEntry entry : aclEntries) {
+ UserPrincipal principal = entry.principal();
+ String principalName = principal.getName();
+
+ // Allow owner, SYSTEM, and Administrators (these are trusted on Windows)
+ boolean isTrustedPrincipal = principal.equals(owner)
+ || principalName.equals("NT AUTHORITY\\SYSTEM")
+ || principalName.equals("BUILTIN\\Administrators")
+ || principalName.endsWith("\\Administrators");
+
+ if (!isTrustedPrincipal) {
+ // Check if this non-owner principal has READ permission
+ Set permissions = entry.permissions();
+ if (permissions.contains(AclEntryPermission.READ_DATA)
+ || permissions.contains(AclEntryPermission.READ_ATTRIBUTES)
+ || permissions.contains(AclEntryPermission.READ_NAMED_ATTRS)) {
+ throw new PSQLException(
+ GT.tr("Private key file \"{0}\" has insecure permissions. "
+ + "Non-owner principal \"{1}\" has read access.",
+ keyPath.toString(),
+ principalName),
+ PSQLState.CONNECTION_FAILURE);
+ }
+ }
+ }
+ return true;
+ } catch (UnsupportedOperationException e) {
+ return false;
+ } catch (IOException e) {
+ throw new PSQLException(
+ GT.tr("Could not read ACL permissions for private key file \"{0}\"", keyPath.toString()),
+ PSQLState.CONNECTION_FAILURE, e);
+ }
+ }
+}
diff --git a/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java b/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
index cb8131d..a81226f 100644
--- a/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
+++ b/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
@@ -5,14 +5,17 @@
package org.postgresql.ssl;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
+import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
-public abstract class DbKeyStoreSocketFactory extends org.postgresql.ssl.WrappedFactory {
+public abstract class DbKeyStoreSocketFactory extends WrappedFactory {
/*
* Populate the WrappedFactory member factory with an SSL Socket Factory that uses the JKS
* keystore provided by getKeyStorePassword() and getKeyStoreStream(). A subclass only needs to
@@ -20,21 +23,21 @@ public abstract class DbKeyStoreSocketFactory extends org.postgresql.ssl.Wrapped
* certificate to send to the server, as well as checking the server's certificate against a set
* of trusted CAs.
*/
- @SuppressWarnings("nullness:method.invocation.invalid")
+ @SuppressWarnings("method.invocation")
public DbKeyStoreSocketFactory() throws DbKeyStoreSocketException {
KeyStore keys;
char[] password;
try {
keys = KeyStore.getInstance("JKS");
// Call of the sub-class method during object initialization is generally a bad idea
- // Currently we suppress it with method.invocation.invalid
+ // Currently we suppress it with method.invocation
password = getKeyStorePassword();
keys.load(getKeyStoreStream(), password);
- } catch (java.security.GeneralSecurityException gse) {
+ } catch (GeneralSecurityException gse) {
throw new DbKeyStoreSocketException("Failed to load keystore: " + gse.getMessage());
- } catch (java.io.FileNotFoundException fnfe) {
+ } catch (FileNotFoundException fnfe) {
throw new DbKeyStoreSocketException("Failed to find keystore file." + fnfe.getMessage());
- } catch (java.io.IOException ioe) {
+ } catch (IOException ioe) {
throw new DbKeyStoreSocketException("Failed to read keystore file: " + ioe.getMessage());
}
try {
@@ -49,7 +52,7 @@ public DbKeyStoreSocketFactory() throws DbKeyStoreSocketException {
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(keyfact.getKeyManagers(), trustfact.getTrustManagers(), null);
factory = ctx.getSocketFactory();
- } catch (java.security.GeneralSecurityException gse) {
+ } catch (GeneralSecurityException gse) {
throw new DbKeyStoreSocketException(
"Failed to set up database socket factory: " + gse.getMessage());
}
diff --git a/src/main/java/org/postgresql/ssl/LazyKeyManager.java b/src/main/java/org/postgresql/ssl/LazyKeyManager.java
index 5ae37c4..7eb2ef6 100644
--- a/src/main/java/org/postgresql/ssl/LazyKeyManager.java
+++ b/src/main/java/org/postgresql/ssl/LazyKeyManager.java
@@ -8,12 +8,13 @@
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
+import org.postgresql.util.internal.FileUtils;
// import org.checkerframework.checker.nullness.qual.Nullable;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.security.AlgorithmParameters;
@@ -48,13 +49,13 @@
* A Key manager that only loads the keys, if necessary.
*/
public class LazyKeyManager implements X509KeyManager {
- private X509Certificate /* @Nullable */ [] cert = null;
- private /* @Nullable */ PrivateKey key = null;
+ private X509Certificate /* @Nullable */ [] cert;
+ private /* @Nullable */ PrivateKey key;
private final /* @Nullable */ String certfile;
private final /* @Nullable */ String keyfile;
private final CallbackHandler cbh;
private final boolean defaultfile;
- private /* @Nullable */ PSQLException error = null;
+ private /* @Nullable */ PSQLException error;
/**
* Constructor. certfile and keyfile can be null, in that case no certificate is presented to the
@@ -73,7 +74,7 @@ public LazyKeyManager(/* @Nullable */ String certfile, /* @Nullable */ String ke
}
/**
- * getCertificateChain and getPrivateKey cannot throw exeptions, therefore any exception is stored
+ * getCertificateChain and getPrivateKey cannot throw exceptions, therefore any exception is stored
* in {@link #error} and can be raised by this method.
*
* @throws PSQLException if any exception is stored in {@link #error} and can be raised
@@ -102,14 +103,30 @@ public void throwKeyManagerException() throws PSQLException {
if (certchain == null) {
return null;
} else {
- X500Principal ourissuer = certchain[certchain.length - 1].getIssuerX500Principal();
+ X509Certificate cert = certchain[certchain.length - 1];
+ X500Principal ourissuer = cert.getIssuerX500Principal();
+ String certKeyType = cert.getPublicKey().getAlgorithm();
+ boolean keyTypeFound = false;
boolean found = false;
- for (Principal issuer : issuers) {
- if (ourissuer.equals(issuer)) {
- found = true;
+ if (keyType != null && keyType.length > 0) {
+ for (String kt : keyType) {
+ if (kt.equalsIgnoreCase(certKeyType)) {
+ keyTypeFound = true;
+ }
}
+ } else {
+ // If no key types were passed in, assume we don't care
+ // about checking that the cert uses a particular key type.
+ keyTypeFound = true;
}
- return (found ? "user" : null);
+ if (keyTypeFound) {
+ for (Principal issuer : issuers) {
+ if (ourissuer.equals(issuer)) {
+ found = keyTypeFound;
+ }
+ }
+ }
+ return found ? "user" : null;
}
}
}
@@ -138,9 +155,9 @@ public void throwKeyManagerException() throws PSQLException {
return null;
}
Collection extends Certificate> certs;
- FileInputStream certfileStream = null;
+ InputStream certfileStream = null;
try {
- certfileStream = new FileInputStream(certfile);
+ certfileStream = FileUtils.newBufferedInputStream(certfile);
certs = cf.generateCertificates(certfileStream);
} catch (FileNotFoundException ioex) {
if (!defaultfile) { // It is not an error if there is no file at the default location
@@ -175,7 +192,7 @@ public void throwKeyManagerException() throws PSQLException {
public String /* @Nullable */ [] getClientAliases(String keyType,
Principal /* @Nullable */ [] issuers) {
String alias = chooseClientAlias(new String[]{keyType}, issuers, (Socket) null);
- return (alias == null ? new String[]{} : new String[]{alias});
+ return alias == null ? new String[]{} : new String[]{alias};
}
private static byte[] readFileFully(String path) throws IOException {
@@ -253,7 +270,7 @@ private static byte[] readFileFully(String path) throws IOException {
// Extract the iteration count and the salt
AlgorithmParameters algParams = ePKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
- // Decrypt the encryped private key into a PKCS8EncodedKeySpec
+ // Decrypt the encrypted private key into a PKCS8EncodedKeySpec
KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);
key = kf.generatePrivate(pkcs8KeySpec);
} catch (GeneralSecurityException ikex) {
diff --git a/src/main/java/org/postgresql/ssl/LibPQFactory.java b/src/main/java/org/postgresql/ssl/LibPQFactory.java
index bea4926..ac48b85 100644
--- a/src/main/java/org/postgresql/ssl/LibPQFactory.java
+++ b/src/main/java/org/postgresql/ssl/LibPQFactory.java
@@ -14,14 +14,15 @@
import org.postgresql.util.ObjectFactory;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
+import org.postgresql.util.internal.FileUtils;
// import org.checkerframework.checker.initialization.qual.UnderInitialization;
// import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.Console;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
@@ -29,6 +30,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
+import java.util.Locale;
import java.util.Properties;
import javax.net.ssl.KeyManager;
@@ -53,10 +55,10 @@ private CallbackHandler getCallbackHandler(
Properties info) throws PSQLException {
// Determine the callback handler
CallbackHandler cbh;
- String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.get(info);
+ String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.getOrDefault(info);
if (sslpasswordcallback != null) {
try {
- cbh = (CallbackHandler) ObjectFactory.instantiate(sslpasswordcallback, info, false, null);
+ cbh = ObjectFactory.instantiate(CallbackHandler.class, sslpasswordcallback, info, false, null);
} catch (Exception e) {
throw new PSQLException(
GT.tr("The password callback class provided {0} could not be instantiated.",
@@ -64,21 +66,27 @@ private CallbackHandler getCallbackHandler(
PSQLState.CONNECTION_FAILURE, e);
}
} else {
- cbh = new ConsoleCallbackHandler(PGProperty.SSL_PASSWORD.get(info));
+ cbh = new ConsoleCallbackHandler(PGProperty.SSL_PASSWORD.getOrDefault(info));
}
return cbh;
}
- private void initPk8(
- /* @UnderInitialization(WrappedFactory.class) */ LibPQFactory this,
- String sslkeyfile, String defaultdir, Properties info) throws PSQLException {
-
+ private String getCertFilePath(
+ /* @UnderInitialization(WrappedFactory.class) */LibPQFactory this, String defaultdir, Properties info) {
// Load the client's certificate and key
- String sslcertfile = PGProperty.SSL_CERT.get(info);
+ String sslcertfile = PGProperty.SSL_CERT.getOrDefault(info);
if (sslcertfile == null) { // Fall back to default
defaultfile = true;
sslcertfile = defaultdir + "postgresql.crt";
}
+ return sslcertfile;
+ }
+
+ private void initPk8(
+ /* @UnderInitialization(WrappedFactory.class) */LibPQFactory this,
+ String sslkeyfile, String defaultdir, Properties info) throws PSQLException {
+
+ String sslcertfile = getCertFilePath(defaultdir, info);
// If the properties are empty, give null to prevent client key selection
km = new LazyKeyManager(("".equals(sslcertfile) ? null : sslcertfile),
@@ -91,6 +99,18 @@ private void initP12(
km = new PKCS12KeyManager(sslkeyfile, getCallbackHandler(info));
}
+ private void initPEM(
+ /* @UnderInitialization(WrappedFactory.class) */LibPQFactory this,
+ String sslKeyFile, String defaultdir, Properties info) throws PSQLException {
+ try {
+ String sslCertFile = getCertFilePath(defaultdir, info);
+ String algorithm = castNonNull(PGProperty.PEM_KEY_ALGORITHM.getOrDefault(info));
+ km = new PEMKeyManager(sslKeyFile, sslCertFile, algorithm);
+ } catch (Exception ex) {
+ throw new PSQLException(GT.tr("Could not initialize PEMKeyManager."), PSQLState.CONNECTION_FAILURE, ex);
+ }
+ }
+
/**
* @param info the connection parameters The following parameters are used:
* sslmode,sslcert,sslkey,sslrootcert,sslhostnameverifier,sslpasswordcallback,sslpassword
@@ -104,13 +124,13 @@ public LibPQFactory(Properties info) throws PSQLException {
String pathsep = System.getProperty("file.separator");
String defaultdir;
- if (System.getProperty("os.name").toLowerCase().contains("windows")) { // It is Windows
+ if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows")) { // It is Windows
defaultdir = System.getenv("APPDATA") + pathsep + "postgresql" + pathsep;
} else {
defaultdir = System.getProperty("user.home") + pathsep + ".postgresql" + pathsep;
}
- String sslkeyfile = PGProperty.SSL_KEY.get(info);
+ String sslkeyfile = PGProperty.SSL_KEY.getOrDefault(info);
if (sslkeyfile == null) { // Fall back to default
defaultfile = true;
sslkeyfile = defaultdir + "postgresql.pk8";
@@ -118,6 +138,8 @@ public LibPQFactory(Properties info) throws PSQLException {
if (sslkeyfile.endsWith(".p12") || sslkeyfile.endsWith(".pfx")) {
initP12(sslkeyfile, info);
+ } else if (sslkeyfile.endsWith(".key") || sslkeyfile.endsWith(".pem")) {
+ initPEM(sslkeyfile, defaultdir, info);
} else {
initPk8(sslkeyfile, defaultdir, info);
}
@@ -138,13 +160,13 @@ public LibPQFactory(Properties info) throws PSQLException {
// this should never happen
throw new NoSuchAlgorithmException("jks KeyStore not available");
}
- String sslrootcertfile = PGProperty.SSL_ROOT_CERT.get(info);
+ String sslrootcertfile = PGProperty.SSL_ROOT_CERT.getOrDefault(info);
if (sslrootcertfile == null) { // Fall back to default
sslrootcertfile = defaultdir + "root.crt";
}
- FileInputStream fis;
+ InputStream is;
try {
- fis = new FileInputStream(sslrootcertfile); // NOSONAR
+ is = FileUtils.newBufferedInputStream(sslrootcertfile); // NOSONAR
} catch (FileNotFoundException ex) {
throw new PSQLException(
GT.tr("Could not open SSL root certificate file {0}.", sslrootcertfile),
@@ -152,9 +174,9 @@ public LibPQFactory(Properties info) throws PSQLException {
}
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
- // Certificate[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{}); //Does
+ // Certificate[] certs = cf.generateCertificates(is).toArray(new Certificate[]{}); //Does
// not work in java 1.4
- Object[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{});
+ Object[] certs = cf.generateCertificates(is).toArray(new Certificate[]{});
ks.load(null, null);
for (int i = 0; i < certs.length; i++) {
ks.setCertificateEntry("cert" + i, (Certificate) certs[i]);
@@ -171,7 +193,7 @@ public LibPQFactory(Properties info) throws PSQLException {
PSQLState.CONNECTION_FAILURE, gsex);
} finally {
try {
- fis.close();
+ is.close();
} catch (IOException e) {
/* ignore */
}
@@ -203,10 +225,13 @@ public LibPQFactory(Properties info) throws PSQLException {
public void throwKeyManagerException() throws PSQLException {
if (km != null) {
if (km instanceof LazyKeyManager) {
- ((LazyKeyManager)km).throwKeyManagerException();
+ ((LazyKeyManager) km).throwKeyManagerException();
}
if (km instanceof PKCS12KeyManager) {
- ((PKCS12KeyManager)km).throwKeyManagerException();
+ ((PKCS12KeyManager) km).throwKeyManagerException();
+ }
+ if (km instanceof PEMKeyManager) {
+ ((PEMKeyManager) km).throwKeyManagerException();
}
}
}
@@ -217,7 +242,7 @@ public void throwKeyManagerException() throws PSQLException {
*/
public static class ConsoleCallbackHandler implements CallbackHandler {
- private char /* @Nullable */ [] password = null;
+ private char /* @Nullable */ [] password;
ConsoleCallbackHandler(/* @Nullable */ String password) {
if (password != null) {
@@ -233,6 +258,7 @@ public static class ConsoleCallbackHandler implements CallbackHandler {
* PasswordCallback is supplied
*/
@Override
+ @SuppressWarnings("SystemConsoleNull")
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
Console cons = System.console();
char[] password = this.password;
diff --git a/src/main/java/org/postgresql/ssl/MakeSSL.java b/src/main/java/org/postgresql/ssl/MakeSSL.java
index bf64673..cbcc02e 100644
--- a/src/main/java/org/postgresql/ssl/MakeSSL.java
+++ b/src/main/java/org/postgresql/ssl/MakeSSL.java
@@ -9,10 +9,12 @@
import org.postgresql.core.PGStream;
import org.postgresql.core.SocketFactoryFactory;
import org.postgresql.jdbc.SslMode;
+import org.postgresql.jdbc.SslNegotiation;
import org.postgresql.util.GT;
import org.postgresql.util.ObjectFactory;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
+import org.postgresql.util.internal.Nullness;
import java.io.IOException;
import java.util.Properties;
@@ -20,6 +22,7 @@
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@@ -36,6 +39,13 @@ public static void convert(PGStream stream, Properties info)
try {
newConnection = (SSLSocket) factory.createSocket(stream.getSocket(),
stream.getHostSpec().getHost(), stream.getHostSpec().getPort(), true);
+ int connectTimeoutSeconds = PGProperty.CONNECT_TIMEOUT.getInt(info);
+ newConnection.setSoTimeout(connectTimeoutSeconds * 1000);
+ if (SslNegotiation.of(Nullness.castNonNull(PGProperty.SSL_NEGOTIATION.getOrDefault(info))) == SslNegotiation.DIRECT ) {
+ SSLParameters sslParameters = newConnection.getSSLParameters();
+ sslParameters.setApplicationProtocols(new String[]{"postgresql"});
+ newConnection.setSSLParameters(sslParameters);
+ }
// We must invoke manually, otherwise the exceptions are hidden
newConnection.setUseClientMode(true);
newConnection.startHandshake();
@@ -51,20 +61,22 @@ public static void convert(PGStream stream, Properties info)
if (sslMode.verifyPeerName()) {
verifyPeerName(stream, info, newConnection);
}
-
+ // Zero timeout (default) means infinite
+ int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
+ newConnection.setSoTimeout(socketTimeout * 1000);
stream.changeSocket(newConnection);
}
private static void verifyPeerName(PGStream stream, Properties info, SSLSocket newConnection)
throws PSQLException {
HostnameVerifier hvn;
- String sslhostnameverifier = PGProperty.SSL_HOSTNAME_VERIFIER.get(info);
+ String sslhostnameverifier = PGProperty.SSL_HOSTNAME_VERIFIER.getOrDefault(info);
if (sslhostnameverifier == null) {
hvn = PGjdbcHostnameVerifier.INSTANCE;
sslhostnameverifier = "PgjdbcHostnameVerifier";
} else {
try {
- hvn = (HostnameVerifier) instantiate(sslhostnameverifier, info, false, null);
+ hvn = instantiate(HostnameVerifier.class, sslhostnameverifier, info, false, null);
} catch (Exception e) {
throw new PSQLException(
GT.tr("The HostnameVerifier class provided {0} could not be instantiated.",
diff --git a/src/main/java/org/postgresql/ssl/NonValidatingFactory.java b/src/main/java/org/postgresql/ssl/NonValidatingFactory.java
index e7d804b..649a54d 100644
--- a/src/main/java/org/postgresql/ssl/NonValidatingFactory.java
+++ b/src/main/java/org/postgresql/ssl/NonValidatingFactory.java
@@ -37,13 +37,16 @@ public NonValidatingFactory(String arg) throws GeneralSecurityException {
public static class NonValidatingTM implements X509TrustManager {
+ @Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
+ @Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
+ @Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
diff --git a/src/main/java/org/postgresql/ssl/PEMKeyManager.java b/src/main/java/org/postgresql/ssl/PEMKeyManager.java
new file mode 100644
index 0000000..5dc8982
--- /dev/null
+++ b/src/main/java/org/postgresql/ssl/PEMKeyManager.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2025, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.ssl;
+
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.List;
+
+public class PEMKeyManager extends BaseX509KeyManager {
+
+ private final String keyFilePath;
+ private final String certFilePath;
+ private final String keyAlgorithm;
+
+ public PEMKeyManager(String pemKeyPath, String pemCertsPath, String keyAlgorithm) {
+ this.keyFilePath = pemKeyPath;
+ this.certFilePath = pemCertsPath;
+ this.keyAlgorithm = keyAlgorithm;
+ }
+
+ @Override
+ public /* @Nullable */ PrivateKey getPrivateKey(String s) {
+ try {
+ Path keyPath = Paths.get(keyFilePath);
+
+ // Validate file permissions before reading
+ validateKeyFilePermissions(keyPath);
+
+ List lines = Files.readAllLines(keyPath);
+ StringBuilder keyContent = new StringBuilder();
+ for (String line : lines) {
+ // as we are using PKCS#8 format, we just expect "BEGIN PRIVATE KEY" as the start of the key
+ // ref: https://datatracker.ietf.org/doc/html/rfc5208#section-5
+ if (line.contains("BEGIN PRIVATE KEY")) {
+ // ignore the start of the key
+ continue;
+ }
+ // as we are using PKCS#8 format, we just expect "END PRIVATE KEY" as the end of the key
+ // ref: https://datatracker.ietf.org/doc/html/rfc5208#section-5
+ if (line.contains("END PRIVATE KEY")) {
+ // stop reading after we encounter end of the key
+ break;
+ }
+ keyContent.append(line.trim());
+ }
+
+ byte[] privateKeyDERBytes = Base64.getDecoder().decode(keyContent.toString());
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyDERBytes);
+ KeyFactory kf = KeyFactory.getInstance(this.keyAlgorithm);
+
+ // to prevent security attacks, lets wipe out the contents of variables holding sensitive data
+ Arrays.fill(privateKeyDERBytes, (byte) 0);
+ for (int i = 0; i < keyContent.length(); i++) {
+ keyContent.setCharAt(i, '\0');
+ }
+
+ return kf.generatePrivate(keySpec);
+ } catch (Exception e) {
+ error = new PSQLException(GT.tr("Could not load the private key"), PSQLState.CONNECTION_FAILURE, e);
+ }
+ return null;
+ }
+
+ @Override
+ public X509Certificate /* @Nullable */ [] getCertificateChain(String alias) {
+ try (InputStream inStream = Files.newInputStream(Paths.get(this.certFilePath))) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ Collection extends Certificate> certs = cf.generateCertificates(inStream);
+ List certChain = new ArrayList<>();
+
+ for (Certificate cert : certs) {
+ if (cert instanceof X509Certificate) {
+ certChain.add((X509Certificate) cert);
+ }
+ }
+
+ return certChain.toArray(new X509Certificate[0]);
+ } catch (Exception e) {
+ error = new PSQLException(GT.tr("Could not load cert chain"), PSQLState.CONNECTION_FAILURE, e);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java b/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
index 20de49f..300bca9 100644
--- a/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
+++ b/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
@@ -188,7 +188,7 @@ public boolean verify(String hostname, SSLSession session) {
return false;
}
- List commonNames = new ArrayList(1);
+ List commonNames = new ArrayList<>(1);
for (Rdn rdn : dn.getRdns()) {
if ("CN".equals(rdn.getType())) {
commonNames.add((String) rdn.getValue());
diff --git a/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java b/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
index 4951581..ef3a5f3 100644
--- a/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
+++ b/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
@@ -5,36 +5,32 @@
package org.postgresql.ssl;
+import org.postgresql.jdbc.ResourceLock;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
+import org.postgresql.util.internal.FileUtils;
// import org.checkerframework.checker.nullness.qual.Nullable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.net.Socket;
import java.security.KeyStore;
import java.security.KeyStoreException;
-import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import javax.net.ssl.X509KeyManager;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.x500.X500Principal;
-public class PKCS12KeyManager implements X509KeyManager {
+public class PKCS12KeyManager extends BaseX509KeyManager {
private final CallbackHandler cbh;
- private /* @Nullable */ PSQLException error = null;
private final String keyfile;
private final KeyStore keyStore;
- boolean keystoreLoaded = false;
+ boolean keystoreLoaded;
+ private final ResourceLock lock = new ResourceLock();
public PKCS12KeyManager(String pkcsFile, CallbackHandler cbh) throws PSQLException {
try {
@@ -48,63 +44,6 @@ public PKCS12KeyManager(String pkcsFile, CallbackHandler cbh) throws PSQLExcepti
}
}
- /**
- * getCertificateChain and getPrivateKey cannot throw exeptions, therefore any exception is stored
- * in {@link #error} and can be raised by this method.
- *
- * @throws PSQLException if any exception is stored in {@link #error} and can be raised
- */
- public void throwKeyManagerException() throws PSQLException {
- if (error != null) {
- throw error;
- }
- }
-
- @Override
- public String /* @Nullable */ [] getClientAliases(String keyType, Principal /* @Nullable */ [] principals) {
- String alias = chooseClientAlias(new String[]{keyType}, principals, (Socket) null);
- return alias == null ? null : new String[]{alias};
- }
-
- @Override
- public /* @Nullable */ String chooseClientAlias(String[] strings, Principal /* @Nullable */ [] principals,
- /* @Nullable */ Socket socket) {
- if (principals == null || principals.length == 0) {
- // Postgres 8.4 and earlier do not send the list of accepted certificate authorities
- // to the client. See BUG #5468. We only hope, that our certificate will be accepted.
- return "user";
- } else {
- // Sending a wrong certificate makes the connection rejected, even, if clientcert=0 in
- // pg_hba.conf.
- // therefore we only send our certificate, if the issuer is listed in issuers
- X509Certificate[] certchain = getCertificateChain("user");
- if (certchain == null) {
- return null;
- } else {
- X500Principal ourissuer = certchain[certchain.length - 1].getIssuerX500Principal();
- boolean found = false;
- for (Principal issuer : principals) {
- if (ourissuer.equals(issuer)) {
- found = true;
- }
- }
- return (found ? "user" : null);
- }
- }
- }
-
- @Override
- public String /* @Nullable */ [] getServerAliases(String s, Principal /* @Nullable */ [] principals) {
- return new String[]{};
- }
-
- @Override
- public /* @Nullable */ String chooseServerAlias(String s, Principal /* @Nullable */ [] principals,
- /* @Nullable */ Socket socket) {
- // we are not a server
- return null;
- }
-
@Override
public X509Certificate /* @Nullable */ [] getCertificateChain(String alias) {
try {
@@ -116,7 +55,7 @@ public void throwKeyManagerException() throws PSQLException {
X509Certificate[] x509Certificates = new X509Certificate[certs.length];
int i = 0;
for (Certificate cert : certs) {
- x509Certificates[i++] = (X509Certificate)cert;
+ x509Certificates[i++] = (X509Certificate) cert;
}
return x509Certificates;
} catch (Exception kse) {
@@ -140,8 +79,7 @@ public void throwKeyManagerException() throws PSQLException {
if (pkEntry == null) {
return null;
}
- PrivateKey myPrivateKey = pkEntry.getPrivateKey();
- return myPrivateKey;
+ return pkEntry.getPrivateKey();
} catch (Exception ioex ) {
error = new PSQLException(GT.tr("Could not read SSL key file {0}.", keyfile),
PSQLState.CONNECTION_FAILURE, ioex);
@@ -149,33 +87,34 @@ public void throwKeyManagerException() throws PSQLException {
return null;
}
- private synchronized void loadKeyStore() throws Exception {
-
- if (keystoreLoaded) {
- return;
- }
- // We call back for the password
- PasswordCallback pwdcb = new PasswordCallback(GT.tr("Enter SSL password: "), false);
- try {
- cbh.handle(new Callback[]{pwdcb});
- } catch (UnsupportedCallbackException ucex) {
- if ((cbh instanceof LibPQFactory.ConsoleCallbackHandler)
- && ("Console is not available".equals(ucex.getMessage()))) {
- error = new PSQLException(GT
- .tr("Could not read password for SSL key file, console is not available."),
- PSQLState.CONNECTION_FAILURE, ucex);
- } else {
- error =
- new PSQLException(
- GT.tr("Could not read password for SSL key file by callbackhandler {0}.",
- cbh.getClass().getName()),
+ private void loadKeyStore() throws Exception {
+ try (ResourceLock ignore = lock.obtain()) {
+ if (keystoreLoaded) {
+ return;
+ }
+ // We call back for the password
+ PasswordCallback pwdcb = new PasswordCallback(GT.tr("Enter SSL password: "), false);
+ try {
+ cbh.handle(new Callback[]{pwdcb});
+ } catch (UnsupportedCallbackException ucex) {
+ if ((cbh instanceof LibPQFactory.ConsoleCallbackHandler)
+ && ("Console is not available".equals(ucex.getMessage()))) {
+ error = new PSQLException(GT
+ .tr("Could not read password for SSL key file, console is not available."),
PSQLState.CONNECTION_FAILURE, ucex);
+ } else {
+ error =
+ new PSQLException(
+ GT.tr("Could not read password for SSL key file by callbackhandler {0}.",
+ cbh.getClass().getName()),
+ PSQLState.CONNECTION_FAILURE, ucex);
+ }
+
}
+ keyStore.load(FileUtils.newBufferedInputStream(keyfile), pwdcb.getPassword());
+ keystoreLoaded = true;
}
-
- keyStore.load(new FileInputStream(new File(keyfile)), pwdcb.getPassword());
- keystoreLoaded = true;
}
}
diff --git a/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java b/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
index 3f1805e..bd47d69 100644
--- a/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
+++ b/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
@@ -6,10 +6,10 @@
package org.postgresql.ssl;
import org.postgresql.util.GT;
+import org.postgresql.util.internal.FileUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -26,10 +26,10 @@
import javax.net.ssl.X509TrustManager;
/**
- *
Provides a SSLSocketFactory that authenticates the remote server against an explicit pre-shared
+ * Provides a SSLSocketFactory that authenticates the remote server against an explicit pre-shared
* SSL certificate. This is more secure than using the NonValidatingFactory as it prevents "man in
* the middle" attacks. It is also more secure than relying on a central CA signing your server's
- * certificate as it pins the server's certificate.
+ * certificate as it pins the server's certificate.
*
*
This class requires a single String parameter specified by setting the connection property
* sslfactoryarg. The value of this property is the PEM-encoded remote server's SSL
@@ -88,14 +88,14 @@ public class SingleCertValidatingFactory extends WrappedFactory {
private static final String SYS_PROP_PREFIX = "sys:";
public SingleCertValidatingFactory(String sslFactoryArg) throws GeneralSecurityException {
- if (sslFactoryArg == null || sslFactoryArg.equals("")) {
+ if (sslFactoryArg == null || "".equals(sslFactoryArg)) {
throw new GeneralSecurityException(GT.tr("The sslfactoryarg property may not be empty."));
}
InputStream in = null;
try {
if (sslFactoryArg.startsWith(FILE_PREFIX)) {
String path = sslFactoryArg.substring(FILE_PREFIX.length());
- in = new BufferedInputStream(new FileInputStream(path));
+ in = FileUtils.newBufferedInputStream(path);
} else if (sslFactoryArg.startsWith(CLASSPATH_PREFIX)) {
String path = sslFactoryArg.substring(CLASSPATH_PREFIX.length());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
@@ -187,15 +187,18 @@ public SingleCertTrustManager(InputStream in) throws IOException, GeneralSecurit
}
}
+ @Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
+ @Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
trustManager.checkServerTrusted(chain, authType);
}
+ @Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{cert};
}
diff --git a/src/main/java/org/postgresql/ssl/WrappedFactory.java b/src/main/java/org/postgresql/ssl/WrappedFactory.java
index 0a983d6..66e940c 100644
--- a/src/main/java/org/postgresql/ssl/WrappedFactory.java
+++ b/src/main/java/org/postgresql/ssl/WrappedFactory.java
@@ -19,36 +19,43 @@ public abstract class WrappedFactory extends SSLSocketFactory {
// The field is indeed not initialized in this class, however it is a part of public API,
// so it is hard to fix.
- @SuppressWarnings("initialization.fields.uninitialized")
+ @SuppressWarnings("initialization.field.uninitialized")
protected SSLSocketFactory factory;
+ @Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return factory.createSocket(host, port);
}
+ @Override
public Socket createSocket(String host, int port) throws IOException {
return factory.createSocket(host, port);
}
+ @Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException {
return factory.createSocket(host, port, localHost, localPort);
}
+ @Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return factory.createSocket(address, port, localAddress, localPort);
}
+ @Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException {
return factory.createSocket(socket, host, port, autoClose);
}
+ @Override
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
+ @Override
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
diff --git a/src/main/java/org/postgresql/ssl/jdbc4/LibPQFactory.java b/src/main/java/org/postgresql/ssl/jdbc4/LibPQFactory.java
index e73588a..40e5139 100644
--- a/src/main/java/org/postgresql/ssl/jdbc4/LibPQFactory.java
+++ b/src/main/java/org/postgresql/ssl/jdbc4/LibPQFactory.java
@@ -75,6 +75,7 @@ public static boolean verifyHostName(String hostname, String pattern) {
* @deprecated use PgjdbcHostnameVerifier
*/
@Deprecated
+ @Override
public boolean verify(String hostname, SSLSession session) {
if (!sslMode.verifyPeerName()) {
return true;
diff --git a/src/main/java/org/postgresql/sspi/ISSPIClient.java b/src/main/java/org/postgresql/sspi/ISSPIClient.java
index fcb14e4..02798be 100644
--- a/src/main/java/org/postgresql/sspi/ISSPIClient.java
+++ b/src/main/java/org/postgresql/sspi/ISSPIClient.java
@@ -10,8 +10,8 @@
import java.sql.SQLException;
/**
- *
Use Waffle-JNI to support SSPI authentication when PgJDBC is running on a Windows
- * client and talking to a Windows server.
+ * Use Waffle-JNI to support SSPI authentication when PgJDBC is running on a Windows
+ * client and talking to a Windows server.
*
*
SSPI is not supported on a non-Windows client.
*/
diff --git a/src/main/java/org/postgresql/translation/bg.po b/src/main/java/org/postgresql/translation/bg.po
index 69ddd59..b64ee41 100644
--- a/src/main/java/org/postgresql/translation/bg.po
+++ b/src/main/java/org/postgresql/translation/bg.po
@@ -888,7 +888,7 @@ msgstr "Невъзможно преобразуване на данни в же
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Размера за fetch size трябва да бъде по-голям или равен на 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/cs.po b/src/main/java/org/postgresql/translation/cs.po
index e5af30d..59f5e47 100644
--- a/src/main/java/org/postgresql/translation/cs.po
+++ b/src/main/java/org/postgresql/translation/cs.po
@@ -846,7 +846,7 @@ msgstr "Nemohu p�elo�it data do po�adovan�ho k�dov�n�."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Nabran� velikost mus� b�t nez�porn�."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/de.po b/src/main/java/org/postgresql/translation/de.po
index 6492344..21a2a51 100644
--- a/src/main/java/org/postgresql/translation/de.po
+++ b/src/main/java/org/postgresql/translation/de.po
@@ -926,7 +926,7 @@ msgstr "Die Daten konnten nicht in die gew�nschte Kodierung gewandelt werden."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Die Fetch-Gr��e muss ein Wert gr��er oder gleich Null sein."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/es.po b/src/main/java/org/postgresql/translation/es.po
index c8c6ef2..4715a20 100644
--- a/src/main/java/org/postgresql/translation/es.po
+++ b/src/main/java/org/postgresql/translation/es.po
@@ -848,7 +848,7 @@ msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/fr.po b/src/main/java/org/postgresql/translation/fr.po
index e015746..0b3e21c 100644
--- a/src/main/java/org/postgresql/translation/fr.po
+++ b/src/main/java/org/postgresql/translation/fr.po
@@ -910,7 +910,7 @@ msgstr "Impossible de traduire les donn�es dans l''encodage d�sir�."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Fetch size doit �tre une valeur sup�rieur ou �gal � 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/it.po b/src/main/java/org/postgresql/translation/it.po
index d226e23..84e8365 100644
--- a/src/main/java/org/postgresql/translation/it.po
+++ b/src/main/java/org/postgresql/translation/it.po
@@ -916,7 +916,7 @@ msgstr "Impossibile tradurre i dati nella codifica richiesta."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "La dimensione dell''area di �fetch� deve essere maggiore o eguale a 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/ja.po b/src/main/java/org/postgresql/translation/ja.po
index ae5b7f6..dad60f5 100644
--- a/src/main/java/org/postgresql/translation/ja.po
+++ b/src/main/java/org/postgresql/translation/ja.po
@@ -887,7 +887,7 @@ msgstr "データを指定されたエンコーディングに変換すること
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "フェッチサイズは、0または、より大きな値でなくてはなりません。"
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/ko.po b/src/main/java/org/postgresql/translation/ko.po
new file mode 100644
index 0000000..48e8fe7
--- /dev/null
+++ b/src/main/java/org/postgresql/translation/ko.po
@@ -0,0 +1,2017 @@
+# ko.po
+# JDBC Translation into Korean
+# Distributed under the same licensing terms as the JDBC driver itself.
+# Sheeraz Majeed, 2024.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: head-ko\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2024-05-29 11:10+0500\n"
+"Last-Translator: Sheeraz Majeed\n"
+"Language-Team: PostgreSQL Translators\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/main/java/org/postgresql/Driver.java:259
+msgid "Error loading default settings from driverconfig.properties"
+msgstr "driverconfig.properties에서 기본 설정을 로드하는 중 오류 발생"
+
+#: src/main/java/org/postgresql/Driver.java:271
+msgid "Properties for the driver contains a non-string value for the key "
+msgstr "드라이버의 속성에 문자열이 아닌 값이 포함되어 있습니다. 키: "
+
+#: src/main/java/org/postgresql/Driver.java:281
+#, java-format
+msgid "Unable to parse URL {0}"
+msgstr "URL {0} 을(를) 파싱할 수 없습니다"
+
+#: src/main/java/org/postgresql/Driver.java:316
+msgid ""
+"Your security policy has prevented the connection from being attempted. You "
+"probably need to grant the connect java.net.SocketPermission to the database "
+"server host and port that you wish to connect to."
+msgstr ""
+"보안 정책으로 인해 연결을 시도할 수 없습니다. 데이터베이스 서버 호스트와 연결"
+"하고자 하는 포트에 java.net.SocketPermission 연결 권한을 부여해야 할 것입니"
+"다."
+
+#: src/main/java/org/postgresql/Driver.java:322
+#: src/main/java/org/postgresql/Driver.java:402
+msgid ""
+"Something unusual has occurred to cause the driver to fail. Please report "
+"this exception."
+msgstr ""
+"드라이버가 실패하는 비정상적인 현상이 발생했습니다. 이 예외를 보고해 주세요."
+
+#: src/main/java/org/postgresql/Driver.java:410
+msgid "Connection attempt timed out."
+msgstr "연결 시도가 시간 초과되었습니다."
+
+#: src/main/java/org/postgresql/Driver.java:423
+msgid "Interrupted while attempting to connect."
+msgstr "연결 시도 중 중단되었습니다."
+
+#: src/main/java/org/postgresql/Driver.java:742
+#, java-format
+msgid "Method {0} is not yet implemented."
+msgstr "메서드 {0} 이(가) 아직 구현되지 않았습니다."
+
+#: src/main/java/org/postgresql/PGConnection.java:279
+msgid "Expected a row when reading password_encryption but none was found"
+msgstr ""
+"password_encryption을 읽을 때 행이 예상되었으나 아무 것도 발견되지 않았습니"
+"다."
+
+#: src/main/java/org/postgresql/PGConnection.java:284
+msgid "SHOW password_encryption returned null value"
+msgstr "SHOW password_encryption이 null 값을 반환했습니다."
+
+#: src/main/java/org/postgresql/PGProperty.java:947
+#: src/main/java/org/postgresql/PGProperty.java:967
+#, java-format
+msgid "{0} parameter value must be an integer but was: {1}"
+msgstr "{0} 매개 변수 값은 정수여야 하지만 실제로는: {1} 입니다."
+
+#: src/main/java/org/postgresql/copy/CopyManager.java:50
+#, java-format
+msgid "Requested CopyIn but got {0}"
+msgstr "요청된 CopyIn 대신 {0} 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/copy/CopyManager.java:61
+#, java-format
+msgid "Requested CopyOut but got {0}"
+msgstr "요청된 CopyOut 대신 {0} 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/copy/CopyManager.java:72
+#, java-format
+msgid "Requested CopyDual but got {0}"
+msgstr "요청된 CopyDual 대신 {0} 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/copy/PGCopyInputStream.java:60
+#, java-format
+msgid "Copying from database failed: {0}"
+msgstr "데이터베이스에서 복사 실패: {0}"
+
+#: src/main/java/org/postgresql/copy/PGCopyInputStream.java:74
+#: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:104
+msgid "This copy stream is closed."
+msgstr "이 복사 스트림은 닫혀 있습니다."
+
+#: src/main/java/org/postgresql/copy/PGCopyInputStream.java:125
+msgid "Read from copy failed."
+msgstr "복사에서 읽기 실패."
+
+#: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:81
+#, java-format
+msgid "Cannot write to copy a byte of value {0}"
+msgstr "값 {0} 의 바이트를 복사에 쓸 수 없습니다."
+
+#: src/main/java/org/postgresql/core/CommandCompleteParser.java:74
+#, java-format
+msgid "Unable to parse the count in command completion tag: {0}."
+msgstr "명령 완료 태그의 카운트를 파싱할 수 없습니다: {0}."
+
+#: src/main/java/org/postgresql/core/ConnectionFactory.java:62
+#, java-format
+msgid "A connection could not be made using the requested protocol {0}."
+msgstr "요청된 프로토콜 {0} 을(를) 사용하여 연결할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/Oid.java:142
+#, java-format
+msgid "oid type {0} not known and not a number"
+msgstr "oid 유형 {0} 이(가) 알려지지 않았고 숫자가 아닙니다."
+
+#: src/main/java/org/postgresql/core/PGStream.java:697
+#: src/main/java/org/postgresql/util/PGbytea.java:199
+#, java-format
+msgid "Premature end of input stream, expected {0} bytes, but only read {1}."
+msgstr "입력 스트림의 조기 종료, 예상 {0} 바이트 중 {1} 바이트만 읽었습니다."
+
+#: src/main/java/org/postgresql/core/PGStream.java:738
+#, java-format
+msgid "Expected an EOF from server, got: {0}"
+msgstr "서버로부터 EOF를 기대했지만, 대신: {0} 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/core/PGStream.java:838
+#, java-format
+msgid ""
+"Result set exceeded maxResultBuffer limit. Received: {0}; Current limit: {1}"
+msgstr ""
+"결과 집합이 maxResultBuffer 한도를 초과했습니다. 받은 값: {0}; 현재 한도: {1}"
+
+#: src/main/java/org/postgresql/core/Parser.java:1201
+#, java-format
+msgid "Malformed function or procedure escape syntax at offset {0}."
+msgstr "오프셋 {0} 에서 잘못된 함수 또는 절차 이스케이프 구문입니다."
+
+#: src/main/java/org/postgresql/core/SetupQueryRunner.java:68
+msgid "An unexpected result was returned by a query."
+msgstr "쿼리가 예상치 못한 결과를 반환했습니다."
+
+#: src/main/java/org/postgresql/core/SocketFactoryFactory.java:43
+#, java-format
+msgid "The SocketFactory class provided {0} could not be instantiated."
+msgstr "제공된 SocketFactory 클래스 {0} 을(를) 인스턴스화할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/SocketFactoryFactory.java:68
+#, java-format
+msgid "The SSLSocketFactory class provided {0} could not be instantiated."
+msgstr "제공된 SSLSocketFactory 클래스 {0} 을(를) 인스턴스화할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/Utils.java:72
+#: src/main/java/org/postgresql/core/Utils.java:89
+msgid "Zero bytes may not occur in string parameters."
+msgstr "문자열 매개 변수에 0 바이트가 발생할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/Utils.java:99
+#: src/main/java/org/postgresql/core/Utils.java:149
+msgid "No IOException expected from StringBuffer or StringBuilder"
+msgstr "StringBuffer 또는 StringBuilder에서 IOException이 발생하지 않았습니다."
+
+#: src/main/java/org/postgresql/core/Utils.java:138
+msgid "Zero bytes may not occur in identifiers."
+msgstr "식별자에 0 바이트가 발생할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/AuthenticationPluginManager.java:73
+#, java-format
+msgid "Unable to load Authentication Plugin {0}"
+msgstr "인증 플러그인 {0} 을(를) 로드할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/AuthenticationPluginManager.java:111
+#, java-format
+msgid ""
+"The server requested password-based authentication, but no password was "
+"provided by plugin {0}"
+msgstr ""
+"서버가 비밀번호 기반 인증을 요청했으나, 플러그인 {0} 에서 비밀번호를 제공하"
+"지 않았습니다."
+
+#: src/main/java/org/postgresql/core/v3/CompositeParameterList.java:38
+#: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:60
+#: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:71
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:225
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3258
+#: src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java:420
+#, java-format
+msgid "The column index is out of range: {0}, number of columns: {1}."
+msgstr "열 인덱스가 범위를 벗어났습니다: {0}, 열 수: {1}."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:130
+msgid "User cannot be null"
+msgstr "사용자는 null일 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:133
+msgid "Database cannot be null"
+msgstr "데이터베이스는 null일 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:227
+#, java-format
+msgid "Invalid targetServerType value: {0}"
+msgstr "잘못된 targetServerType 값: {0}"
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:347
+#, java-format
+msgid ""
+"Connection to {0} refused. Check that the hostname and port are correct and "
+"that the postmaster is accepting TCP/IP connections."
+msgstr ""
+"{0} 에 대한 연결이 거부되었습니다. 호스트 이름과 포트가 올바른지, 그리고 "
+"postmaster가 TCP/IP 연결을 수락하고 있는지 확인하십시오."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:358
+#: src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java:139
+msgid "The connection attempt failed."
+msgstr "연결 시도가 실패했습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:373
+#, java-format
+msgid "Could not find a server with specified targetServerType: {0}"
+msgstr "지정된 targetServerType을 가진 서버를 찾을 수 없습니다: {0}"
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:517
+msgid "The server does not support GSS Encoding."
+msgstr "서버가 GSS 인코딩을 지원하지 않습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:530
+msgid "The server does not support GSS Encryption."
+msgstr "서버가 GSS 암호화를 지원하지 않습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:558
+msgid "An error occurred while setting up the GSS Encoded connection."
+msgstr "GSS 인코딩된 연결을 설정하는 동안 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:603
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:615
+msgid "The server does not support SSL."
+msgstr "서버가 SSL을 지원하지 않습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:629
+msgid "An error occurred while setting up the SSL connection."
+msgstr "SSL 연결을 설정하는 동안 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:844
+msgid ""
+"The server requested SCRAM-based authentication, but no password was "
+"provided."
+msgstr "서버가 SCRAM 기반 인증을 요청했지만, 비밀번호가 제공되지 않았습니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:850
+msgid ""
+"The server requested SCRAM-based authentication, but the password is an "
+"empty string."
+msgstr "서버가 SCRAM 기반 인증을 요청했지만, 비밀번호가 빈 문자열입니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:862
+msgid ""
+"SCRAM authentication is not supported by this driver. You need JDK >= 8 and "
+"pgjdbc >= 42.2.0 (not \".jre\" versions)"
+msgstr ""
+"이 드라이버는 SCRAM 인증을 지원하지 않습니다. JDK >= 8 및 pgjdbc >= "
+"42.2.0( \".jre\" 버전이 아님)이 필요합니다."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:883
+#, java-format
+msgid ""
+"The authentication type {0} is not supported. Check that you have configured "
+"the pg_hba.conf file to include the client''s IP address or subnet, and that "
+"it is using an authentication scheme supported by the driver."
+msgstr ""
+"인증 유형 {0} 이(가) 지원되지 않습니다. pg_hba.conf 파일에 클라이언트의 IP 주"
+"소 또는 서브넷이 포함되어 있고, 드라이버가 지원하는 인증 방식을 사용하고 있는"
+"지 확인하십시오."
+
+#: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:890
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2821
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2854
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2858
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2934
+#: src/main/java/org/postgresql/gss/GssAction.java:156
+msgid "Protocol error. Session setup failed."
+msgstr "프로토콜 오류. 세션 설정 실패."
+
+#: src/main/java/org/postgresql/core/v3/CopyInImpl.java:58
+msgid "CopyIn copy direction can't receive data"
+msgstr "CopyIn 복사 방향은 데이터를 수신할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/CopyOperationImpl.java:65
+msgid "CommandComplete expected COPY but got: "
+msgstr "CommandComplete는 COPY를 예상했으나 대신 다음을 받았습니다: "
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:205
+msgid "Tried to obtain lock while already holding it"
+msgstr "이미 잠금을 보유한 상태에서 잠금을 얻으려고 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:221
+msgid "Tried to break lock on database connection"
+msgstr "데이터베이스 연결에서 잠금을 해제하려고 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:239
+msgid "Interrupted while waiting to obtain lock on database connection"
+msgstr "데이터베이스 연결에서 잠금을 얻기 위해 대기하는 동안 중단되었습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:392
+msgid "Unable to bind parameter values for statement."
+msgstr "문에 대한 매개 변수 값을 바인딩할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:398
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:585
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:671
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:721
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:852
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2615
+#: src/main/java/org/postgresql/util/StreamWrapper.java:85
+msgid "An I/O error occurred while sending to the backend."
+msgstr "백엔드로 전송하는 동안 I/O 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:451
+msgid "Error releasing savepoint"
+msgstr "세이브포인트 해제 오류"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:642
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:690
+#, java-format
+msgid "Expected command status BEGIN, got {0}."
+msgstr "예상한 명령 상태 BEGIN 대신 {0} 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:695
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2200
+#, java-format
+msgid "Unexpected command status: {0}."
+msgstr "예상치 못한 명령 상태: {0}."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:810
+msgid "An error occurred while trying to get the socket timeout."
+msgstr "소켓 시간 초과를 가져오는 동안 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:845
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:935
+#, java-format
+msgid "Unknown Response Type {0}."
+msgstr "알 수 없는 응답 유형 {0}."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:869
+msgid "An error occurred while trying to reset the socket timeout."
+msgstr "소켓 시간 초과를 재설정하는 동안 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:982
+msgid "Database connection failed when starting copy"
+msgstr "복사 시작 시 데이터베이스 연결 실패"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1020
+msgid "Tried to cancel an inactive copy operation"
+msgstr "비활성 복사 작업을 취소하려고 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1059
+msgid "Database connection failed when canceling copy operation"
+msgstr "복사 작업을 취소하는 동안 데이터베이스 연결 실패"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1075
+msgid "Missing expected error response to copy cancel request"
+msgstr "복사 취소 요청에 대한 예상 오류 응답이 누락되었습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1079
+#, java-format
+msgid "Got {0} error responses to single copy cancel request"
+msgstr "단일 복사 취소 요청에 대해 {0} 오류 응답을 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1095
+msgid "Tried to end inactive copy"
+msgstr "비활성 복사를 끝내려고 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1110
+msgid "Database connection failed when ending copy"
+msgstr "복사를 종료하는 동안 데이터베이스 연결 실패"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1130
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1159
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1180
+msgid "Tried to write to an inactive copy operation"
+msgstr "비활성 복사 작업에 쓰기를 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1141
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1171
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1187
+msgid "Database connection failed when writing to copy"
+msgstr "복사에 쓰는 동안 데이터베이스 연결 실패"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1204
+msgid "Tried to read from inactive copy"
+msgstr "비활성 복사에서 읽기를 시도했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1211
+msgid "Database connection failed when reading from copy"
+msgstr "복사에서 읽는 동안 데이터베이스 연결 실패"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1236
+msgid "PGStream is closed"
+msgstr "PGStream이 닫혔습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1297
+#, java-format
+msgid "Received CommandComplete ''{0}'' without an active copy operation"
+msgstr "활성 복사 작업 없이 CommandComplete ''{0}'' 을(를) 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1322
+#, java-format
+msgid "Got CopyInResponse from server during an active {0}"
+msgstr "활성 {0} 중 서버로부터 CopyInResponse를 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1336
+#, java-format
+msgid "Got CopyOutResponse from server during an active {0}"
+msgstr "활성 {0} 중 서버로부터 CopyOutResponse를 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1350
+#, java-format
+msgid "Got CopyBothResponse from server during an active {0}"
+msgstr "활성 {0} 중 서버로부터 CopyBothResponse를 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1369
+msgid "Got CopyData without an active copy operation"
+msgstr "활성 복사 작업 없이 CopyData를 받았습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1373
+#, java-format
+msgid "Unexpected copydata from server for {0}"
+msgstr "{0} 에 대한 서버의 예상치 못한 copydata"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1433
+#, java-format
+msgid "Unexpected packet type during copy: {0}"
+msgstr "복사 중 예상치 못한 패킷 유형: {0}"
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1736
+#, java-format
+msgid ""
+"Bind message length {0} too long. This can be caused by very large or "
+"incorrect length specifications on InputStream parameters."
+msgstr ""
+"바인드 메시지 길이 {0} 이 너무 깁니다. 이는 매우 크거나 잘못된 길이 지정이 "
+"InputStream 매개 변수에 포함되어 있을 때 발생할 수 있습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2383
+msgid "Ran out of memory retrieving query results."
+msgstr "쿼리 결과를 검색하는 동안 메모리가 부족했습니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2549
+msgid "COPY commands are only supported using the CopyManager API."
+msgstr "COPY 명령은 CopyManager API를 사용하여서만 지원됩니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2891
+#, java-format
+msgid ""
+"The server''s client_encoding parameter was changed to {0}. The JDBC driver "
+"requires client_encoding to be UTF8 for correct operation."
+msgstr ""
+"서버의 client_encoding 매개 변수가 {0} 으로 변경되었습니다. JDBC 드라이버는 "
+"올바른 작동을 위해 client_encoding이 UTF8이어야 합니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2901
+#, java-format
+msgid ""
+"The server''s DateStyle parameter was changed to {0}. The JDBC driver "
+"requires DateStyle to begin with ISO for correct operation."
+msgstr ""
+"서버의 DateStyle 매개 변수가 {0} 으로 변경되었습니다. JDBC 드라이버는 올바른 "
+"작동을 위해 DateStyle이 ISO로 시작해야 합니다."
+
+#: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2914
+#, java-format
+msgid ""
+"The server''s standard_conforming_strings parameter was reported as {0}. The "
+"JDBC driver expected on or off."
+msgstr ""
+"서버의 standard_conforming_strings 매개 변수가 {0} 으로 보고되었습니다. JDBC "
+"드라이버는 켜짐 또는 꺼짐을 예상했습니다."
+
+#: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:262
+#, java-format
+msgid "Unable to convert bytea parameter at position {0} to literal"
+msgstr "위치 {0} 의 bytea 매개 변수를 리터럴로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:402
+#, java-format
+msgid "No value specified for parameter {0}."
+msgstr "매개 변수 {0} 에 대해 지정된 값이 없습니다."
+
+#: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:607
+#, java-format
+msgid "Added parameters index out of range: {0}, number of columns: {1}."
+msgstr "추가된 매개 변수 인덱스가 범위를 벗어났습니다: {0}, 열 수: {1}."
+
+#: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:152
+#, java-format
+msgid "Unexpected packet type during replication: {0}"
+msgstr "복제 중 예상치 못한 패킷 유형: {0}"
+
+#: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:283
+msgid "This replication stream has been closed."
+msgstr "이 복제 스트림은 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/PGPooledConnection.java:129
+msgid "This PooledConnection has already been closed."
+msgstr "이 PooledConnection은 이미 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/PGPooledConnection.java:331
+msgid ""
+"Connection has been closed automatically because a new connection was opened "
+"for the same PooledConnection or the PooledConnection has been closed."
+msgstr ""
+"같은 PooledConnection에 대해 새 연결이 열리거나 PooledConnection이 닫혔기 때"
+"문에 연결이 자동으로 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/PGPooledConnection.java:332
+msgid "Connection has been closed."
+msgstr "연결이 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/PGPooledConnection.java:439
+msgid "Statement has been closed."
+msgstr "Statement가 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:286
+msgid "Failed to setup DataSource."
+msgstr "DataSource 설정 실패."
+
+#: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:390
+msgid "DataSource has been closed."
+msgstr "DataSource가 닫혔습니다."
+
+#: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1442
+#: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1452
+#, java-format
+msgid "Unsupported property name: {0}"
+msgstr "지원되지 않는 속성 이름: {0}"
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:89
+#, java-format
+msgid "Fastpath call {0} - No result was returned and we expected a numeric."
+msgstr "Fastpath 호출 {0} - 결과가 반환되지 않았고 숫자를 예상했습니다."
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:167
+#, java-format
+msgid "Fastpath call {0} - No result was returned and we expected an integer."
+msgstr "Fastpath 호출 {0} - 결과가 반환되지 않았고 정수를 예상했습니다."
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:175
+#, java-format
+msgid ""
+"Fastpath call {0} - No result was returned or wrong size while expecting an "
+"integer."
+msgstr ""
+"Fastpath 호출 {0} - 결과가 반환되지 않았거나 정수를 예상하는 동안 잘못된 크기"
+"입니다."
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:192
+#, java-format
+msgid "Fastpath call {0} - No result was returned and we expected a long."
+msgstr "Fastpath 호출 {0} - 결과가 반환되지 않았고 long을 예상했습니다."
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:200
+#, java-format
+msgid ""
+"Fastpath call {0} - No result was returned or wrong size while expecting a "
+"long."
+msgstr ""
+"Fastpath 호출 {0} - 결과가 반환되지 않았거나 long을 예상하는 동안 잘못된 크기"
+"입니다."
+
+#: src/main/java/org/postgresql/fastpath/Fastpath.java:303
+#, java-format
+msgid "The fastpath function {0} is unknown."
+msgstr "Fastpath 함수 {0} 은(는) 알려지지 않았습니다."
+
+#: src/main/java/org/postgresql/geometric/PGbox.java:83
+#: src/main/java/org/postgresql/geometric/PGcircle.java:81
+#: src/main/java/org/postgresql/geometric/PGcircle.java:89
+#: src/main/java/org/postgresql/geometric/PGline.java:142
+#: src/main/java/org/postgresql/geometric/PGline.java:151
+#: src/main/java/org/postgresql/geometric/PGlseg.java:76
+#: src/main/java/org/postgresql/geometric/PGpoint.java:88
+#, java-format
+msgid "Conversion to type {0} failed: {1}."
+msgstr "유형 {0} 으로 변환 실패: {1}."
+
+#: src/main/java/org/postgresql/geometric/PGpath.java:78
+#, java-format
+msgid "Cannot tell if path is open or closed: {0}."
+msgstr "경로가 열려 있는지 닫혀 있는지 알 수 없습니다: {0}."
+
+#: src/main/java/org/postgresql/gss/GssAction.java:167
+#: src/main/java/org/postgresql/gss/GssEncAction.java:147
+#: src/main/java/org/postgresql/gss/MakeGSS.java:173
+#: src/main/java/org/postgresql/gss/MakeGSS.java:181
+msgid "GSS Authentication failed"
+msgstr "GSS 인증 실패"
+
+#: src/main/java/org/postgresql/gss/MakeGSS.java:169
+msgid ""
+"Neither Subject.doAs (Java before 18) nor Subject.callAs (Java 18+) method "
+"found"
+msgstr ""
+"Subject.doAs (Java 18 이전) 또는 Subject.callAs (Java 18+) 메서드가 발견되지 "
+"않았습니다."
+
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:84
+msgid ""
+"Truncation of large objects is only implemented in 8.3 and later servers."
+msgstr "대형 객체의 잘림은 8.3 이후의 서버에서만 구현됩니다."
+
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:89
+msgid "Cannot truncate LOB to a negative length."
+msgstr "LOB를 음수 길이로 잘릴 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:96
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:249
+#, java-format
+msgid "PostgreSQL LOBs can only index to: {0}"
+msgstr "PostgreSQL LOB는 다음으로만 인덱싱할 수 있습니다: {0}"
+
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:245
+msgid "LOB positioning offsets start at 1."
+msgstr "LOB 위치 오프셋은 1부터 시작합니다."
+
+#: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:261
+msgid "free() was called on this LOB previously"
+msgstr "이 LOB에서 이전에 free()가 호출되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/ArrayDecoding.java:284
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2440
+#: src/main/java/org/postgresql/util/HStoreConverter.java:46
+#: src/main/java/org/postgresql/util/HStoreConverter.java:82
+msgid ""
+"Invalid character data was found. This is most likely caused by stored data "
+"containing characters that are invalid for the character set the database "
+"was created in. The most common example of this is storing 8bit data in a "
+"SQL_ASCII database."
+msgstr ""
+"잘못된 문자 데이터가 발견되었습니다. 이는 데이터베이스가 생성된 문자 세트에 "
+"대해 잘못된 문자를 포함하는 저장된 데이터로 인해 발생할 가능성이 큽니다. 가"
+"장 일반적인 예는 SQL_ASCII 데이터베이스에 8비트 데이터를 저장하는 것입니다."
+
+#: src/main/java/org/postgresql/jdbc/ArrayEncoding.java:867
+#: src/main/java/org/postgresql/jdbc/ArrayEncoding.java:904
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1181
+msgid "Unable to translate data into the desired encoding."
+msgstr "데이터를 원하는 인코딩으로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/ArrayEncoding.java:1124
+#: src/main/java/org/postgresql/jdbc/ArrayEncoding.java:1135
+#: src/main/java/org/postgresql/jdbc/ArrayEncoding.java:1161
+#, java-format
+msgid "Invalid elements {0}"
+msgstr "잘못된 요소 {0}"
+
+#: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:102
+msgid "Too many update results were returned."
+msgstr "너무 많은 업데이트 결과가 반환되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:163
+#, java-format
+msgid ""
+"Batch entry {0} {1} was aborted: {2} Call getNextException to see other "
+"errors in the batch."
+msgstr ""
+"배치 항목 {0} {1} 이(가) 중단되었습니다: {2} 배치의 다른 오류를 보려면 "
+"getNextException을 호출하십시오."
+
+#: src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java:99
+#, java-format
+msgid "Cannot cast to boolean: \"{0}\""
+msgstr "boolean으로 캐스팅할 수 없습니다: \"{0}\""
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:240
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:170
+#, java-format
+msgid "{0} function takes four and only four argument."
+msgstr "{0} 함수는 네 개의 인수만 받습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:270
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:337
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:741
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:199
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:266
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:667
+#, java-format
+msgid "{0} function takes two and only two arguments."
+msgstr "{0} 함수는 두 개의 인수만 받습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:288
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:441
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:467
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:526
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:729
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:214
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:358
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:384
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:443
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:656
+#, java-format
+msgid "{0} function takes one and only one argument."
+msgstr "{0} 함수는 한 개의 인수만 받습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:312
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:386
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:241
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:310
+#, java-format
+msgid "{0} function takes two or three arguments."
+msgstr "{0} 함수는 두 개 또는 세 개의 인수를 받습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:411
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:426
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:694
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:720
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:647
+#, java-format
+msgid "{0} function doesn''t take any argument."
+msgstr "{0} 함수는 인수를 받지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:587
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:640
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:503
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:573
+#, java-format
+msgid "{0} function takes three and only three arguments."
+msgstr "{0} 함수는 세 개의 인수만 받습니다."
+
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:600
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:621
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:624
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:657
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:670
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:673
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:513
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:530
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:587
+#: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:599
+#, java-format
+msgid "Interval {0} not yet implemented"
+msgstr "구간 {0} 은(는) 아직 구현되지 않았습니다."
+
+#: src/main/java/org/postgresql/jdbc/GSSEncMode.java:61
+#, java-format
+msgid "Invalid gssEncMode value: {0}"
+msgstr "잘못된 gssEncMode 값: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:40
+#: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:55
+#: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:73
+msgid "Cannot reference a savepoint after it has been released."
+msgstr "해제된 후에는 세이브포인트를 참조할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:45
+msgid "Cannot retrieve the id of a named savepoint."
+msgstr "이름이 지정된 세이브포인트의 ID를 검색할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:60
+msgid "Cannot retrieve the name of an unnamed savepoint."
+msgstr "이름이 없는 세이브포인트의 이름을 검색할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgArray.java:153
+#: src/main/java/org/postgresql/jdbc/PgArray.java:369
+#, java-format
+msgid "The array index is out of range: {0}"
+msgstr "배열 인덱스가 범위를 벗어났습니다: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgArray.java:174
+#: src/main/java/org/postgresql/jdbc/PgArray.java:386
+#, java-format
+msgid "The array index is out of range: {0}, number of elements: {1}."
+msgstr "배열 인덱스가 범위를 벗어났습니다: {0}, 요소 수: {1}."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:99
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:105
+msgid "A CallableStatement was executed with nothing returned."
+msgstr "CallableStatement가 실행되었으나 반환된 값이 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:116
+msgid "A CallableStatement was executed with an invalid number of parameters"
+msgstr "CallableStatement가 잘못된 수의 매개 변수로 실행되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:154
+#, java-format
+msgid ""
+"A CallableStatement function was executed and the out parameter {0} was of "
+"type {1} however type {2} was registered."
+msgstr ""
+"CallableStatement 함수가 실행되었고 out 매개 변수 {0} 의 유형이 {1} 이었지만 "
+"유형 {2} 가 등록되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:217
+msgid ""
+"This statement does not declare an OUT parameter. Use '{' ?= call ... '}' "
+"to declare one."
+msgstr ""
+"이 문은 OUT 매개 변수를 선언하지 않습니다. OUT 매개 변수를 선언하려면 '{' ?= "
+"call ... '}'을 사용하십시오."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:240
+msgid "wasNull cannot be call before fetching a result."
+msgstr "wasNull은 결과를 가져오기 전에 호출할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:377
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:399
+#, java-format
+msgid ""
+"Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was "
+"made."
+msgstr ""
+"유형 {0} 의 매개 변수가 등록되었지만 get{1} (sqltype={2}) 호출이 이루어졌습니"
+"다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:413
+msgid ""
+"A CallableStatement was declared, but no call to registerOutParameter(1, "
+") was made."
+msgstr ""
+"CallableStatement가 선언되었지만 registerOutParameter(1, ) 호출이 "
+"이루어지지 않았습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:418
+msgid "No function outputs were registered."
+msgstr "등록된 함수 출력이 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:425
+msgid ""
+"Results cannot be retrieved from a CallableStatement before it is executed."
+msgstr "CallableStatement가 실행되기 전에 결과를 검색할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:735
+#, java-format
+msgid "Unsupported type conversion to {1}."
+msgstr "{1} 으로의 지원되지 않는 유형 변환."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:331
+#, java-format
+msgid "Unsupported value for stringtype parameter: {0}"
+msgstr "stringtype 매개 변수에 대한 지원되지 않는 값: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:589
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:138
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:267
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:303
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:387
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:529
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:566
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:653
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:658
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:703
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:708
+msgid "No results were returned by the query."
+msgstr "쿼리에서 반환된 결과가 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:607
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:624
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:302
+msgid "A result was returned when none was expected."
+msgstr "예상치 못한 결과가 반환되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:733
+msgid "Custom type maps are not supported."
+msgstr "사용자 정의 유형 맵은 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:775
+#, java-format
+msgid "Failed to create object for: {0}."
+msgstr "{0} 에 대한 개체 생성 실패."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:842
+#, java-format
+msgid "Unable to load the class {0} responsible for the datatype {1}"
+msgstr "데이터 유형 {1} 을 담당하는 클래스 {0} 을(를) 로드할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:870
+msgid "Unable to close connection properly"
+msgstr "연결을 올바르게 닫을 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:913
+msgid ""
+"Cannot change transaction read-only property in the middle of a transaction."
+msgstr "트랜잭션 중간에 트랜잭션 읽기 전용 속성을 변경할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:998
+msgid "Cannot commit when autoCommit is enabled."
+msgstr "autoCommit이 활성화된 상태에서 커밋할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1009
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1568
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1612
+msgid "This connection has been closed."
+msgstr "이 연결은 닫혔습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1019
+msgid "Cannot rollback when autoCommit is enabled."
+msgstr "autoCommit이 활성화된 상태에서 롤백할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1075
+msgid ""
+"Cannot change transaction isolation level in the middle of a transaction."
+msgstr "트랜잭션 중간에 트랜잭션 격리 수준을 변경할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1081
+#, java-format
+msgid "Transaction isolation level {0} not supported."
+msgstr "트랜잭션 격리 수준 {0} 이(가) 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1247
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2242
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:1013
+msgid "Fetch size must be a value greater than or equal to 0."
+msgstr "가져오기 크기는 0보다 크거나 같아야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1493
+#, java-format
+msgid "Unable to find server array type for provided name {0}."
+msgstr "제공된 이름 {0} 에 대한 서버 배열 유형을 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1519
+#, java-format
+msgid "Invalid timeout ({0}<0)."
+msgstr "잘못된 시간 초과 ({0}<0)."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1556
+msgid "Validating connection."
+msgstr "연결을 검증 중입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1589
+#, java-format
+msgid "Failed to set ClientInfo property: {0}"
+msgstr "ClientInfo 속성 설정 실패: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1599
+msgid "ClientInfo property not supported."
+msgstr "ClientInfo 속성이 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1625
+msgid "One or more ClientInfo failed."
+msgstr "하나 이상의 ClientInfo가 실패했습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1727
+msgid "Network timeout must be a value greater than or equal to 0."
+msgstr "네트워크 시간 초과는 0보다 크거나 같아야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1736
+msgid "Unable to set network timeout."
+msgstr "네트워크 시간 초과를 설정할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1761
+msgid "Unable to get network timeout."
+msgstr "네트워크 시간 초과를 가져올 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1776
+#, java-format
+msgid "Unknown ResultSet holdability setting: {0}."
+msgstr "알 수 없는 ResultSet 유지 가능성 설정: {0}."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1794
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1815
+msgid "Cannot establish a savepoint in auto-commit mode."
+msgstr "자동 커밋 모드에서는 세이브포인트를 설정할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1884
+msgid "Returning autogenerated keys is not supported."
+msgstr "자동 생성된 키 반환이 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1946
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1960
+#, java-format
+msgid "Could not instantiate xmlFactoryFactory: {0}"
+msgstr "xmlFactoryFactory를 인스턴스화할 수 없습니다: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgConnection.java:1951
+#, java-format
+msgid ""
+"Connection property xmlFactoryFactory must implement PGXmlFactoryFactory: {0}"
+msgstr ""
+"연결 속성 xmlFactoryFactory는 PGXmlFactoryFactory를 구현해야 합니다: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgConnectionCleaningAction.java:85
+msgid "Leak detected: Connection.close() was not called"
+msgstr "누출 감지됨: Connection.close()가 호출되지 않았습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:73
+msgid ""
+"Unable to determine a value for MaxIndexKeys due to missing system catalog "
+"data."
+msgstr ""
+"시스템 카탈로그 데이터가 누락되어 MaxIndexKeys 값을 결정할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:96
+msgid "Unable to find name datatype in the system catalogs."
+msgstr "시스템 카탈로그에서 이름 데이터 유형을 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:356
+msgid "Unable to find keywords in the system catalogs."
+msgstr "시스템 카탈로그에서 키워드를 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1080
+msgid ""
+"Unable to determine a value for DefaultTransactionIsolation due to missing "
+"entry in pg_catalog.pg_settings WHERE name='default_transaction_isolation'."
+msgstr ""
+"pg_catalog.pg_settings에 name='default_transaction_isolation' 항목이 누락되"
+"어 DefaultTransactionIsolation 값을 결정할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1265
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:3092
+msgid "oid"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1265
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:3092
+msgid "proname"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1267
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1737
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:3094
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:272
+msgid "typtype"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1270
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:3097
+msgid "proargtypes"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1754
+msgid "adsrc"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1765
+msgid "attidentity"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1767
+msgid "attgenerated"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1900
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1977
+msgid "rolname"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1901
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1978
+msgid "relacl"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1907
+msgid "attacl"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/PgParameterMetaData.java:96
+#, java-format
+msgid "The parameter index is out of range: {0}, number of parameters: {1}."
+msgstr "매개 변수 인덱스가 범위를 벗어났습니다: {0}, 매개 변수 수: {1}."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:105
+#, java-format
+msgid ""
+"PreparedStatement can have at most {0} parameters. Please consider using "
+"arrays, or splitting the query in several ones, or using COPY. Given query "
+"has {1} parameters"
+msgstr ""
+"PreparedStatement는 최대 {0} 개의 매개 변수를 가질 수 있습니다. 배열을 사용하"
+"거나 쿼리를 여러 개로 나누거나 COPY를 사용하는 것을 고려하십시오. 주어진 쿼리"
+"는 {1} 개의 매개 변수를 가집니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:123
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:148
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:173
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1128
+msgid ""
+"Can''t use query methods that take a query string on a PreparedStatement."
+msgstr ""
+"PreparedStatement에서 쿼리 문자열을 사용하는 쿼리 메서드를 사용할 수 없습니"
+"다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:297
+msgid "Unknown Types value."
+msgstr "알 수 없는 유형 값입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:451
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1295
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1603
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1664
+#, java-format
+msgid "Invalid stream length {0}."
+msgstr "잘못된 스트림 길이 {0}."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:480
+#, java-format
+msgid "The JVM claims not to support the {0} encoding."
+msgstr "JVM이 {0} 인코딩을 지원하지 않는다고 주장합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:483
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1323
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1362
+msgid "Provided InputStream failed."
+msgstr "제공된 InputStream이 실패했습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:525
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1196
+#, java-format
+msgid "Unknown type {0}."
+msgstr "알 수 없는 유형 {0}."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:547
+msgid "No hstore extension installed."
+msgstr "설치된 hstore 확장이 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:686
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:708
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:718
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:731
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1059
+#, java-format
+msgid "Cannot cast an instance of {0} to type {1}"
+msgstr "{0} 의 인스턴스를 유형 {1}(으)로 캐스팅할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:749
+#, java-format
+msgid "Unsupported Types value: {0}"
+msgstr "지원되지 않는 유형 값: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:980
+#, java-format
+msgid "Cannot convert an instance of {0} to type {1}"
+msgstr "{0} 의 인스턴스를 유형 {1} (으)로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1065
+#, java-format
+msgid ""
+"Can''t infer the SQL type to use for an instance of {0}. Use setObject() "
+"with an explicit Types value to specify the type to use."
+msgstr ""
+"{0} 의 인스턴스에 사용할 SQL 유형을 추론할 수 없습니다. 명시적 유형 값을 사용"
+"하여 setObject()를 사용하여 사용할 유형을 지정하십시오."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1232
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1342
+msgid "Unexpected error writing large object to database."
+msgstr "데이터베이스에 대형 객체를 쓰는 동안 예상치 못한 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1280
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1425
+msgid "Provided Reader failed."
+msgstr "제공된 Reader가 실패했습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1600
+#: src/main/java/org/postgresql/util/StreamWrapper.java:60
+msgid "Object is too large to send over the protocol."
+msgstr "객체가 프로토콜을 통해 보내기에는 너무 큽니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:324
+msgid ""
+"Operation requires a scrollable ResultSet, but this ResultSet is "
+"FORWARD_ONLY."
+msgstr ""
+"작업에는 스크롤 가능한 ResultSet이 필요하지만 이 ResultSet은 FORWARD_ONLY입니"
+"다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:543
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:586
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:636
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:696
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:719
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:742
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:773
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:796
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3522
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3600
+#, java-format
+msgid "Cannot convert the column of type {0} to requested type {1}."
+msgstr "유형 {0} 의 열을 요청된 유형 {1} (으)로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1022
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1043
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2276
+msgid "Can''t use relative move methods while on the insert row."
+msgstr "삽입 행에 있는 동안 상대적 이동 메서드를 사용할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1067
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:1004
+#, java-format
+msgid "Invalid fetch direction constant: {0}."
+msgstr "잘못된 가져오기 방향 상수: {0}."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1079
+msgid "Cannot call cancelRowUpdates() when on the insert row."
+msgstr "삽입 행에 있는 동안 cancelRowUpdates()를 호출할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1097
+msgid "Cannot call deleteRow() when on the insert row."
+msgstr "삽입 행에 있는 동안 deleteRow()를 호출할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1104
+msgid ""
+"Currently positioned before the start of the ResultSet. You cannot call "
+"deleteRow() here."
+msgstr ""
+"현재 ResultSet의 시작 이전에 위치해 있습니다. 여기에서 deleteRow()를 호출할 "
+"수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1110
+msgid ""
+"Currently positioned after the end of the ResultSet. You cannot call "
+"deleteRow() here."
+msgstr ""
+"현재 ResultSet의 끝 이후에 위치해 있습니다. 여기에서 deleteRow()를 호출할 수 "
+"없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1115
+msgid "There are no rows in this ResultSet."
+msgstr "이 ResultSet에는 행이 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1156
+msgid "Not on the insert row."
+msgstr "삽입 행에 있지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1160
+msgid "You must specify at least one column value to insert a row."
+msgstr "행을 삽입하려면 적어도 하나의 열 값을 지정해야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1495
+msgid "Can''t refresh the insert row."
+msgstr "삽입 행을 새로 고칠 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1571
+msgid "Cannot call updateRow() when on the insert row."
+msgstr "삽입 행에 있는 동안 updateRow()를 호출할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1579
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3617
+msgid ""
+"Cannot update the ResultSet because it is either before the start or after "
+"the end of the results."
+msgstr ""
+"결과의 시작 전이나 끝 후에 있기 때문에 ResultSet을 업데이트할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1842
+msgid "ResultSets with concurrency CONCUR_READ_ONLY cannot be updated."
+msgstr "동시성 CONCUR_READ_ONLY를 가진 ResultSet은 업데이트할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1946
+#, java-format
+msgid "No eligible primary or unique key found for table {0}."
+msgstr "테이블 {0} 에 대해 적합한 기본 키 또는 고유 키가 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2146
+#, java-format
+msgid "The JVM claims not to support the encoding: {0}"
+msgstr "JVM이 인코딩을 지원하지 않는다고 주장합니다: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2537
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:2542
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3315
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3321
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3346
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3352
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3376
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3381
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3397
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3418
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3429
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3442
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3571
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3581
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3592
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3605
+#, java-format
+msgid "Bad value for type {0} : {1}"
+msgstr "유형 {0} 에 대한 잘못된 값: {1}"
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3086
+#, java-format
+msgid "The column name {0} was not found in this ResultSet."
+msgstr "이 ResultSet에서 열 이름 {0} 을(를) 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3227
+msgid ""
+"ResultSet is not updateable. The query that generated this result set must "
+"select only one table, and must select all primary keys from that table. See "
+"the JDBC 2.1 API Specification, section 5.6 for more details."
+msgstr ""
+"ResultSet은 업데이트할 수 없습니다. 이 결과 집합을 생성한 쿼리는 하나의 테이"
+"블만 선택해야 하며 해당 테이블의 모든 기본 키를 선택해야 합니다. 자세한 내용"
+"은 JDBC 2.1 API 명세서 섹션 5.6을 참조하십시오."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3243
+msgid "This ResultSet is closed."
+msgstr "이 ResultSet은 닫혔습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3277
+msgid "ResultSet not positioned properly, perhaps you need to call next."
+msgstr ""
+"ResultSet이 올바르게 위치하지 않았습니다. 아마도 next를 호출해야 할 것입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3639
+msgid "Invalid UUID data."
+msgstr "잘못된 UUID 데이터입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3739
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3746
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3757
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3768
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3779
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3790
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3801
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3812
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3823
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3830
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3837
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3846
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3861
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3868
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3875
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3886
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3893
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3900
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3937
+#, java-format
+msgid "conversion to {0} from {1} not supported"
+msgstr "{1} 에서 {0} (으)로의 변환이 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgResultSet.java:3914
+msgid "Invalid Inet data."
+msgstr "잘못된 Inet 데이터입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:170
+msgid "Unable to decode xml data."
+msgstr "xml 데이터를 디코딩할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:173
+#, java-format
+msgid "Unknown XML Source class: {0}"
+msgstr "알 수 없는 XML 소스 클래스: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:229
+msgid "Unable to create SAXResult for SQLXML."
+msgstr "SQLXML에 대한 SAXResult를 생성할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:245
+msgid "Unable to create StAXResult for SQLXML"
+msgstr "SQLXML에 대한 StAXResult를 생성할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:250
+#, java-format
+msgid "Unknown XML Result class: {0}"
+msgstr "알 수 없는 XML 결과 클래스: {0}"
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:266
+msgid "This SQLXML object has already been freed."
+msgstr "이 SQLXML 객체는 이미 해제되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:275
+msgid ""
+"This SQLXML object has not been initialized, so you cannot retrieve data "
+"from it."
+msgstr "이 SQLXML 객체가 초기화되지 않았으므로 데이터를 검색할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:288
+#, java-format
+msgid "Failed to convert binary xml data to encoding: {0}."
+msgstr "이진 xml 데이터를 인코딩 {0} (으)로 변환하지 못했습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:315
+msgid "Unable to convert DOMResult SQLXML data to a string."
+msgstr "DOMResult SQLXML 데이터를 문자열로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgSQLXML.java:328
+msgid ""
+"This SQLXML object has already been initialized, so you cannot manipulate it "
+"further."
+msgstr "이 SQLXML 객체는 이미 초기화되었으므로 더 이상 조작할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:166
+msgid "Unknown value for ResultSet type"
+msgstr "ResultSet 유형에 대한 알 수 없는 값입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:172
+msgid "Unknown value for ResultSet concurrency"
+msgstr "ResultSet 동시성에 대한 알 수 없는 값입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:181
+msgid "Unknown value for ResultSet holdability"
+msgstr "ResultSet 유지 가능성에 대한 알 수 없는 값입니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:279
+msgid "Multiple ResultSets were returned by the query."
+msgstr "쿼리에서 여러 ResultSet이 반환되었습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:366
+msgid "Can''t use executeWithFlags(int) on a Statement."
+msgstr "문에서 executeWithFlags(int)를 사용할 수 없습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:579
+msgid "Maximum number of rows must be a value greater than or equal to 0."
+msgstr "최대 행 수는 0보다 크거나 같아야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:628
+msgid "Query timeout must be a value greater than or equals to 0."
+msgstr "쿼리 시간 초과는 0보다 크거나 같아야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:671
+msgid "The maximum field size must be a value greater than or equal to 0."
+msgstr "최대 필드 크기는 0보다 크거나 같아야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:776
+msgid "This statement has been closed."
+msgstr "이 문이 닫혔습니다."
+
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:1165
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:1303
+#: src/main/java/org/postgresql/jdbc/PgStatement.java:1336
+msgid "Returning autogenerated keys by column index is not supported."
+msgstr "열 인덱스별 자동 생성 키 반환이 지원되지 않습니다."
+
+#: src/main/java/org/postgresql/jdbc/QueryExecutorTimeZoneProvider.java:32
+msgid ""
+"Backend timezone is not known. Backend should have returned TimeZone when "
+"establishing a connection"
+msgstr ""
+"백엔드 표준 시간대를 알 수 없습니다. 연결을 설정할 때 백엔드에서 TimeZone을 "
+"반환해야 합니다."
+
+#: src/main/java/org/postgresql/jdbc/SslMode.java:78
+#, java-format
+msgid "Invalid sslmode value: {0}"
+msgstr "잘못된 sslmode 값: {0}"
+
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:379
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:496
+#, java-format
+msgid "Bad value for type timestamp/date/time: {0}"
+msgstr "유형 타임스탬프/날짜/시간에 대한 잘못된 값: {0}"
+
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:511
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1326
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1383
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1427
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1480
+#: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1607
+#, java-format
+msgid "Unsupported binary encoding of {0}."
+msgstr "{0} 의 지원되지 않는 이진 인코딩."
+
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:307
+msgid "typname"
+msgstr ""
+
+#: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:1087
+#, java-format
+msgid "Value is not an OID: {0}"
+msgstr "값이 OID가 아닙니다: {0}"
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:68
+msgid "No SCRAM mechanism(s) advertised by the server"
+msgstr "서버에서 광고한 SCRAM 메커니즘이 없습니다."
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:82
+msgid "Invalid or unsupported by client SCRAM mechanisms"
+msgstr "클라이언트에서 지원되지 않거나 잘못된 SCRAM 메커니즘"
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:125
+msgid "SCRAM session does not exist"
+msgstr "SCRAM 세션이 존재하지 않습니다."
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:135
+#, java-format
+msgid "Invalid server-first-message: {0}"
+msgstr "잘못된 서버 첫 번째 메시지: {0}"
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:171
+msgid "SCRAM client final processor does not exist"
+msgstr "SCRAM 클라이언트 최종 프로세서가 존재하지 않습니다."
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:179
+#, java-format
+msgid "Invalid server-final-message: {0}"
+msgstr "잘못된 서버 최종 메시지: {0}"
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:185
+#, java-format
+msgid "SCRAM authentication failed, server returned error: {0}"
+msgstr "SCRAM 인증 실패, 서버에서 오류를 반환했습니다: {0}"
+
+#: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:192
+msgid "Invalid server SCRAM signature"
+msgstr "잘못된 서버 SCRAM 서명"
+
+#: src/main/java/org/postgresql/largeobject/BlobInputStream.java:138
+#: src/main/java/org/postgresql/largeobject/BlobInputStream.java:231
+#, java-format
+msgid ""
+"Can not read data from large object {0}, position: {1}, buffer size: {2}"
+msgstr ""
+"대형 객체 {0} 에서 데이터를 읽을 수 없습니다. 위치: {1}, 버퍼 크기: {2}"
+
+#: src/main/java/org/postgresql/largeobject/BlobInputStream.java:261
+#: src/main/java/org/postgresql/largeobject/BlobOutputStream.java:236
+#, java-format
+msgid "Can not close large object {0}"
+msgstr "대형 객체 {0} 을(를) 닫을 수 없습니다."
+
+#: src/main/java/org/postgresql/largeobject/BlobInputStream.java:317
+#, java-format
+msgid "Can not reset stream for large object {0} to position {1}"
+msgstr "대형 객체 {0} 에 대한 스트림을 위치 {1} (으)로 재설정할 수 없습니다."
+
+#: src/main/java/org/postgresql/largeobject/BlobOutputStream.java:105
+#: src/main/java/org/postgresql/largeobject/BlobOutputStream.java:190
+#, java-format
+msgid "Can not write data to large object {0}, requested write length: {1}"
+msgstr "대형 객체 {0} 에 데이터를 쓸 수 없습니다. 요청된 쓰기 길이: {1}"
+
+#: src/main/java/org/postgresql/largeobject/BlobOutputStream.java:217
+#, java-format
+msgid "Can not flush large object {0}"
+msgstr "대형 객체 {0} 을(를) 플러시할 수 없습니다."
+
+#: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:244
+#: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:285
+msgid "Large Objects may not be used in auto-commit mode."
+msgstr "대형 객체는 자동 커밋 모드에서 사용할 수 없습니다."
+
+#: src/main/java/org/postgresql/osgi/PGDataSourceFactory.java:89
+#, java-format
+msgid "Unsupported properties: {0}"
+msgstr "지원되지 않는 속성: {0}"
+
+#: src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java:40
+msgid "Server does not support temporary replication slots"
+msgstr "서버가 임시 복제 슬롯을 지원하지 않습니다."
+
+#: src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java:72
+#: src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java:55
+msgid "slot_name"
+msgstr ""
+
+#: src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java:74
+#: src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java:57
+msgid "consistent_point"
+msgstr ""
+
+#: src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java:75
+#: src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java:58
+msgid "snapshot_name"
+msgstr ""
+
+#: src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java:76
+#: src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java:59
+msgid "output_plugin"
+msgstr ""
+
+#: src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java:79
+#: src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java:62
+#, java-format
+msgid "{0} returned no results"
+msgstr "{0} 반환된 결과가 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:152
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:141
+msgid ""
+"Could not find a java cryptographic algorithm: X.509 CertificateFactory not "
+"available."
+msgstr ""
+"Java 암호화 알고리즘을 찾을 수 없습니다: X.509 CertificateFactory를 사용할 "
+"수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:164
+#, java-format
+msgid "Could not open SSL certificate file {0}."
+msgstr "SSL 인증서 파일 {0} 을(를) 열 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:169
+#, java-format
+msgid "Loading the SSL certificate {0} into a KeyManager failed."
+msgstr "SSL 인증서 {0} 을(를) KeyManager로 로드하지 못했습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:179
+#, java-format
+msgid "Could not close SSL certificate file {0}."
+msgstr "SSL 인증서 파일 {0} 을(를) 닫을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:245
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:151
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:174
+msgid "Enter SSL password: "
+msgstr "SSL 비밀번호 입력: "
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:252
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:181
+msgid "Could not read password for SSL key file, console is not available."
+msgstr "SSL 키 파일의 비밀번호를 읽을 수 없습니다. 콘솔을 사용할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:257
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:186
+#, java-format
+msgid "Could not read password for SSL key file by callbackhandler {0}."
+msgstr ""
+"콜백 핸들러 {0} 을(를) 사용하여 SSL 키 파일의 비밀번호를 읽을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:277
+#, java-format
+msgid "Could not decrypt SSL key file {0}."
+msgstr "SSL 키 파일 {0} 을(를) 해독할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:284
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:162
+#, java-format
+msgid "Could not read SSL key file {0}."
+msgstr "SSL 키 파일 {0} 을(를) 읽을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LazyKeyManager.java:287
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:194
+#, java-format
+msgid "Could not find a java cryptographic algorithm: {0}."
+msgstr "Java 암호화 알고리즘을 찾을 수 없습니다: {0}."
+
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:63
+#, java-format
+msgid "The password callback class provided {0} could not be instantiated."
+msgstr "제공된 비밀번호 콜백 클래스 {0} 을(를) 인스턴스화할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:151
+#, java-format
+msgid "Could not open SSL root certificate file {0}."
+msgstr "SSL 루트 인증서 파일 {0} 을(를) 열 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:166
+#, java-format
+msgid "Could not read SSL root certificate file {0}."
+msgstr "SSL 루트 인증서 파일 {0} 을(를) 읽을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:170
+#, java-format
+msgid "Loading the SSL root certificate {0} into a TrustManager failed."
+msgstr "SSL 루트 인증서 {0} 을(를) TrustManager로 로드하지 못했습니다."
+
+#: src/main/java/org/postgresql/ssl/LibPQFactory.java:188
+msgid "Could not initialize SSL context."
+msgstr "SSL 컨텍스트를 초기화할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/MakeSSL.java:45
+#, java-format
+msgid "SSL error: {0}"
+msgstr "SSL 오류: {0}"
+
+#: src/main/java/org/postgresql/ssl/MakeSSL.java:74
+#, java-format
+msgid "The HostnameVerifier class provided {0} could not be instantiated."
+msgstr "제공된 HostnameVerifier 클래스 {0} 을(를) 인스턴스화할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/MakeSSL.java:85
+#, java-format
+msgid "The hostname {0} could not be verified by hostnameverifier {1}."
+msgstr "hostnameverifier {1} 이(가) 호스트 이름 {0}을(를) 확인할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:87
+#, java-format
+msgid "Unable to parse X509Certificate for hostname {0}"
+msgstr "호스트 이름 {0} 에 대한 X509Certificate을(를) 구문 분석할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:92
+#, java-format
+msgid "No certificates found for hostname {0}"
+msgstr "호스트 이름 {0} 에 대한 인증서를 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:111
+#, java-format
+msgid "Hostname {0} is invalid"
+msgstr "호스트 이름 {0} 이(가) 유효하지 않습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:128
+#, java-format
+msgid "Unable to parse certificates for hostname {0}"
+msgstr "호스트 이름 {0} 에 대한 인증서를 구문 분석할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:159
+#, java-format
+msgid "Server name validation pass for {0}, subjectAltName {1}"
+msgstr "서버 이름 유효성 검사가 {0}, subjectAltName {1} 에 대해 통과했습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:175
+#, java-format
+msgid ""
+"Server name validation failed: certificate for host {0} dNSName entries "
+"subjectAltName, but none of them match. Assuming server name validation "
+"failed"
+msgstr ""
+"서버 이름 유효성 검사 실패: 호스트 {0} dNSName 항목 subjectAltName에 대한 인"
+"증서가 있지만 일치하지 않습니다. 서버 이름 유효성 검사 실패로 가정"
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:186
+#, java-format
+msgid ""
+"Server name validation failed: unable to extract common name from "
+"X509Certificate for hostname {0}"
+msgstr ""
+"서버 이름 유효성 검사 실패: 호스트 이름 {0} 에 대한 X509Certificate에서 공통 "
+"이름을 추출할 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:199
+#, java-format
+msgid ""
+"Server name validation failed: certificate for hostname {0} has no DNS "
+"subjectAltNames, and it CommonName is missing as well"
+msgstr ""
+"서버 이름 유효성 검사 실패: 호스트 이름 {0} 에 대한 인증서에 DNS "
+"subjectAltNames가 없으며 CommonName도 없습니다."
+
+#: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:219
+#, java-format
+msgid ""
+"Server name validation failed: hostname {0} does not match common name {1}"
+msgstr ""
+"서버 이름 유효성 검사 실패: 호스트 이름 {0} 이(가) 공통 이름 {1} 과(와) 일치"
+"하지 않습니다."
+
+#: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:47
+msgid "Unable to find pkcs12 keystore."
+msgstr "pkcs12 키 저장소를 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:92
+msgid "The sslfactoryarg property may not be empty."
+msgstr "sslfactoryarg 속성은 비어 있을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:107
+#, java-format
+msgid "Unable to find resource {0} via Thread contextClassLoader {1}"
+msgstr ""
+"스레드 contextClassLoader {1} 을(를) 통해 리소스 {0} 을(를) 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:115
+#, java-format
+msgid "Unable to find resource {0} via class {1} ClassLoader {2}"
+msgstr ""
+"클래스 {1} ClassLoader {2} 을(를) 통해 리소스 {0} 을(를) 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:126
+msgid ""
+"The environment variable containing the server's SSL certificate must not be "
+"empty."
+msgstr "서버의 SSL 인증서를 포함하는 환경 변수는 비어 있을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:134
+msgid ""
+"The system property containing the server's SSL certificate must not be "
+"empty."
+msgstr "서버의 SSL 인증서를 포함하는 시스템 속성은 비어 있을 수 없습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:141
+msgid ""
+"The sslfactoryarg property must start with the prefix file:, classpath:, "
+"env:, sys:, or -----BEGIN CERTIFICATE-----."
+msgstr ""
+"sslfactoryarg 속성은 file:, classpath:, env:, sys: 또는 -----BEGIN "
+"CERTIFICATE----- 접두사로 시작해야 합니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:153
+msgid "An error occurred reading the certificate"
+msgstr "인증서를 읽는 동안 오류가 발생했습니다."
+
+#: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:186
+msgid "No X509TrustManager found"
+msgstr "X509TrustManager를 찾을 수 없습니다."
+
+#: src/main/java/org/postgresql/sspi/SSPIClient.java:67
+msgid "Unable to instantiate SecBufferDesc, so SSPI is unavailable"
+msgstr "SecBufferDesc을 인스턴스화할 수 없어 SSPI를 사용할 수 없습니다."
+
+#: src/main/java/org/postgresql/util/HStoreConverter.java:61
+msgid "hstore key must not be null"
+msgstr "hstore 키는 null일 수 없습니다."
+
+#: src/main/java/org/postgresql/util/PGInterval.java:221
+msgid "Conversion of interval failed"
+msgstr "간격 변환 실패"
+
+#: src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java:201
+#, java-format
+msgid ""
+"WARNING! Required to allocate {0} bytes, which exceeded possible heap memory "
+"size. Assigned {1} bytes as limit."
+msgstr ""
+"경고! {0} 바이트를 할당해야 하는데 이는 가능한 힙 메모리 크기를 초과했습니"
+"다. {1} 바이트를 한도로 할당했습니다."
+
+#: src/main/java/org/postgresql/util/PGbytea.java:233
+msgid "Can't convert {0} to {1} literal"
+msgstr "{0} 을(를) {1} 리터럴로 변환할 수 없습니다."
+
+#: src/main/java/org/postgresql/util/PGmoney.java:74
+msgid "Conversion of money failed."
+msgstr "금액 변환 실패."
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:45
+#, java-format
+msgid ""
+" (pgjdbc: autodetected server-encoding to be {0}, if the message is not "
+"readable, please check database logs and/or host, port, dbname, user, "
+"password, pg_hba.conf)"
+msgstr ""
+"(pgjdbc: 서버 인코딩이 {0} (으)로 자동 감지되었습니다. 메시지를 읽을 수 없는 "
+"경우 데이터베이스 로그 및/또는 호스트, 포트, dbname, 사용자, 비밀번호, "
+"pg_hba.conf를 확인하십시오.)"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:190
+#, java-format
+msgid "Detail: {0}"
+msgstr "세부 정보: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:195
+#, java-format
+msgid "Hint: {0}"
+msgstr "힌트: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:199
+#, java-format
+msgid "Position: {0}"
+msgstr "위치: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:203
+#, java-format
+msgid "Where: {0}"
+msgstr "위치: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:209
+#, java-format
+msgid "Internal Query: {0}"
+msgstr "내부 쿼리: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:213
+#, java-format
+msgid "Internal Position: {0}"
+msgstr "내부 위치: {0}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:220
+#, java-format
+msgid "Location: File: {0}, Routine: {1}, Line: {2}"
+msgstr "위치: 파일: {0}, 루틴: {1}, 줄: {2}"
+
+#: src/main/java/org/postgresql/util/ServerErrorMessage.java:225
+#, java-format
+msgid "Server SQLState: {0}"
+msgstr "서버 SQL 상태: {0}"
+
+#: src/main/java/org/postgresql/util/TempFileHolder.java:45
+msgid "StreamWrapper leak detected StreamWrapper.close() was not called. "
+msgstr ""
+"StreamWrapper 누수가 감지되었습니다. StreamWrapper.close()가 호출되지 않았습"
+"니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:133
+msgid ""
+"Transaction control methods setAutoCommit(true), commit, rollback and "
+"setSavePoint not allowed while an XA transaction is active."
+msgstr ""
+"XA 트랜잭션이 활성 상태인 동안 트랜잭션 제어 메서드 setAutoCommit(true), "
+"commit, rollback 및 setSavePoint가 허용되지 않습니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:191
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:277
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:386
+#, java-format
+msgid "Invalid flags {0}"
+msgstr "잘못된 플래그 {0}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:195
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:281
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:496
+msgid "xid must not be null"
+msgstr "xid는 null일 수 없습니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:199
+msgid "Connection is busy with another transaction"
+msgstr "연결이 다른 트랜잭션으로 바쁩니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:208
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:291
+msgid "suspend/resume not implemented"
+msgstr "일시 중지/다시 시작이 구현되지 않았습니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:216
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:223
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:227
+#, java-format
+msgid ""
+"Invalid protocol state requested. Attempted transaction interleaving is not "
+"supported. xid={0}, currentXid={1}, state={2}, flags={3}"
+msgstr ""
+"잘못된 프로토콜 상태 요청됨. 트랜잭션 교차 시도는 지원되지 않습니다. "
+"xid={0}, currentXid={1}, state={2}, flags={3}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:238
+msgid "Error disabling autocommit"
+msgstr "자동 커밋 비활성화 오류"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:285
+#, java-format
+msgid ""
+"tried to call end without corresponding start call. state={0}, start "
+"xid={1}, currentXid={2}, preparedXid={3}"
+msgstr ""
+"해당 시작 호출 없이 end를 호출하려고 했습니다. state={0}, start xid={1}, "
+"currentXid={2}, preparedXid={3}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:331
+#, java-format
+msgid ""
+"Preparing already prepared transaction, the prepared xid {0}, prepare xid={1}"
+msgstr "이미 준비된 트랜잭션 준비, 준비된 xid {0}, prepare xid={1}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:334
+#, java-format
+msgid "Current connection does not have an associated xid. prepare xid={0}"
+msgstr "현재 연결에는 연결된 xid가 없습니다. prepare xid={0}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:341
+#, java-format
+msgid ""
+"Not implemented: Prepare must be issued using the same connection that "
+"started the transaction. currentXid={0}, prepare xid={1}"
+msgstr ""
+"구현되지 않음: Prepare는 트랜잭션을 시작한 동일한 연결을 사용하여 발행해야 합"
+"니다. currentXid={0}, prepare xid={1}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:345
+#, java-format
+msgid "Prepare called before end. prepare xid={0}, state={1}"
+msgstr "end 이전에 호출 준비. prepare xid={0}, state={1}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:365
+#, java-format
+msgid "Error preparing transaction. prepare xid={0}"
+msgstr "트랜잭션 준비 오류. prepare xid={0}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:421
+msgid "Error during recover"
+msgstr "복구 중 오류"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:485
+#, java-format
+msgid ""
+"Error rolling back prepared transaction. rollback xid={0}, preparedXid={1}, "
+"currentXid={2}"
+msgstr ""
+"준비된 트랜잭션을 롤백하는 중 오류가 발생했습니다. 롤백 xid={0}, "
+"preparedXid={1}, currentXid={2}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:526
+#, java-format
+msgid ""
+"One-phase commit called for xid {0} but connection was prepared with xid {1}"
+msgstr ""
+"1단계 커밋이 xid {0} 에 대해 호출되었으나 연결이 xid {1}(으)로 준비되었습니"
+"다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:534
+msgid ""
+"Not implemented: one-phase commit must be issued using the same connection "
+"that was used to start it"
+msgstr ""
+"구현되지 않음: 1단계 커밋은 시작하는 데 사용된 동일한 연결을 사용하여 발행해"
+"야 합니다."
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:538
+#, java-format
+msgid "One-phase commit with unknown xid. commit xid={0}, currentXid={1}"
+msgstr "알 수 없는 xid와 1단계 커밋. commit xid={0}, currentXid={1}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:542
+#, java-format
+msgid "commit called before end. commit xid={0}, state={1}"
+msgstr "end 이전에 호출 커밋. commit xid={0}, state={1}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:553
+#, java-format
+msgid "Error during one-phase commit. commit xid={0}"
+msgstr "1단계 커밋 중 오류 발생. commit xid={0}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:581
+#, java-format
+msgid ""
+"Not implemented: 2nd phase commit must be issued using an idle connection. "
+"commit xid={0}, currentXid={1}, state={2}, transactionState={3}"
+msgstr ""
+"구현되지 않음: 2단계 커밋은 유휴 연결을 사용하여 발행해야 합니다. commit "
+"xid={0}, currentXid={1}, state={2}, transactionState={3}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:614
+#, java-format
+msgid ""
+"Error committing prepared transaction. commit xid={0}, preparedXid={1}, "
+"currentXid={2}"
+msgstr ""
+"준비된 트랜잭션을 커밋하는 중 오류가 발생했습니다. commit xid={0}, "
+"preparedXid={1}, currentXid={2}"
+
+#: src/main/java/org/postgresql/xa/PGXAConnection.java:631
+#, java-format
+msgid "Heuristic commit/rollback not supported. forget xid={0}"
+msgstr "휴리스틱 커밋/롤백이 지원되지 않습니다. forget xid={0}"
diff --git a/src/main/java/org/postgresql/translation/messages.pot b/src/main/java/org/postgresql/translation/messages.pot
index a879b09..52d3822 100644
--- a/src/main/java/org/postgresql/translation/messages.pot
+++ b/src/main/java/org/postgresql/translation/messages.pot
@@ -827,7 +827,7 @@ msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/messages_bg.java b/src/main/java/org/postgresql/translation/messages_bg.java
index 21fce2f..dfddefb 100644
--- a/src/main/java/org/postgresql/translation/messages_bg.java
+++ b/src/main/java/org/postgresql/translation/messages_bg.java
@@ -300,7 +300,7 @@ public class messages_bg extends java.util.ResourceBundle {
t[657] = "Отчетен параметър от тип {0}, но обработено като get{1} (sqltype={2}). ";
t[662] = "Unsupported value for stringtype parameter: {0}";
t[663] = "Непозволена стойност за StringType параметър: {0}";
- t[664] = "Fetch size must be a value greater to or equal to 0.";
+ t[664] = "Fetch size must be a value greater than or equal to 0.";
t[665] = "Размера за fetch size трябва да бъде по-голям или равен на 0.";
t[670] = "Cannot tell if path is open or closed: {0}.";
t[671] = "Не може да определи дали адреса е отворен или затворен: {0}.";
diff --git a/src/main/java/org/postgresql/translation/messages_cs.java b/src/main/java/org/postgresql/translation/messages_cs.java
index e3c63a8..8e5d342 100644
--- a/src/main/java/org/postgresql/translation/messages_cs.java
+++ b/src/main/java/org/postgresql/translation/messages_cs.java
@@ -118,7 +118,7 @@ public class messages_cs extends java.util.ResourceBundle {
t[187] = "Ovladač nyní nepodporuje příkaz COPY.";
t[190] = "Invalid character data was found. This is most likely caused by stored data containing characters that are invalid for the character set the database was created in. The most common example of this is storing 8bit data in a SQL_ASCII database.";
t[191] = "Nalezena vada ve znakových datech. Toto může být způsobeno uloženými daty obsahujícími znaky, které jsou závadné pro znakovou sadu nastavenou při zakládání databáze. Nejznámejší příklad je ukládání 8bitových dat vSQL_ASCII databázi.";
- t[196] = "Fetch size must be a value greater to or equal to 0.";
+ t[196] = "Fetch size must be a value greater than or equal to 0.";
t[197] = "Nabraná velikost musí být nezáporná.";
t[204] = "Unsupported Types value: {0}";
t[205] = "Nepodporovaná hodnota typu: {0}";
diff --git a/src/main/java/org/postgresql/translation/messages_de.java b/src/main/java/org/postgresql/translation/messages_de.java
index dcac938..8e045a0 100644
--- a/src/main/java/org/postgresql/translation/messages_de.java
+++ b/src/main/java/org/postgresql/translation/messages_de.java
@@ -118,7 +118,7 @@ public class messages_de extends java.util.ResourceBundle {
t[299] = "Diese PooledConnection ist bereits geschlossen worden.";
t[302] = "ClientInfo property not supported.";
t[303] = "Die ClientInfo-Eigenschaft ist nicht unterstützt.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "Die Fetch-Größe muss ein Wert größer oder gleich Null sein.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "Es konnte keine Verbindung unter Verwendung des Protokolls {0} hergestellt werden.";
diff --git a/src/main/java/org/postgresql/translation/messages_fr.java b/src/main/java/org/postgresql/translation/messages_fr.java
index 71230fa..8a2426b 100644
--- a/src/main/java/org/postgresql/translation/messages_fr.java
+++ b/src/main/java/org/postgresql/translation/messages_fr.java
@@ -116,7 +116,7 @@ public class messages_fr extends java.util.ResourceBundle {
t[297] = "Le troncage des large objects n''est implémenté que dans les serveurs 8.3 et supérieurs.";
t[298] = "This PooledConnection has already been closed.";
t[299] = "Cette PooledConnection a déjà été fermée.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "Fetch size doit être une valeur supérieur ou égal à 0.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "Aucune connexion n''a pu être établie en utilisant le protocole demandé {0}. ";
diff --git a/src/main/java/org/postgresql/translation/messages_it.java b/src/main/java/org/postgresql/translation/messages_it.java
index 9f412b5..2bf0cb3 100644
--- a/src/main/java/org/postgresql/translation/messages_it.java
+++ b/src/main/java/org/postgresql/translation/messages_it.java
@@ -108,7 +108,7 @@ public class messages_it extends java.util.ResourceBundle {
t[281] = "Il server ha richiesto l''autenticazione con password, ma tale password non è stata fornita.";
t[298] = "This PooledConnection has already been closed.";
t[299] = "Questo «PooledConnection» è stato chiuso.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "La dimensione dell''area di «fetch» deve essere maggiore o eguale a 0.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "Non è stato possibile attivare la connessione utilizzando il protocollo richiesto {0}.";
diff --git a/src/main/java/org/postgresql/translation/messages_ja.java b/src/main/java/org/postgresql/translation/messages_ja.java
index d1ffd50..93d32e9 100644
--- a/src/main/java/org/postgresql/translation/messages_ja.java
+++ b/src/main/java/org/postgresql/translation/messages_ja.java
@@ -96,7 +96,7 @@ public class messages_ja extends java.util.ResourceBundle {
t[211] = "サーバはパスワード・ベースの認証を要求しましたが、パスワードが渡されませんでした。";
t[214] = "Interrupted while attempting to connect.";
t[215] = "接続試行中に割り込みがありました。";
- t[216] = "Fetch size must be a value greater to or equal to 0.";
+ t[216] = "Fetch size must be a value greater than or equal to 0.";
t[217] = "フェッチサイズは、0または、より大きな値でなくてはなりません。";
t[228] = "Added parameters index out of range: {0}, number of columns: {1}.";
t[229] = "パラメータ・インデックスは範囲外です: {0} , カラム数: {1}";
diff --git a/src/main/java/org/postgresql/translation/messages_pl.java b/src/main/java/org/postgresql/translation/messages_pl.java
index c9705f5..3bcccb6 100644
--- a/src/main/java/org/postgresql/translation/messages_pl.java
+++ b/src/main/java/org/postgresql/translation/messages_pl.java
@@ -106,7 +106,7 @@ public class messages_pl extends java.util.ResourceBundle {
t[255] = "Aktualna pozycja przed początkiem ResultSet. Nie można wywołać deleteRow().";
t[258] = "Failed to create object for: {0}.";
t[259] = "Nie powiodło się utworzenie obiektu dla: {0}.";
- t[262] = "Fetch size must be a value greater to or equal to 0.";
+ t[262] = "Fetch size must be a value greater than or equal to 0.";
t[263] = "Rozmiar pobierania musi być wartością dodatnią lub 0.";
t[270] = "No results were returned by the query.";
t[271] = "Zapytanie nie zwróciło żadnych wyników.";
diff --git a/src/main/java/org/postgresql/translation/messages_pt_BR.java b/src/main/java/org/postgresql/translation/messages_pt_BR.java
index a38e9b1..44548bc 100644
--- a/src/main/java/org/postgresql/translation/messages_pt_BR.java
+++ b/src/main/java/org/postgresql/translation/messages_pt_BR.java
@@ -134,7 +134,7 @@ public class messages_pt_BR extends java.util.ResourceBundle {
t[299] = "Este PooledConnection já foi fechado.";
t[302] = "ClientInfo property not supported.";
t[303] = "propriedade ClientInfo não é suportada.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "Tamanho da busca deve ser um valor maior ou igual a 0.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "A conexão não pode ser feita usando protocolo informado {0}.";
diff --git a/src/main/java/org/postgresql/translation/messages_ru.java b/src/main/java/org/postgresql/translation/messages_ru.java
index 3a83278..0785b56 100644
--- a/src/main/java/org/postgresql/translation/messages_ru.java
+++ b/src/main/java/org/postgresql/translation/messages_ru.java
@@ -53,7 +53,7 @@ public class messages_ru extends java.util.ResourceBundle {
t[104] = "Connection to {0} refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.";
t[105] = "Подсоединение по адресу {0} отклонено. Проверьте что хост и порт указаны правильно и что postmaster принимает TCP/IP-подсоединения.";
t[108] = "This statement has been closed.";
- t[109] = "Этот Sstatement был закрыт.";
+ t[109] = "Этот statement был закрыт.";
t[110] = "Error committing prepared transaction. commit xid={0}, preparedXid={1}, currentXid={2}";
t[111] = "Ошибка при фиксации подготовленной транзакции. commit xid={0}, preparedXid={1}, currentXid={2}";
t[114] = "Position: {0}";
diff --git a/src/main/java/org/postgresql/translation/messages_sr.java b/src/main/java/org/postgresql/translation/messages_sr.java
index 78cd1ca..9385937 100644
--- a/src/main/java/org/postgresql/translation/messages_sr.java
+++ b/src/main/java/org/postgresql/translation/messages_sr.java
@@ -136,7 +136,7 @@ public class messages_sr extends java.util.ResourceBundle {
t[299] = "PooledConnection je već zatvoren.";
t[302] = "ClientInfo property not supported.";
t[303] = "ClientInfo property nije podržan.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "Doneta veličina mora biti vrednost veća ili jednaka 0.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "Konekciju nije moguće kreirati uz pomoć protokola {0}.";
diff --git a/src/main/java/org/postgresql/translation/messages_tr.java b/src/main/java/org/postgresql/translation/messages_tr.java
index f6e0e94..cdf1ca1 100644
--- a/src/main/java/org/postgresql/translation/messages_tr.java
+++ b/src/main/java/org/postgresql/translation/messages_tr.java
@@ -136,7 +136,7 @@ public class messages_tr extends java.util.ResourceBundle {
t[299] = "Geçerli PooledConnection zaten önceden kapatıldı.";
t[302] = "ClientInfo property not supported.";
t[303] = "Clientinfo property'si desteklenememktedir.";
- t[306] = "Fetch size must be a value greater to or equal to 0.";
+ t[306] = "Fetch size must be a value greater than or equal to 0.";
t[307] = "Fetch boyutu sıfır veya daha büyük bir değer olmalıdır.";
t[312] = "A connection could not be made using the requested protocol {0}.";
t[313] = "İstenilen protokol ile bağlantı kurulamadı {0}";
diff --git a/src/main/java/org/postgresql/translation/messages_zh_CN.java b/src/main/java/org/postgresql/translation/messages_zh_CN.java
index 81adfb9..688be2e 100644
--- a/src/main/java/org/postgresql/translation/messages_zh_CN.java
+++ b/src/main/java/org/postgresql/translation/messages_zh_CN.java
@@ -82,7 +82,7 @@ public class messages_zh_CN extends java.util.ResourceBundle {
t[205] = "不能在 ResultSet 的第一笔数据之前呼叫 deleteRow()。";
t[214] = "The maximum field size must be a value greater than or equal to 0.";
t[215] = "最大栏位容量必须大于或等于 0。";
- t[216] = "Fetch size must be a value greater to or equal to 0.";
+ t[216] = "Fetch size must be a value greater than or equal to 0.";
t[217] = "数据读取笔数(fetch size)必须大于或等于 0。";
t[220] = "PostgreSQL LOBs can only index to: {0}";
t[221] = "PostgreSQL LOBs 仅能索引到:{0}";
diff --git a/src/main/java/org/postgresql/translation/messages_zh_TW.java b/src/main/java/org/postgresql/translation/messages_zh_TW.java
index 1d9eb65..d68c803 100644
--- a/src/main/java/org/postgresql/translation/messages_zh_TW.java
+++ b/src/main/java/org/postgresql/translation/messages_zh_TW.java
@@ -82,7 +82,7 @@ public class messages_zh_TW extends java.util.ResourceBundle {
t[205] = "不能在 ResultSet 的第一筆資料之前呼叫 deleteRow()。";
t[214] = "The maximum field size must be a value greater than or equal to 0.";
t[215] = "最大欄位容量必須大於或等於 0。";
- t[216] = "Fetch size must be a value greater to or equal to 0.";
+ t[216] = "Fetch size must be a value greater than or equal to 0.";
t[217] = "資料讀取筆數(fetch size)必須大於或等於 0。";
t[220] = "PostgreSQL LOBs can only index to: {0}";
t[221] = "PostgreSQL LOBs 僅能索引到:{0}";
diff --git a/src/main/java/org/postgresql/translation/nl.po b/src/main/java/org/postgresql/translation/nl.po
index 5de5f8b..4c12cc4 100644
--- a/src/main/java/org/postgresql/translation/nl.po
+++ b/src/main/java/org/postgresql/translation/nl.po
@@ -855,7 +855,7 @@ msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/pl.po b/src/main/java/org/postgresql/translation/pl.po
index 6d6a87c..7fe82cb 100644
--- a/src/main/java/org/postgresql/translation/pl.po
+++ b/src/main/java/org/postgresql/translation/pl.po
@@ -872,7 +872,7 @@ msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Rozmiar pobierania musi być wartością dodatnią lub 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/pt_BR.po b/src/main/java/org/postgresql/translation/pt_BR.po
index 9d5450d..73a4f30 100644
--- a/src/main/java/org/postgresql/translation/pt_BR.po
+++ b/src/main/java/org/postgresql/translation/pt_BR.po
@@ -901,7 +901,7 @@ msgstr "Não foi possível traduzir dado para codificação desejada."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Tamanho da busca deve ser um valor maior ou igual a 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/ru.po b/src/main/java/org/postgresql/translation/ru.po
index 5ceadc8..4be692f 100644
--- a/src/main/java/org/postgresql/translation/ru.po
+++ b/src/main/java/org/postgresql/translation/ru.po
@@ -907,7 +907,7 @@ msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr ""
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/sr.po b/src/main/java/org/postgresql/translation/sr.po
index 607ea03..59efc77 100644
--- a/src/main/java/org/postgresql/translation/sr.po
+++ b/src/main/java/org/postgresql/translation/sr.po
@@ -894,7 +894,7 @@ msgstr "Nije moguće prevesti podatke u odabrani encoding format."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Doneta veličina mora biti vrednost veća ili jednaka 0."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/tr.po b/src/main/java/org/postgresql/translation/tr.po
index 0c19ed0..6e337d1 100644
--- a/src/main/java/org/postgresql/translation/tr.po
+++ b/src/main/java/org/postgresql/translation/tr.po
@@ -880,7 +880,7 @@ msgstr "Veri, istenilen dil kodlamasına çevrilemiyor."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "Fetch boyutu sıfır veya daha büyük bir değer olmalıdır."
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/zh_CN.po b/src/main/java/org/postgresql/translation/zh_CN.po
index e4ed574..a723533 100644
--- a/src/main/java/org/postgresql/translation/zh_CN.po
+++ b/src/main/java/org/postgresql/translation/zh_CN.po
@@ -852,7 +852,7 @@ msgstr "无法将数据转成目标编码。"
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "数据读取笔数(fetch size)必须大于或等于 0。"
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/translation/zh_TW.po b/src/main/java/org/postgresql/translation/zh_TW.po
index 2c33128..5392ec6 100644
--- a/src/main/java/org/postgresql/translation/zh_TW.po
+++ b/src/main/java/org/postgresql/translation/zh_TW.po
@@ -852,7 +852,7 @@ msgstr "無法將資料轉成目標編碼。"
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
#: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
#: src/main/java/org/postgresql/jdbc/PgStatement.java:929
-msgid "Fetch size must be a value greater to or equal to 0."
+msgid "Fetch size must be a value greater than or equal to 0."
msgstr "資料讀取筆數(fetch size)必須大於或等於 0。"
#: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
diff --git a/src/main/java/org/postgresql/util/ByteBufferByteStreamWriter.java b/src/main/java/org/postgresql/util/ByteBufferByteStreamWriter.java
index be25be5..f0a8aa0 100644
--- a/src/main/java/org/postgresql/util/ByteBufferByteStreamWriter.java
+++ b/src/main/java/org/postgresql/util/ByteBufferByteStreamWriter.java
@@ -36,13 +36,17 @@ public int getLength() {
@Override
public void writeTo(ByteStreamTarget target) throws IOException {
+ if (buf.hasArray()) {
+ // Avoid copying the array if possible
+ target.getOutputStream()
+ .write(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
+ return;
+ }
+
// this _does_ involve some copying to a temporary buffer, but that's unavoidable
// as OutputStream itself only accepts single bytes or heap allocated byte arrays
- WritableByteChannel c = Channels.newChannel(target.getOutputStream());
- try {
+ try (WritableByteChannel c = Channels.newChannel(target.getOutputStream())) {
c.write(buf);
- } finally {
- c.close();
}
}
}
diff --git a/src/main/java/org/postgresql/util/ByteBuffersByteStreamWriter.java b/src/main/java/org/postgresql/util/ByteBuffersByteStreamWriter.java
new file mode 100644
index 0000000..9edde9f
--- /dev/null
+++ b/src/main/java/org/postgresql/util/ByteBuffersByteStreamWriter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * A {@link ByteStreamWriter} that writes a {@link ByteBuffer java.nio.ByteBuffer} to a byte array
+ * parameter.
+ */
+class ByteBuffersByteStreamWriter implements ByteStreamWriter {
+
+ private final ByteBuffer[] buffers;
+ private final int length;
+
+ /**
+ * Construct the writer with the given {@link ByteBuffer}
+ *
+ * @param buffers the buffer to use.
+ */
+ ByteBuffersByteStreamWriter(ByteBuffer... buffers) {
+ this.buffers = buffers;
+ int length = 0;
+ for (ByteBuffer buffer : buffers) {
+ length += buffer.remaining();
+ }
+ this.length = length;
+ }
+
+ @Override
+ public int getLength() {
+ return length;
+ }
+
+ @Override
+ public void writeTo(ByteStreamTarget target) throws IOException {
+ boolean allArraysAreAccessible = true;
+ for (ByteBuffer buffer : buffers) {
+ if (!buffer.hasArray()) {
+ allArraysAreAccessible = false;
+ break;
+ }
+ }
+
+ OutputStream os = target.getOutputStream();
+ if (allArraysAreAccessible) {
+ for (ByteBuffer buffer : buffers) {
+ os.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
+ }
+ return;
+ }
+ // Channels.newChannel does not buffer writes, so we can mix writes to the channel with writes
+ // to the OutputStream
+ try (WritableByteChannel c = Channels.newChannel(os)) {
+ for (ByteBuffer buffer : buffers) {
+ if (buffer.hasArray()) {
+ os.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
+ } else {
+ c.write(buffer);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/postgresql/util/ByteConverter.java b/src/main/java/org/postgresql/util/ByteConverter.java
index d2cc478..2a216e2 100644
--- a/src/main/java/org/postgresql/util/ByteConverter.java
+++ b/src/main/java/org/postgresql/util/ByteConverter.java
@@ -22,12 +22,12 @@ public class ByteConverter {
*/
private static final class PositiveShorts {
private short[] shorts = new short[8];
- private int idx = 0;
+ private int idx;
PositiveShorts() {
}
- public void push(short s) {
+ void push(short s) {
if (s < 0) {
throw new IllegalArgumentException("only non-negative values accepted: " + s);
}
@@ -37,15 +37,15 @@ public void push(short s) {
shorts[idx++] = s;
}
- public int size() {
+ int size() {
return idx;
}
- public boolean isEmpty() {
+ boolean isEmpty() {
return idx == 0;
}
- public short pop() {
+ short pop() {
return idx > 0 ? shorts[--idx] : -1;
}
@@ -59,6 +59,8 @@ private void grow() {
private static final short NUMERIC_POS = 0x0000;
private static final short NUMERIC_NEG = 0x4000;
private static final short NUMERIC_NAN = (short) 0xC000;
+ private static final short NUMERIC_PINF = (short) 0xD000;
+ private static final short NUMERIC_NINF = (short) 0xF000;
private static final int SHORT_BYTES = 2;
private static final int LONG_BYTES = 4;
private static final int[] INT_TEN_POWERS = new int[6];
@@ -68,13 +70,13 @@ private void grow() {
private static final BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
static {
- for (int i = 0; i < INT_TEN_POWERS.length; ++i) {
+ for (int i = 0; i < INT_TEN_POWERS.length; i++) {
INT_TEN_POWERS[i] = (int) Math.pow(10, i);
}
- for (int i = 0; i < LONG_TEN_POWERS.length; ++i) {
+ for (int i = 0; i < LONG_TEN_POWERS.length; i++) {
LONG_TEN_POWERS[i] = (long) Math.pow(10, i);
}
- for (int i = 0; i < BI_TEN_POWERS.length; ++i) {
+ for (int i = 0; i < BI_TEN_POWERS.length; i++) {
BI_TEN_POWERS[i] = BigInteger.TEN.pow(i);
}
}
@@ -90,7 +92,7 @@ private ByteConverter() {
*/
public static int bytesToInt(byte []bytes) {
if ( bytes.length == 1 ) {
- return (int)bytes[0];
+ return (int) bytes[0];
}
if ( bytes.length == SHORT_BYTES ) {
return int2(bytes, 0);
@@ -113,13 +115,14 @@ public static Number numeric(byte [] bytes) {
/**
* Convert a variable length array of bytes to a {@link Number}. The result will
- * always be a {@link BigDecimal} or {@link Double#NaN}.
+ * always be a {@link BigDecimal} or a special {@link Double} value.
*
* @param bytes array of bytes to be decoded from binary numeric representation.
* @param pos index of the start position of the bytes array for number
* @param numBytes number of bytes to use, length is already encoded
* in the binary format but this is used for double checking
- * @return BigDecimal representation of numeric or {@link Double#NaN}.
+ * @return BigDecimal representation of numeric or one of the special double values
+ * {@link Double#NaN}, {@link Double#NEGATIVE_INFINITY} and {@link Double#POSITIVE_INFINITY}.
*/
public static Number numeric(byte [] bytes, int pos, int numBytes) {
@@ -127,12 +130,12 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
throw new IllegalArgumentException("number of bytes should be at-least 8");
}
- //number of 2-byte shorts representing 4 decimal digits
- short len = ByteConverter.int2(bytes, pos);
+ //number of 2-byte shorts representing 4 decimal digits - should be treated as unsigned
+ int len = ByteConverter.int2(bytes, pos) & 0xFFFF;
//0 based number of 4 decimal digits (i.e. 2-byte shorts) before the decimal
//a value <= 0 indicates an absolute value < 1.
short weight = ByteConverter.int2(bytes, pos + 2);
- //indicates positive, negative or NaN
+ //indicates positive, negative, NaN, Infinity or -Infinity
short sign = ByteConverter.int2(bytes, pos + 4);
//number of digits after the decimal. This must be >= 0.
//a value of 0 indicates a whole number (integer).
@@ -157,14 +160,18 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
throw new IllegalArgumentException("invalid length of bytes \"numeric\" value");
}
- if (!(sign == NUMERIC_POS
- || sign == NUMERIC_NEG
- || sign == NUMERIC_NAN)) {
- throw new IllegalArgumentException("invalid sign in \"numeric\" value");
- }
-
- if (sign == NUMERIC_NAN) {
- return Double.NaN;
+ switch (sign) {
+ case NUMERIC_POS:
+ case NUMERIC_NEG:
+ break;
+ case NUMERIC_NAN:
+ return Double.NaN;
+ case NUMERIC_PINF:
+ return Double.POSITIVE_INFINITY;
+ case NUMERIC_NINF:
+ return Double.NEGATIVE_INFINITY;
+ default:
+ throw new IllegalArgumentException("invalid sign in \"numeric\" value");
}
if ((scale & NUMERIC_DSCALE_MASK) != scale) {
@@ -184,35 +191,45 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
if (weight < 0) {
assert scale > 0;
int effectiveScale = scale;
+ //adjust weight to determine how many leading 0s after the decimal
+ //before the provided values/digits actually begin
++weight;
if (weight < 0) {
- effectiveScale += (4 * weight);
+ effectiveScale += 4 * weight;
}
int i = 1;
//typically there should not be leading 0 short values, as it is more
//efficient to represent that in the weight value
- for ( ; i < len && d == 0; ++i) {
+ for (; i < len && d == 0; i++) {
+ //each leading 0 value removes 4 from the effective scale
effectiveScale -= 4;
idx += 2;
d = ByteConverter.int2(bytes, idx);
}
- BigInteger unscaledBI = null;
assert effectiveScale > 0;
if (effectiveScale >= 4) {
effectiveScale -= 4;
} else {
+ //an effective scale of less than four means that the value d
+ //has trailing 0s which are not significant
+ //so we divide by the appropriate power of 10 to reduce those
d = (short) (d / INT_TEN_POWERS[4 - effectiveScale]);
effectiveScale = 0;
}
+ //defer moving to BigInteger as long as possible
+ //operations on the long are much faster
+ BigInteger unscaledBI = null;
long unscaledInt = d;
- for ( ; i < len; ++i) {
+ for (; i < len; i++) {
if (i == 4 && effectiveScale > 2) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
idx += 2;
d = ByteConverter.int2(bytes, idx);
+ //if effective scale is at least 4, then all 4 digits should be used
+ //and the existing number needs to be shifted 4
if (effectiveScale >= 4) {
if (unscaledBI == null) {
unscaledInt *= 10000;
@@ -221,11 +238,14 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
}
effectiveScale -= 4;
} else {
+ //if effective scale is less than 4, then only shift left based on remaining scale
if (unscaledBI == null) {
unscaledInt *= INT_TEN_POWERS[effectiveScale];
} else {
unscaledBI = unscaledBI.multiply(tenPower(effectiveScale));
}
+ //and d needs to be shifted to the right to only get correct number of
+ //significant digits
d = (short) (d / INT_TEN_POWERS[4 - effectiveScale]);
effectiveScale = 0;
}
@@ -237,9 +257,11 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
}
}
}
+ //now we need BigInteger to create BigDecimal
if (unscaledBI == null) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
+ //if there is remaining effective scale, apply it here
if (effectiveScale > 0) {
unscaledBI = unscaledBI.multiply(tenPower(effectiveScale));
}
@@ -252,9 +274,12 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
//if there is no scale, then shorts are the unscaled int
if (scale == 0) {
+ //defer moving to BigInteger as long as possible
+ //operations on the long are much faster
BigInteger unscaledBI = null;
long unscaledInt = d;
- for (int i = 1; i < len; ++i) {
+ //loop over all of the len shorts to process as the unscaled int
+ for (int i = 1; i < len; i++) {
if (i == 4) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
@@ -270,12 +295,14 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
}
}
}
+ //now we need BigInteger to create BigDecimal
if (unscaledBI == null) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
if (sign == NUMERIC_NEG) {
unscaledBI = unscaledBI.negate();
}
+ //the difference between len and weight (adjusted from 0 based) becomes the scale for BigDecimal
final int bigDecScale = (len - (weight + 1)) * 4;
//string representation always results in a BigDecimal with scale of 0
//the binary representation, where weight and len can infer trailing 0s, can result in a negative scale
@@ -283,16 +310,21 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
return bigDecScale == 0 ? new BigDecimal(unscaledBI) : new BigDecimal(unscaledBI, bigDecScale).setScale(0);
}
+ //defer moving to BigInteger as long as possible
+ //operations on the long are much faster
BigInteger unscaledBI = null;
long unscaledInt = d;
+ //weight and scale as defined by postgresql are a bit different than how BigDecimal treats scale
+ //maintain the effective values to massage as we process through values
int effectiveWeight = weight;
int effectiveScale = scale;
- for (int i = 1 ; i < len; ++i) {
+ for (int i = 1; i < len; i++) {
if (i == 4) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
idx += 2;
d = ByteConverter.int2(bytes, idx);
+ //first process effective weight down to 0
if (effectiveWeight > 0) {
--effectiveWeight;
if (unscaledBI == null) {
@@ -301,6 +333,8 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
unscaledBI = unscaledBI.multiply(BI_TEN_THOUSAND);
}
} else if (effectiveScale >= 4) {
+ //if effective scale is at least 4, then all 4 digits should be used
+ //and the existing number needs to be shifted 4
effectiveScale -= 4;
if (unscaledBI == null) {
unscaledInt *= 10000;
@@ -308,11 +342,14 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
unscaledBI = unscaledBI.multiply(BI_TEN_THOUSAND);
}
} else {
+ //if effective scale is less than 4, then only shift left based on remaining scale
if (unscaledBI == null) {
unscaledInt *= INT_TEN_POWERS[effectiveScale];
} else {
unscaledBI = unscaledBI.multiply(tenPower(effectiveScale));
}
+ //and d needs to be shifted to the right to only get correct number of
+ //significant digits
d = (short) (d / INT_TEN_POWERS[4 - effectiveScale]);
effectiveScale = 0;
}
@@ -325,12 +362,15 @@ public static Number numeric(byte [] bytes, int pos, int numBytes) {
}
}
+ //now we need BigInteger to create BigDecimal
if (unscaledBI == null) {
unscaledBI = BigInteger.valueOf(unscaledInt);
}
+ //if there is remaining weight, apply it here
if (effectiveWeight > 0) {
unscaledBI = unscaledBI.multiply(tenPower(effectiveWeight * 4));
}
+ //if there is remaining effective scale, apply it here
if (effectiveScale > 0) {
unscaledBI = unscaledBI.multiply(tenPower(effectiveScale));
}
@@ -351,7 +391,7 @@ public static byte[] numeric(BigDecimal nbr) {
BigInteger unscaled = nbr.unscaledValue().abs();
int scale = nbr.scale();
if (unscaled.equals(BigInteger.ZERO)) {
- final byte[] bytes = new byte[] {0,0,-1,-1,0,0,0,0};
+ final byte[] bytes = new byte[]{0, 0, -1, -1, 0, 0, 0, 0};
ByteConverter.int2(bytes, 6, Math.max(0, scale));
return bytes;
}
@@ -415,7 +455,7 @@ public static byte[] numeric(BigDecimal nbr) {
weight -= segments;
} else {
//now add leading 0 shorts
- for (int i = 0; i < segments; ++i) {
+ for (int i = 0; i < segments; i++) {
shorts.push((short) 0);
}
}
@@ -493,7 +533,7 @@ public static int int4(byte[] bytes, int idx) {
((bytes[idx] & 255) << 24)
+ ((bytes[idx + 1] & 255) << 16)
+ ((bytes[idx + 2] & 255) << 8)
- + ((bytes[idx + 3] & 255));
+ + (bytes[idx + 3] & 255);
}
/**
@@ -504,7 +544,7 @@ public static int int4(byte[] bytes, int idx) {
* @return parsed short value.
*/
public static short int2(byte[] bytes, int idx) {
- return (short) (((bytes[idx] & 255) << 8) + ((bytes[idx + 1] & 255)));
+ return (short) (((bytes[idx] & 255) << 8) + (bytes[idx + 1] & 255));
}
/**
diff --git a/src/main/java/org/postgresql/util/ByteStreamWriter.java b/src/main/java/org/postgresql/util/ByteStreamWriter.java
index 798f938..381a06f 100644
--- a/src/main/java/org/postgresql/util/ByteStreamWriter.java
+++ b/src/main/java/org/postgresql/util/ByteStreamWriter.java
@@ -8,6 +8,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
/**
* A class that can be used to set a byte array parameter by writing to an OutputStream.
@@ -15,16 +16,20 @@
*
The intended use case is wanting to write data to a byte array parameter that is stored off
* heap in a direct memory pool or in some other form that is inconvenient to assemble into a single
* heap-allocated buffer.
- *
Users should write their own implementation depending on the
+ *
+ *
Users should write their own implementation depending on the
* original data source. The driver provides a built-in implementation supporting the {@link
* java.nio.ByteBuffer} class, see {@link ByteBufferByteStreamWriter}.
- *
Intended usage is to simply pass in an instance using
+ *
+ *
Intended usage is to simply pass in an instance using
* {@link java.sql.PreparedStatement#setObject(int, Object)}:
*
* int bufLength = someBufferObject.length();
* preparedStatement.setObject(1, new MyByteStreamWriter(bufLength, someBufferObject));
*
+ *
*
The length must be known ahead of the stream being written to.
+ *
*
This provides the application more control over memory management than calling
* {@link java.sql.PreparedStatement#setBinaryStream(int, InputStream)} as with the latter the
* caller has no control over the buffering strategy.
@@ -34,7 +39,7 @@ public interface ByteStreamWriter {
/**
* Returns the length of the stream.
*
- *
This must be known ahead of calling {@link #writeTo(ByteStreamTarget)}.
+ *
This must be known ahead of calling {@link #writeTo(ByteStreamTarget)}.
*
* @return the number of bytes in the stream.
*/
@@ -43,7 +48,7 @@ public interface ByteStreamWriter {
/**
* Write the data to the provided {@link OutputStream}.
*
- *
Should not write more than {@link #getLength()} bytes. If attempted, the provided stream
+ *
Should not write more than {@link #getLength()} bytes. If attempted, the provided stream
* will throw an {@link java.io.IOException}.
*
* @param target the stream to write the data to
@@ -51,6 +56,12 @@ public interface ByteStreamWriter {
*/
void writeTo(ByteStreamTarget target) throws IOException;
+ static ByteStreamWriter of(ByteBuffer... buf) {
+ return buf.length == 1
+ ? new ByteBufferByteStreamWriter(buf[0])
+ : new ByteBuffersByteStreamWriter(buf);
+ }
+
/**
* Provides a target to write bytes to.
*/
diff --git a/src/main/java/org/postgresql/util/ClassUtils.java b/src/main/java/org/postgresql/util/ClassUtils.java
new file mode 100644
index 0000000..78cc805
--- /dev/null
+++ b/src/main/java/org/postgresql/util/ClassUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Utility class for safe class loading operations.
+ */
+public final class ClassUtils {
+
+ private ClassUtils() {
+ // Utility class
+ }
+
+ /**
+ * Safely loads a class using the three-parameter Class.forName with initialize=false
+ * and validates it's assignable to the expected type.
+ *
+ * @param className the name of the class to load
+ * @param expectedClass the expected superclass or interface
+ * @param classLoader the class loader to use
+ * @param the expected type
+ * @return the loaded class as a subclass of the expected type
+ * @throws ClassNotFoundException if the class cannot be found
+ */
+ public static Class extends T> forName(String className, Class expectedClass, /* @Nullable */ ClassLoader classLoader)
+ throws ClassNotFoundException {
+ return Class.forName(className, false, classLoader).asSubclass(expectedClass);
+ }
+}
diff --git a/src/main/java/org/postgresql/util/DriverInfo.java b/src/main/java/org/postgresql/util/DriverInfo.java
index 6559234..c003b55 100644
--- a/src/main/java/org/postgresql/util/DriverInfo.java
+++ b/src/main/java/org/postgresql/util/DriverInfo.java
@@ -16,13 +16,13 @@ private DriverInfo() {
// Driver name
public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
public static final String DRIVER_SHORT_NAME = "PgJDBC";
- public static final String DRIVER_VERSION = "42.3.1";
+ public static final String DRIVER_VERSION = "42.7.11";
public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + DRIVER_VERSION;
// Driver version
public static final int MAJOR_VERSION = 42;
- public static final int MINOR_VERSION = 3;
- public static final int PATCH_VERSION = 1;
+ public static final int MINOR_VERSION = 7;
+ public static final int PATCH_VERSION = 11;
// JDBC specification
public static final String JDBC_VERSION = "4.2";
diff --git a/src/main/java/org/postgresql/util/ExpressionProperties.java b/src/main/java/org/postgresql/util/ExpressionProperties.java
index bf8a7c0..3fe46c6 100644
--- a/src/main/java/org/postgresql/util/ExpressionProperties.java
+++ b/src/main/java/org/postgresql/util/ExpressionProperties.java
@@ -19,6 +19,7 @@ public class ExpressionProperties extends Properties {
private static final /* @Regex(1) */ Pattern EXPRESSION = Pattern.compile("\\$\\{([^}]+)\\}");
+ @SuppressWarnings("HidingField")
private final Properties[] defaults;
/**
@@ -31,8 +32,8 @@ public ExpressionProperties(Properties ...defaults) {
}
/**
- *
Returns property value with all {@code ${propKey}} like references replaced with the value of
- * the relevant property with recursive resolution.
+ * Returns property value with all {@code ${propKey}} like references replaced with the value of
+ * the relevant property with recursive resolution.
*
*
The method returns null if the property is not found.
*
diff --git a/src/main/java/org/postgresql/util/HStoreConverter.java b/src/main/java/org/postgresql/util/HStoreConverter.java
index 2a112bc..02928d4 100644
--- a/src/main/java/org/postgresql/util/HStoreConverter.java
+++ b/src/main/java/org/postgresql/util/HStoreConverter.java
@@ -24,7 +24,7 @@ public class HStoreConverter {
int numElements = ByteConverter.int4(b, pos);
pos += 4;
try {
- for (int i = 0; i < numElements; ++i) {
+ for (int i = 0; i < numElements; i++) {
int keyLen = ByteConverter.int4(b, pos);
pos += 4;
String key = encoding.decode(b, pos, keyLen);
diff --git a/src/main/java/org/postgresql/util/HostSpec.java b/src/main/java/org/postgresql/util/HostSpec.java
index 6858a9f..1b9d877 100644
--- a/src/main/java/org/postgresql/util/HostSpec.java
+++ b/src/main/java/org/postgresql/util/HostSpec.java
@@ -9,6 +9,7 @@
// import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -41,6 +42,7 @@ public int getPort() {
return port;
}
+ @Override
public String toString() {
return host + ":" + port;
}
@@ -80,12 +82,12 @@ private Boolean matchesNonProxyHosts() {
}
@SuppressWarnings("regex")
- private /* @Nullable */ Pattern toPattern(String mask) {
+ private static /* @Nullable */ Pattern toPattern(String mask) {
StringBuilder joiner = new StringBuilder();
String separator = "";
for (String disjunct : mask.split("\\|")) {
if (!disjunct.isEmpty()) {
- String regex = disjunctToRegex(disjunct.toLowerCase());
+ String regex = disjunctToRegex(disjunct.toLowerCase(Locale.ROOT));
joiner.append(separator).append(regex);
separator = "|";
}
@@ -94,7 +96,7 @@ private Boolean matchesNonProxyHosts() {
return joiner.length() == 0 ? null : compile(joiner.toString());
}
- private String disjunctToRegex(String disjunct) {
+ private static String disjunctToRegex(String disjunct) {
String regex;
if (disjunct.startsWith("*")) {
diff --git a/src/main/java/org/postgresql/util/IntList.java b/src/main/java/org/postgresql/util/IntList.java
new file mode 100644
index 0000000..7e701ed
--- /dev/null
+++ b/src/main/java/org/postgresql/util/IntList.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+import java.util.Arrays;
+
+/**
+ * A specialized class to store a list of {@code int} values, so it does not need auto-boxing. Note:
+ * this is a driver-internal class, and it is not intended to be used outside the driver.
+ */
+public final class IntList {
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
+ private int[] ints = EMPTY_INT_ARRAY;
+ private int size;
+
+ public void add(int i) {
+ int size = this.size;
+ ensureCapacity(size);
+ ints[size] = i;
+ this.size = size + 1;
+ }
+
+ private void ensureCapacity(int size) {
+ int length = ints.length;
+ if (size >= length) {
+ // double in size until 1024 in size, then grow by 1.5x
+ final int newLength = length == 0 ? 8 :
+ length < 1024 ? length << 1 :
+ (length + (length >> 1));
+ ints = Arrays.copyOf(ints, newLength);
+ }
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public int get(int i) {
+ if (i < 0 || i >= size) {
+ throw new ArrayIndexOutOfBoundsException("Index: " + i + ", Size: " + size);
+ }
+ return ints[i];
+ }
+
+ public void clear() {
+ size = 0;
+ }
+
+ /**
+ * Returns an array containing all the elements in this list. The modifications of the returned
+ * array will not affect this list.
+ *
+ * @return an array containing all the elements in this list
+ */
+ public int[] toArray() {
+ if (size == 0) {
+ return EMPTY_INT_ARRAY;
+ }
+ return Arrays.copyOf(ints, size);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[");
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(ints[i]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/postgresql/util/KerberosTicket.java b/src/main/java/org/postgresql/util/KerberosTicket.java
new file mode 100644
index 0000000..096c51e
--- /dev/null
+++ b/src/main/java/org/postgresql/util/KerberosTicket.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+public class KerberosTicket {
+
+ private static final String CONFIG_ITEM_NAME = "ticketCache";
+ private static final String KRBLOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
+
+ static class CustomKrbConfig extends Configuration {
+
+ @SuppressWarnings("nullness")
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ if (CONFIG_ITEM_NAME.equals(name)) {
+ Map options = new HashMap<>();
+ options.put("refreshKrb5Config", String.valueOf(false));
+ options.put("useTicketCache", String.valueOf(true));
+ options.put("doNotPrompt", String.valueOf(true));
+ options.put("useKeyTab", String.valueOf(true));
+ options.put("isInitiator", String.valueOf(false));
+ options.put("renewTGT", String.valueOf(false));
+ options.put("debug", String.valueOf(false));
+ return new AppConfigurationEntry[]{
+ new AppConfigurationEntry(KRBLOGIN_MODULE,
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)};
+ }
+ return null;
+ }
+
+ }
+
+ public static boolean credentialCacheExists(Properties info) {
+ LoginContext lc = null;
+
+ // in the event that the user has specified a jaas.conf file then we want to remember it
+ Configuration existingConfiguration = Configuration.getConfiguration();
+ Configuration.setConfiguration(new CustomKrbConfig());
+
+ try {
+ lc = new LoginContext(CONFIG_ITEM_NAME, new CallbackHandler() {
+
+ @Override
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ // if the user has not configured jaasLogin correctly this can happen
+ throw new RuntimeException("This is an error, you should set doNotPrompt to false in jaas.config");
+ }
+ });
+ lc.login();
+ } catch (LoginException e) {
+ // restore saved configuration
+ if (existingConfiguration != null ) {
+ Configuration.setConfiguration(existingConfiguration);
+ }
+ return false;
+ }
+ // restore saved configuration
+ if (existingConfiguration != null ) {
+ Configuration.setConfiguration(existingConfiguration);
+ }
+ Subject sub = lc.getSubject();
+ return sub != null;
+ }
+}
diff --git a/src/main/java/org/postgresql/util/LazyCleaner.java b/src/main/java/org/postgresql/util/LazyCleaner.java
new file mode 100644
index 0000000..24b9b46
--- /dev/null
+++ b/src/main/java/org/postgresql/util/LazyCleaner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+public interface LazyCleaner {
+ /**
+ * Cleanable interface for objects that can be manually cleaned.
+ *
+ * @param the type of exception that can be thrown during cleanup
+ */
+ interface Cleanable {
+ void clean() throws T;
+ }
+
+ /**
+ * CleaningAction interface for cleanup actions that are notified whether cleanup
+ * occurred due to a leak (automatic) or manual cleanup.
+ *
+ * @param the type of exception that can be thrown during cleanup
+ */
+ interface CleaningAction {
+ void onClean(boolean leak) throws T;
+ }
+
+ /**
+ * Registers an object for cleanup when it becomes phantom reachable.
+ *
+ * @param obj the object to monitor for cleanup (should not be the same as action)
+ * @param action the action to perform when the object becomes unreachable
+ * @param the type of exception that can be thrown during cleanup
+ * @return a Cleanable that can be used to manually trigger cleanup
+ */
+ Cleanable register(Object obj, CleaningAction action);
+}
diff --git a/src/main/java/org/postgresql/util/LazyCleanerImpl.java b/src/main/java/org/postgresql/util/LazyCleanerImpl.java
new file mode 100644
index 0000000..5f5f6a1
--- /dev/null
+++ b/src/main/java/org/postgresql/util/LazyCleanerImpl.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+/* changes were made to move it into the org.postgresql.util package
+ *
+ * Copyright 2022 Juan Lopes
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.postgresql.util;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.time.Duration;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.BooleanSupplier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * LazyCleaner is a utility class that allows to register objects for deferred cleanup.
+ *
+ *
This is the Java 8 compatible implementation that uses PhantomReferences
+ * and ForkJoinPool for deferred cleanup operations.
+ *
+ *
On Java 11+, this class is replaced by a version that uses the native
+ * {@link java.lang.ref.Cleaner} API via the multi-release JAR mechanism.
+ *
+ *
Note: this is a driver-internal class
+ */
+public class LazyCleanerImpl implements LazyCleaner {
+ private static final Logger LOGGER = Logger.getLogger(LazyCleanerImpl.class.getName());
+ private static final LazyCleanerImpl instance = new LazyCleanerImpl(
+ "PostgreSQL-JDBC-Cleaner",
+ Duration.ofMillis(Long.getLong("pgjdbc.config.cleanup.thread.ttl", 30000))
+ );
+
+ private final ReferenceQueue