66 - ppc64le
77dist : bionic
88env :
9+ - POSTGRESQL_VERSION : 13
10+ JAVA_VERSION : 15
11+ JVM_IMPL : hotspot
12+ MVN_VERSION : 3.5.2
913 - POSTGRESQL_VERSION : 12
1014 JAVA_VERSION : 14
1115 JVM_IMPL : hotspot
@@ -151,8 +155,13 @@ script: |
151155
152156 boolean succeeding = false; // begin pessimistic
153157
158+ import static java.nio.file.Files.createTempFile
159+ import static java.nio.file.Files.write
160+ import java.nio.file.Path
154161 import static java.nio.file.Paths.get
155162 import java.sql.Connection
163+ import java.sql.PreparedStatement
164+ import java.sql.ResultSet
156165 import org.postgresql.pljava.packaging.Node
157166 import static org.postgresql.pljava.packaging.Node.q
158167 import static org.postgresql.pljava.packaging.Node.stateMachine
@@ -200,7 +209,7 @@ script: |
200209 "create extension no result",
201210 null,
202211
203- q(c, "create extension pljava")
212+ q(c, "CREATE EXTENSION pljava")
204213 .flatMap(Node::semiFlattenDiagnostics)
205214 .peek(Node::peek),
206215
@@ -251,6 +260,70 @@ script: |
251260 (o,p,q) -> null == o
252261 );
253262
263+ /*
264+ * Exercise TrialPolicy some. Need another connection to change
265+ * vmoptions. Uses some example functions, so insert here before the
266+ * test of undeploying the examples.
267+ */
268+ try ( Connection c2 = n1.connect() )
269+ {
270+ Path trialPolicy =
271+ createTempFile(n1.data_dir().getParent(), "trial", "policy");
272+
273+ write(trialPolicy, List.of(
274+ "grant {",
275+ " permission",
276+ " org.postgresql.pljava.policy.TrialPolicy$Permission;",
277+ "};"
278+ ));
279+
280+ PreparedStatement setVmOpts = c2.prepareStatement(
281+ "SELECT null::pg_catalog.void" +
282+ " FROM pg_catalog.set_config('pljava.vmoptions', ?, false)"
283+ );
284+
285+ setVmOpts.setString(1, vmopts +
286+ " -Dorg.postgresql.pljava.policy.trial=" + trialPolicy.toUri());
287+
288+ succeeding &= stateMachine(
289+ "change pljava.vmoptions",
290+ null,
291+
292+ q(setVmOpts, setVmOpts::execute)
293+ .flatMap(Node::semiFlattenDiagnostics)
294+ .peek(Node::peek),
295+
296+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
297+ (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false,
298+ (o,p,q) -> null == o
299+ );
300+
301+ PreparedStatement tryForbiddenRead = c2.prepareStatement(
302+ "SELECT" +
303+ " CASE WHEN javatest.java_getsystemproperty('java.home')" +
304+ " OPERATOR(pg_catalog.=) ?" +
305+ " THEN javatest.logmessage('INFO', 'trial policy test ok')" +
306+ " ELSE javatest.logmessage('WARNING', 'trial policy test ng')" +
307+ " END"
308+ );
309+
310+ tryForbiddenRead.setString(1, System.getProperty("java.home"));
311+
312+ succeeding &= stateMachine(
313+ "try to read a forbidden property",
314+ null,
315+
316+ q(tryForbiddenRead, tryForbiddenRead::execute)
317+ .flatMap(Node::semiFlattenDiagnostics)
318+ .peek(Node::peek),
319+
320+ (o,p,q) -> isDiagnostic(o, Set.of("error", "warning")) ? 1 : -2,
321+ (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false,
322+ (o,p,q) -> null == o
323+ );
324+ // done with connection c2
325+ }
326+
254327 /*
255328 * Also confirm that the generated undeploy actions work.
256329 */
@@ -266,6 +339,148 @@ script: |
266339 (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false,
267340 (o,p,q) -> null == o
268341 );
342+
343+ /*
344+ * Get another new connection and make sure the extension can be
345+ * loaded in a non-superuser session.
346+ */
347+ try ( Connection c2 = n1.connect() )
348+ {
349+ succeeding &= stateMachine(
350+ "become non-superuser",
351+ null,
352+
353+ q(c2,
354+ "CREATE ROLE alice;" +
355+ "GRANT USAGE ON SCHEMA sqlj TO alice;" +
356+ "SET SESSION AUTHORIZATION alice")
357+ .flatMap(Node::semiFlattenDiagnostics)
358+ .peek(Node::peek),
359+
360+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
361+ (o,p,q) -> null == o
362+ );
363+
364+ succeeding &= stateMachine(
365+ "load as non-superuser",
366+ null,
367+
368+ q(c2, "SELECT null::pg_catalog.void" +
369+ " FROM sqlj.get_classpath('public')")
370+ .flatMap(Node::semiFlattenDiagnostics)
371+ .peek(Node::peek),
372+
373+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
374+ (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false,
375+ (o,p,q) -> null == o
376+ );
377+ // done with connection c2 again
378+ }
379+
380+ /*
381+ * Make sure the extension drops cleanly and nothing
382+ * is left in sqlj.
383+ */
384+ succeeding &= stateMachine(
385+ "drop extension and schema no result",
386+ null,
387+
388+ q(c, "DROP EXTENSION pljava;DROP SCHEMA sqlj")
389+ .flatMap(Node::semiFlattenDiagnostics)
390+ .peek(Node::peek),
391+
392+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
393+ (o,p,q) -> null == o
394+ );
395+ }
396+
397+ /*
398+ * Get another new connection and confirm that the old, pre-extension,
399+ * LOAD method of installing PL/Java works. It is largely obsolete in
400+ * the era of extensions, but still covers the use case of installing
401+ * PL/Java without admin access on the server filesystem to where
402+ * CREATE EXTENSION requires the files to be; they can still be
403+ * installed in some other writable location the server can read, and
404+ * pljava.module_path set to the right locations of the jars, and the
405+ * correct shared-object path given to LOAD.
406+ *
407+ * Also test the after-the-fact packaging up with CREATE EXTENSION
408+ * FROM unpackaged. That officially goes away in PG 13, where the
409+ * equivalent sequence
410+ * CREATE EXTENSION pljava VERSION unpackaged
411+ * \c
412+ * ALTER EXTENSION pljava UPDATE
413+ * should be tested instead.
414+ */
415+ try ( Connection c = n1.connect() )
416+ {
417+ int majorVersion = c.getMetaData().getDatabaseMajorVersion();
418+
419+ succeeding &= stateMachine(
420+ "load as non-extension",
421+ null,
422+
423+ Node.loadPLJava(c)
424+ .flatMap(Node::semiFlattenDiagnostics)
425+ .peek(Node::peek),
426+
427+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
428+ (o,p,q) -> null == o
429+ );
430+
431+ if ( 13 <= majorVersion )
432+ {
433+ succeeding &= stateMachine(
434+ "create unpackaged (PG >= 13)",
435+ null,
436+
437+ q(c, "CREATE EXTENSION pljava VERSION unpackaged")
438+ .flatMap(Node::semiFlattenDiagnostics)
439+ .peek(Node::peek),
440+
441+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
442+ (o,p,q) -> null == o
443+ );
444+ }
445+ }
446+
447+ /*
448+ * CREATE EXTENSION FROM unpackaged (or the second half of the
449+ * PG >= 13 CREATE EXTENSION VERSION unpackaged;ALTER EXTENSION UPDATE
450+ * sequence) has to happen over a new connection.
451+ */
452+ try ( Connection c = n1.connect() )
453+ {
454+ int majorVersion = c.getMetaData().getDatabaseMajorVersion();
455+
456+ succeeding &= stateMachine(
457+ "package after loading",
458+ null,
459+
460+ q(c, 13 > majorVersion
461+ ? "CREATE EXTENSION pljava FROM unpackaged"
462+ : "ALTER EXTENSION pljava UPDATE")
463+ .flatMap(Node::semiFlattenDiagnostics)
464+ .peek(Node::peek),
465+
466+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
467+ (o,p,q) -> null == o
468+ );
469+
470+ /*
471+ * Again make sure extension drops cleanly with nothing left behind.
472+ */
473+ succeeding &= stateMachine(
474+ "drop extension and schema no result",
475+ null,
476+
477+ q(c, "DROP EXTENSION pljava;DROP SCHEMA sqlj")
478+ .flatMap(Node::semiFlattenDiagnostics)
479+ .peek(Node::peek),
480+
481+ (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2,
482+ (o,p,q) -> null == o
483+ );
269484 }
270485 } catch ( Throwable t )
271486 {
0 commit comments