From 7d93594fe325cb134b751f110e31b629c475a5d7 Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Sun, 9 Mar 2025 22:13:51 -0400 Subject: [PATCH 1/2] Match implementor non-case-folded form to provider The DDRProcessor creates an implied dependency of an 'implementor' name on the SQLAction carrying a matching 'provides' string. An implementor name, per ISO SQL/JRT, is an SQL identifier, case-insensitive unless quoted. But a PL/Java 'provides' string is just a string, with only case-sensitive exact matching. The DDRProcessor was using a surprising and undocumented rule, matching only if the provides string was identical to the implementor name's lower-case-folded form. The results could be especially puzzling because the 'weak' nature of the implementor/provides dependency meant there was no error or warning of the failue to match. The dependency simply wasn't created, leading possibly to an incorrectly-ordered deployment descriptor. Rather than some more general and complicated solution, simply change the rule so the provider string is matched to the non-folded implementor name as it was spelled in the source annotation. Thus as long as the programmer does the expected thing and spells them the same in the source, the right thing happens. Addresses #515. --- .../postgresql/pljava/annotation/processing/DDRProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From fc16b49cd4cf02d8205988df263618dd73d5c0d8 Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Thu, 13 Mar 2025 10:12:24 -0400 Subject: [PATCH 2/2] Improve documentation of conditional execution This had never been well documented. The javadoc of the ConditionalDDR example code was about the best there was. --- .../pljava/annotation/SQLAction.java | 41 +++++-- .../pljava/annotation/package-info.java | 112 +++++++++++++++--- .../example/annotation/ConditionalDDR.java | 61 ++++++---- src/site/markdown/use/variables.md | 4 +- 4 files changed, 168 insertions(+), 50 deletions(-) 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: + *

      + *
    • It does not reverse for generating {@code remove} actions. + * Normal dependencies must be reversed for that case, so dependent objects + * are removed before those they depend on. By contrast, a condition determining + * the setting of an implementor name must be evaluated before the name + * is needed, whether the jar is being installed or removed. + *
    • If it does not have an explicit {@code remove} action (the usual case), + * its {@code install} action (the condition test and setting of the name) + * is used both when installing and removing. + *
    • It is weak. The SQL generator does not flag an error if the implicit + * {@code requires} for an implementor name is not satisfied by any annotation's + * {@code provides} in the visible Java sources. It is possible the name may be + * set some other way in the DBMS environment where the jar is to be deployed. + * Faced with statements that require such 'unprovided' implementor names, + * the SQL generator just falls back to emitting them as late in the deployment + * descriptor as possible, after all other statements that do not depend + * on them. *
    + *

    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-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}: + *

      + *
    • It is weak: if there is nothing declared that {@code provides} it, + * that's not an error; affected {@code }s 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 names. + *
    • It does not have its sense reversed when generating + * the {@code REMOVE} 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 name has to be placed + * ahead of the uses of the name, whether deploying or undeploying. + *
    • An {@code SQLAction} setting an implementor name does not need to have + * any {@code remove=} actions. If it does not (the usual case), its * {@code install=} actions will be used in both sections of the deployment * descriptor. + *
    *

    * 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