11/*
2- * Copyright (c) 2015-2024 Tada AB and other contributors, as listed below.
2+ * Copyright (c) 2015-2026 Tada AB and other contributors, as listed below.
33 *
44 * All rights reserved. This program and the accompanying materials
55 * are made available under the terms of the The BSD 3-Clause License
3030 * For "Node" behavior:
3131 */
3232
33+ import java .io .InterruptedIOException ;
34+ import java .io .IOException ;
35+
3336import static java .lang .ProcessBuilder .Redirect .INHERIT ;
3437import java .lang .reflect .InvocationHandler ; // flexible SAM allowing exceptions
3538import java .lang .reflect .UndeclaredThrowableException ;
@@ -155,13 +158,13 @@ public class Node extends JarX {
155158
156159 private Matcher m_prefix ;
157160 private int m_fsepLength ;
158- private String m_lineSep ;
159161 private boolean m_dryrun = false ;
160162
161163 private static Node s_jarxHelper = new Node (null , 0 , null , null );
162164 private static boolean s_jarProcessed = false ;
163165 private static String s_examplesJar ;
164166 private static String s_sharedObject ;
167+ private static String s_lineSep = getProperty ("line.separator" );
165168
166169 /**
167170 * Performs an ordinary installation, using {@code pg_config} or the
@@ -205,7 +208,6 @@ public void prepareResolver(String v) throws Exception
205208 {
206209 m_prefix = compile ("^pljava/([^/]+dir)(?![^/])" ).matcher ("" );
207210 m_fsepLength = getProperty ("file.separator" ).length ();
208- m_lineSep = getProperty ("line.separator" );
209211 }
210212
211213 /**
@@ -227,42 +229,7 @@ public String resolve(String storedPath, String platformPath)
227229 String replacement = getProperty (propkey );
228230 if ( null == replacement )
229231 {
230- String pgc = getProperty ("pgconfig" , "pg_config" );
231- ProcessBuilder pb = new ProcessBuilder (pgc , "--" +key );
232- pb .redirectError (ProcessBuilder .Redirect .INHERIT );
233- Process proc = pb .start ();
234- byte [] output ;
235- try ( InputStream instream = proc .getInputStream () )
236- {
237- proc .getOutputStream ().close ();
238- output = instream .readAllBytes ();
239- }
240- finally
241- {
242- int status = proc .waitFor ();
243- if ( 0 != status )
244- {
245- System .err .println (
246- "ERROR: pg_config status is " +status );
247- System .exit (1 );
248- }
249- }
250- /*
251- * pg_config output is the saved value followed by one \n only.
252- * However, on Windows, the C library treats stdout as text mode
253- * by default, and pg_config does nothing to change that, so the
254- * single \n written by pg_config gets turned to \r\n before it
255- * arrives here. The earlier use of the trim() method papered
256- * over the problem, but trim() can remove too much. Simply have
257- * to assume that the string will end with line.separator, and
258- * remove that.
259- */
260- replacement = defaultCharset ().newDecoder ()
261- .decode (ByteBuffer .wrap (output , 0 , output .length ))
262- .toString ();
263- assert replacement .endsWith (m_lineSep );
264- replacement = replacement .substring (0 ,
265- replacement .length () - m_lineSep .length ());
232+ replacement = getPgConfigProperty ("--" +key );
266233 setProperty (propkey , replacement );
267234 }
268235 int plen = m_fsepLength - 1 ; /* original separator had length 1 */
@@ -288,6 +255,64 @@ else if ( storedPath.matches(
288255 * of this class that is acting as a "Node" rather than as the JarX helper.
289256 */
290257
258+ /**
259+ * Returns the output, decoded using default platform charset, of the
260+ * {@code pg_config} command executed with the single supplied argument.
261+ * <p>
262+ * If multiple versions of {@code pg_config} are available or
263+ * {@code pg_config} is not present on the path, the system property
264+ * {@code pgconfig} should be set as an absolute path to the desired
265+ * executable.
266+ * <p>
267+ * For example, {@code getPgConfigProperty("--version")} can be used if
268+ * version information is needed early for selecting options to pass to
269+ * {@code init} or {@code initialized_cluster}.
270+ *
271+ * @see #init(Map,UnaryOperator) init
272+ *
273+ * @param pgConfigArgument argument to be passed to the command
274+ * @return output of the input command executed with the input argument
275+ * @throws IOException if unable to read output of the command
276+ * @throws InterruptedException if command does not complete successfully
277+ */
278+ public static String getPgConfigProperty (String pgConfigArgument )
279+ throws IOException , InterruptedException
280+ {
281+ String pgc = getProperty ("pgconfig" , "pg_config" );
282+ ProcessBuilder pb = new ProcessBuilder (pgc , pgConfigArgument );
283+ pb .redirectError (INHERIT );
284+ Process proc = pb .start ();
285+ byte [] output ;
286+ try ( InputStream instream = proc .getInputStream () )
287+ {
288+ proc .getOutputStream ().close ();
289+ output = instream .readAllBytes ();
290+ }
291+ finally
292+ {
293+ int status = proc .waitFor ();
294+ if ( 0 != status )
295+ throw new InterruptedIOException (
296+ "pg_config has exited with status " + status );
297+ }
298+ /*
299+ * pg_config output is the saved value followed by one \n only.
300+ * However, on Windows, the C library treats stdout as text mode
301+ * by default, and pg_config does nothing to change that, so the
302+ * single \n written by pg_config gets turned to \r\n before it
303+ * arrives here. The earlier use of the trim() method papered
304+ * over the problem, but trim() can remove too much. Simply have
305+ * to assume that the string will end with line.separator, and
306+ * remove that.
307+ */
308+ String replacement = defaultCharset ().newDecoder ()
309+ .decode (ByteBuffer .wrap (output , 0 , output .length ))
310+ .toString ();
311+ assert replacement .endsWith (s_lineSep );
312+ return replacement .substring (0 ,
313+ replacement .length () - s_lineSep .length ());
314+ }
315+
291316 /**
292317 * True if the platform is determined to be Windows.
293318 *<p>
@@ -792,20 +817,38 @@ public void init(UnaryOperator<ProcessBuilder> tweaks) throws Exception
792817 * and <em>tweaks</em> to be applied to the {@code ProcessBuilder}
793818 * before it is started.
794819 *<p>
795- * By default, {@code postgres} will be the name of the superuser, UTF-8
796- * will be the encoding, {@code auth-local} will be {@code peer} and
797- * {@code auth-host} will be {@code md5}. The initialization will skip
798- * {@code fsync} for speed rather than safety (if something goes wrong, just
799- * {@code clean_node()} and start over).
820+ * When any of the following is not present in <em>suppliedOptions</em>,
821+ * it will default as follows: {@code postgres} will be the name of
822+ * the superuser, UTF-8 will be the encoding, {@code auth-local} will be
823+ * {@code peer}, and {@code auth-host} will be an authentication method
824+ * using passwords (see below).
825+ *<p>
826+ * The initialization will, by default, skip {@code fsync} for speed rather
827+ * than safety (if something goes wrong, just {@code clean_node()} and
828+ * start over).
800829 *<p>
801830 * The {@code initdb} that will be run is the one in the {@code bindir}
802831 * reported by {@code pg_config} (or set by {@code -Dpgconfig.bindir}).
832+ *<p>
833+ * <strong>Password authentication methods:</strong> Early versions of
834+ * this class defaulted to {@code md5}, which has been deprecated. As of
835+ * this writing, the default is now {@code scram-sha-256}, which became
836+ * available in PostgreSQL 10. If this class is used in automated testing
837+ * of PL/Java support for earlier PostgreSQL releases, the script will need
838+ * to pass something like {@code Map.of("--auth-host", "md5")} with
839+ * <em>suppliedOptions</em>. A script that needs to make that decision can
840+ * use {@code getPgConfigProperty("--version")} to retrieve a version string
841+ * before calling this method (and, therefore, before a data directory has
842+ * been populated with a {@code PG_VERSION} file).
803843 * @param suppliedOptions a Map where each key is an option to initdb
804844 * (for example, --encoding), and the value corresponds.
805845 * @param tweaks a lambda applicable to the {@code ProcessBuilder} to
806846 * further configure it. On Windows, the tweaks will be applied ahead of
807847 * transformation of the arguments by
808848 * {@link #forWindowsCRuntime forWindowsCRuntime}.
849+ *
850+ * @see #initialized_cluster(Map,UnaryOperator) initialized_cluster
851+ * @see #getPgConfigProperty getPgConfigProperty
809852 */
810853 public void init (
811854 Map <String ,String > suppliedOptions ,
@@ -840,7 +883,7 @@ public void init(
840883 options .putIfAbsent ("--encoding" , "utf-8" );
841884 options .putIfAbsent ("--pwfile" , pwfile .toString ());
842885 options .putIfAbsent ("--auth-local" , "peer" );
843- options .putIfAbsent ("--auth-host" , "md5 " );
886+ options .putIfAbsent ("--auth-host" , "scram-sha-256 " );
844887 options .putIfAbsent ("-N" , null );
845888
846889 String [] args =
0 commit comments