diff --git a/pljava-api/src/main/java/org/postgresql/pljava/annotation/SQLAction.java b/pljava-api/src/main/java/org/postgresql/pljava/annotation/SQLAction.java index face77719..a1ff47377 100644 --- a/pljava-api/src/main/java/org/postgresql/pljava/annotation/SQLAction.java +++ b/pljava-api/src/main/java/org/postgresql/pljava/annotation/SQLAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below. + * Copyright (c) 2004-2025 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -22,14 +22,33 @@ /** * Annotation that supplies verbatim commands to be copied into the * deployment descriptor. - * - * Strings supplied within a single SQLAction annotation will be copied - * in the order supplied. Strings from different SQLAction annotations, and - * generated code for functions, will be assembled in an order that can be - * influenced by 'provides' and 'requires' labels. No snippet X will be - * emitted ahead of any snippets that provide what X requires. The "remove" - * actions will be assembled in the reverse of that order. - * + *

+ * Strings supplied to {@link #install install} or {@link #remove remove} within + * a single {@code SQLAction} annotation become code snippets emitted into the + * deployment descriptor's {@code INSTALL} or {@code REMOVE} section, + * respectively, in the order supplied. + *

+ * Snippets from different {@code SQLAction} annotations, + * and snippets generated by annotations on functions, types, and such, will be + * assembled in an order that can be influenced by {@link #provides provides} + * and {@link #requires requires} labels. No snippet X will be emitted as an + * {@code INSTALL} action ahead of any snippets that provide what X requires. + * The sense of that dependency is reversed when ordering {@code REMOVE} + * snippets. + *

Conditional execution

+ *

+ * An {@code SQLAction} may supply an {@code install} snippet that tests some + * condition at the time of deployment and adjusts the + * {@code pljava.implementors} setting to include or not include a specific + * {@code }, controlling whether actions later in + * the deployment descriptor that are annotated with that + * {@code } will be executed. The {@code SQLAction} that + * controls whether an {@code } will be recognized should use + * {@link #provides provides} with exactly that name, which is implicitly + * 'required' by statements that use that name as + * {@link #implementor implementor}. For details on this usage, which involves + * a different ordering rule, see "conditional execution" in + * {@link org.postgresql.pljava.annotation the package documentation}. * @author Thomas Hallgren - pre-Java6 version * @author Chapman Flack (Purdue Mathematics) - updated to Java6, * added SQLAction @@ -58,6 +77,10 @@ * generated in such an order that other objects that 'require' labels * 'provided' by this come later in the output for install actions, and * earlier for remove actions. + *

+ * For use of this element on an {@code SQLAction} that tests a condition + * to control conditional execution, see "conditional execution" in + * {@link SQLAction the class description}. */ String[] provides() default {}; diff --git a/pljava-api/src/main/java/org/postgresql/pljava/annotation/package-info.java b/pljava-api/src/main/java/org/postgresql/pljava/annotation/package-info.java index ca5af21cf..932113bdd 100644 --- a/pljava-api/src/main/java/org/postgresql/pljava/annotation/package-info.java +++ b/pljava-api/src/main/java/org/postgresql/pljava/annotation/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023 Tada AB and other contributors, as listed below. + * Copyright (c) 2015-2025 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -13,6 +13,7 @@ /** * Annotations for use in Java code to generate the SQLJ Deployment Descriptor * automatically. + *

Eliminating error-prone hand-maintained SQL scripts

*

* To define functions or types in PL/Java requires more than one step. The * Java code must be written, compiled to a jar, and made available to the @@ -22,14 +23,14 @@ * version that undoes it when uninstalling the jar) can be written in a * prescribed form and stored inside the jar itself as an "SQLJ Deployment * Descriptor", and processed automatically when the jar is installed in or - * removed from the backend. + * removed from the DBMS. *

* To write the deployment descriptor by hand can be tedious and error-prone, * as it must largely duplicate the method and type declarations in the * Java code, but using SQL's syntax and types in place of Java's. Instead, * when the annotations in this package are used in the Java code, the Java - * compiler itself will generate a deployment descriptor file, ready to include - * with the compiled classes to make a complete SQLJ jar. + * compiler itself will generate a deployment descriptor (DDR) file, ready to + * include with the compiled classes to make a complete SQLJ jar. *

* Automatic descriptor generation requires attention to a few things. *

+ *

New compiler options when generating the deployment descriptor

+ *

Additional options are available when invoking the Java compiler, and + * can be specified with {@code -Aoption=value} on the command line: *

- *
ddr.output + *
{@code ddr.output} *
The file name to be used for the generated deployment descriptor. * If not specified, the file will be named pljava.ddr and found * in the top directory of the tree where the compiled class files are written. - *
ddr.name.trusted + *
{@code ddr.name.trusted} *
The language name that will be used to declare methods that are * annotated to have {@link org.postgresql.pljava.annotation.Function.Trust#SANDBOXED} behavior. If not - * specified, the name java will be used. It must match the name + * specified, the name {@code java} will be used. It must match the name * used for the "trusted" language declaration when PL/Java was installed. - *
ddr.name.untrusted + *
{@code ddr.name.untrusted} *
The language name that will be used to declare methods that are * annotated to have {@link org.postgresql.pljava.annotation.Function.Trust#UNSANDBOXED} behavior. If not - * specified, the name javaU will be used. It must match the name + * specified, the name {@code javaU} will be used. It must match the name * used for the "untrusted" language declaration when PL/Java was installed. - *
ddr.implementor + *
{@code ddr.implementor} *
The identifier (defaulting to {@code PostgreSQL} if not specified here) * that will be used in the {@code }s wrapping any SQL * generated from elements that do not specify their own. If this is set to a * single hyphen (-), elements that specify no implementor will produce plain * {@code }s not wrapped in {@code }s. - *
ddr.reproducible + *
{@code ddr.reproducible} *
When {@code true} (the default), SQL statements are written to the * deployment descriptor in an order meant to be consistent across successive * compilations of the same sources. This option is further discussed below. *
- *
  • The deployment descriptor may contain statements that cannot succeed if + *

    Controlling order of statements in the deployment descriptor

    + *

    The deployment descriptor may contain statements that cannot succeed if * placed in the wrong order, and to keep a manually-edited script in a workable * order while adding and modifying code can be difficult. Most of the * annotations in this package accept arbitrary {@code requires} and @@ -80,12 +91,13 @@ * compiler, except that it will make sure not to write anything that * {@code requires} some string X into the generated script * before whatever {@code provides} it. - *

  • There can be multiple ways to order the statements in the deployment + *

    Effect of {@code ddr.reproducible}

    + *

    There can be multiple ways to order the statements in the deployment * descriptor to satisfy the given {@code provides} and {@code requires} * relationships. While the compiler will always write the descriptor in an * order that satisfies those relationships, when the {@code ddr.reproducible} * option is {@code false}, the precise order may differ between successive - * compilations of the same sources, which should not affect successful + * compilations of the same sources, which should not affect successful * loading and unloading of the jar with {@code install_jar} and * {@code remove_jar}. In testing, this can help to confirm that all of the * needed {@code provides} and {@code requires} relationships have been @@ -94,6 +106,74 @@ * orders, chosen arbitrarily but consistently between multiple compilations as * long as the sources are unchanged. This can be helpful in software * distribution when reproducible output is wanted. + *

    Conditional execution in the deployment descriptor

    + *

    The deployment-descriptor syntax fixed by the ISO SQL/JRT standard has + * a rudimentary conditional-inclusion feature based on + * {@code }s. + * SQL statements wrapped in {@code BEGIN}/{@code END} with an + * {@code } are executed only if that name is recognized + * by the DBMS when installing or removing the jar. Statements in the deployment + * descriptor that are not wrapped in an {@code } are + * executed unconditionally. + *

    PL/Java's descriptor generator normally emits statements + * as {@code }s, using the name {@code PostgreSQL} + * (or the value of the {@code ddr.implementor} option if present on + * the compiler command line) by default, or a specific name supplied + * with {@code implementor=} to one of the annotations in this package. + *

    When loading or unloading a jar file and processing its deployment + * descriptor, PL/Java 'recognizes' any implementor name listed in the runtime + * setting {@code pljava.implementors}, which contains only {@code PostgreSQL} + * by default. + *

    The {@code pljava.implementors} setting can be changed, even by SQL + * statements within a deployment descriptor, to affect which subsequent + * statements will be executed. An SQL statement may test some condition and + * set {@code pljava.implementors} accordingly. In PL/Java's supplied examples, + * ConditionalDDR illustrates this approach to conditional execution. + *

    Naturally, this scheme requires the SQL generator to emit the statement + * that tests the condition earlier in the deployment descriptor than + * the statements relying on the {@code } being set. + * Building on the existing ability to control the order of statements + * using {@code provides} and {@code requires} elements, an {@code implementor} + * element specified in the annotation for a statement is treated also as + * an implicit {@code requires} for that name, so the programmer only needs + * to place an explicit {@code provides} element on whatever + * {@link SQLAction SQLAction} tests the condition and determines if the name + * will be recognized. + *

    The {@code provides}/{@code requires} relationship so created differs + * in three ways from other {@code provides}/{@code requires} relationships: + *

    + *

    Matching {@code implementor} and {@code provides}

    + *

    Given the 'weak' nature of the {@code implementor}/{@code provides} + * relationship, an error will not be reported if a spelling or upper/lower case + * difference prevents identifying an {@code } with the + * {@code provides} string of an annotated statement intended to match it. + * The resulting deployment descriptor may have a workable order + * as a result of the fallback ordering rules, or may have a mysteriously + * unworkable order, particularly of the {@code remove} actions. + *

    According to the ISO SQL/JRT standard, an {@code } is + * an SQL identifier, having a case-insensitive matching behavior unless quoted. + * PL/Java, however, treats a {@code provides} value as an arbitrary Java string + * that can only match exactly, and so PL/Java's SQL generator will successfully + * match up {@code implementor} and {@code provides} strings only when + * they are identical in spelling and case. */ package org.postgresql.pljava.annotation; diff --git a/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java b/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java index 262ec227d..2da7b0bc5 100644 --- a/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java +++ b/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2024 Tada AB and other contributors, as listed below. + * Copyright (c) 2004-2025 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -6074,7 +6074,7 @@ interface Snippet */ default DependTag implementorTag() { - return new DependTag.Explicit(implementorName().pgFolded()); + return new DependTag.Explicit(implementorName().nonFolded()); } /** * Return an array of SQL commands (one complete command to a string) to diff --git a/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java index 21375c909..26fabe830 100644 --- a/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java +++ b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/ConditionalDDR.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023 Tada AB and other contributors, as listed below. + * Copyright (c) 2015-2025 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -24,38 +24,51 @@ * that are not tagged with an implementor name). The default setting of * {@code pljava.implementors} is simply {@code postgresql}. *

    - * In this example, an SQLAction (with the default implementor name PostgreSQL - * so it should always execute) tests some condition and, based on the result, - * adds {@code LifeIsGood} to the list of recognized implementor names. + * In this example, an {@code SQLAction} (with the default implementor name + * {@code PostgreSQL} so it should always execute) tests some condition and, + * based on the result, adds {@code LifeIsGood} to the list of recognized + * implementor names. *

    - * Later SQLActions with that implementor name should also be executed, while - * those with a different, unrecognized implementor should not. + * Later {@code SQLAction}s with that implementor name should also be executed, + * while those with a different, unrecognized implementor should not. *

    * That is what happens at deployment (or undeployment) time, when the * jar has been loaded into the target database and the deployment descriptor is * being processed. *

    - * The {@code provides} and {@code requires} attributes matter at + * The {@code provides} attributes matter at * compile time: they are hints to the DDR generator so it will be sure - * to write the SQLAction that tests the condition ahead of the ones that - * depend on the condition having been tested. The example illustrates that an - * SQLAction's {@code implementor} is treated as an implicit {@code requires}. - * Unlike an explicit one, it is weak: if there is nothing declared that - * {@code provides} it, that's not an error; affected SQLActions will just be - * placed as late in the generated DDR as other dependencies allow, in case - * something in the preceding actions will be setting those implementor tags. + * to write the {@code SQLAction} that tests the condition ahead of whatever + * depends on the condition having been tested. The example illustrates that + * {@code implementor} is treated also as an implicit {@code requires}. *

    - * The implicit {@code requires} derived from an {@code implementor} is also - * special in another way: it does not have its sense reversed when generating - * the "undeploy" actions of the deployment descriptor. Ordinary requirements - * do, so the dependent objects get dropped before the things they depend on. - * But the code for setting a conditional implementor tag has to be placed - * ahead of the uses of the tag, whether deploying or undeploying. + * Note: while ISO SQL/JRT specifies that an {@code } is an + * SQL identifier, which would match case-insensitively unless quoted, PL/Java + * treats {@code provides} elements as arbitrary strings that can only be + * matched with identical spelling and case. Therefore, the matching of the + * implicit {@code requires} of an {@code } and the explicit + * {@code provides} on an {@code SQLAction} depends on the {@code implementor} + * and {@code provides} values being supplied with identical spelling and case, *

    - * An {@code SQLAction} setting an implementor tag does not need to have any - * {@code remove=} actions. If it does not (the usual case), its + * The dependency created when matching {@code implementor} to {@code provides} + * differs in three ways from an explicit dependency between {@code requires} + * and {@code provides}: + *

    *

    * This example adds {@code LifeIsGood} ahead of the prior content of * {@code pljava.implementors}. Simply replacing the value would stop the @@ -64,8 +77,8 @@ * local, so it is reverted when the transaction completes. *

    * In addition to the goodness-of-life examples, this file also generates - * one or more statements setting PostgreSQL-version-based implementor tags that - * are relied on by various other examples in this directory. + * one or more statements setting PostgreSQL-version-based implementor names + * that are relied on by various other examples in this directory. */ @SQLAction(provides={"LifeIsGood","LifeIsNotGood"}, install= "SELECT CASE 42 WHEN 42 THEN " + diff --git a/src/site/markdown/use/variables.md b/src/site/markdown/use/variables.md index 87be1205a..a26055327 100644 --- a/src/site/markdown/use/variables.md +++ b/src/site/markdown/use/variables.md @@ -65,7 +65,8 @@ These PostgreSQL configuration variables can influence PL/Java's operation: only on a system recognizing that name. By default, this list contains only the entry `postgresql`. A deployment descriptor that contains commands with other implementor names can achieve a rudimentary kind of conditional - execution if earlier commands adjust this list of names. _Commas separate + execution if earlier commands adjust this list of names, as described + [here][condex]. _Commas separate elements of this list. Elements that are not regular identifiers need to be surrounded by double-quotes; prior to PostgreSQL 11, that syntax can be used directly in a `SET` command, while in 11 and after, such a value needs to be @@ -213,6 +214,7 @@ These PostgreSQL configuration variables can influence PL/Java's operation: [vmop]: ../install/vmoptions.html [sqlascii]: charsets.html#Using_PLJava_with_server_encoding_SQL_ASCII [addm]: ../install/vmoptions.html#Adding_to_the_set_of_readable_modules +[condex]: ../pljava-api/apidocs/org.postgresql.pljava/org/postgresql/pljava/annotation/package-summary.html#conditional-execution-in-the-deployment-descriptor-heading [policy]: policy.html [unenforced]: unenforced.html [mappedudt]: ../pljava-api/apidocs/org.postgresql.pljava/org/postgresql/pljava/annotation/MappedUDT.html