Package org.postgresql.pljava.annotation


package org.postgresql.pljava.annotation
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 PostgreSQL server. Before the server can use the objects in the jar, the corresponding PostgreSQL declarations of functions/types/triggers/operators, and so on, must be made in SQL. This often lengthy SQL script (and the 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 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 (DDR) file, ready to include with the compiled classes to make a complete SQLJ jar.

Automatic descriptor generation requires attention to a few things.

  • The pljava-api jar must be on the Java compiler's class path. (All but the simplest PL/Java functions probably refer to some class in PL/Java's API anyway, in which case the jar would already have to be on the class path.)
  • Java compilers older than Java 23 will automatically find and use PL/Java's DDR processor as long as the pljava-api jar is on the class path. Starting in Java 23, the compiler will not do so automatically, and a -processor org.postgresql.pljava.annotation.processing.DDRProcessor option is also needed on the javac command line. (Warnings about this are issued starting in Java 21, though the processor is still used automatically, with the warnings, until Java 23.)
  • When recompiling after changing only a few sources, it is possible the Java compiler will only process a subset of the source files containing annotations. If so, it may generate an incomplete deployment descriptor, and a clean build may be required to ensure the complete descriptor is written.

New compiler options when generating the deployment descriptor

Additional options are available when invoking the Java compiler, and can be specified with -Aoption=value on the command line:

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
The language name that will be used to declare methods that are annotated to have Function.Trust.SANDBOXED behavior. If not specified, the name java will be used. It must match the name used for the "trusted" language declaration when PL/Java was installed.
ddr.name.untrusted
The language name that will be used to declare methods that are annotated to have Function.Trust.UNSANDBOXED behavior. If not specified, the name javaU will be used. It must match the name used for the "untrusted" language declaration when PL/Java was installed.
ddr.implementor
The identifier (defaulting to PostgreSQL if not specified here) that will be used in the <implementor block>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 <SQL statement>s not wrapped in <implementor block>s.
ddr.reproducible
When 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.

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 requires and provides strings, which can be used to control the order of statements in the generated descriptor. The strings given for requires and provides have no meaning to the compiler, except that it will make sure not to write anything that requires some string X into the generated script before whatever provides it.

Effect of ddr.reproducible

There can be multiple ways to order the statements in the deployment descriptor to satisfy the given provides and requires relationships. While the compiler will always write the descriptor in an order that satisfies those relationships, when the ddr.reproducible option is false, the precise order may differ between successive compilations of the same sources, which should not affect successful loading and unloading of the jar with install_jar and remove_jar. In testing, this can help to confirm that all of the needed provides and requires relationships have been declared. When the ddr.reproducible option is true, the order of statements in the deployment descriptor will be one of the possible 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 <implementor block>s. SQL statements wrapped in BEGIN/END with an <implementor name> 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 <implementor block> are executed unconditionally.

PL/Java's descriptor generator normally emits statements as <implementor block>s, using the name PostgreSQL (or the value of the ddr.implementor option if present on the compiler command line) by default, or a specific name supplied with 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 pljava.implementors, which contains only PostgreSQL by default.

The 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 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 <implementor name> being set. Building on the existing ability to control the order of statements using provides and requires elements, an implementor element specified in the annotation for a statement is treated also as an implicit requires for that name, so the programmer only needs to place an explicit provides element on whatever SQLAction tests the condition and determines if the name will be recognized.

The provides/requires relationship so created differs in three ways from other provides/requires relationships:

  • It does not reverse for generating 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 remove action (the usual case), its 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 requires for an implementor name is not satisfied by any annotation's 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 implementor and provides

Given the 'weak' nature of the implementor/provides relationship, an error will not be reported if a spelling or upper/lower case difference prevents identifying an <implementor name> with the 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 remove actions.

According to the ISO SQL/JRT standard, an <implementor name> is an SQL identifier, having a case-insensitive matching behavior unless quoted. PL/Java, however, treats a provides value as an arbitrary Java string that can only match exactly, and so PL/Java's SQL generator will successfully match up implementor and provides strings only when they are identical in spelling and case.

  • Class
    Description
    Declares a PostgreSQL aggregate.
    Declares the effect of the finish function in a Plan.
    Specifies one "plan" for evaluating the aggregate; one must always be specified (as plan), and a second may be specified (as movingPlan).
    Annotation on a PL/Java class to form a User Defined Type that will become a new PostgreSQL base type.
    The supported alignment constraints for the type's internal representation.
    The type categories that are predefined in PostgreSQL.
    Character constants corresponding to the predefined categories, for use in the BaseUDT.category() annotation element.
    The supported TOAST strategies for the type's stored representation.
    Declares a PostgreSQL CAST.
    When this cast can be applied: only in explicit form, when used in assignment context, or implicitly whenever needed.
    A known conversion path when a dedicated function is not supplied: BINARY for two types that are known to have the same internal representation, or INOUT to invoke the first type's text-output function followed by the second type's text-input function.
    Annotates a Java method for which an SQL function declaration should be generated into the deployment descriptor file.
    The volatility category describing the presence or absence of side-effects constraining what the optimizer can safely do with the function.
    Whether the function is called even for null input, or known to return null in that case and therefore not called.
    Whether the function is unsafe to use in any parallel query plan at all, or avoids certain operations and can appear in such a plan but must be executed only in the parallel group leader, or avoids an even larger set of operations and is safe to execute anywhere in a parallel plan.
    Whether the function executes with the same identity and permissions as the role that has invoked it (the usual case), or with the rights of the role that defined it (such as to offer controlled access to data the invoker would otherwise have no access to).
    Whether the function only needs limited capabilities and can run in the "trusted" language sandbox, or has to be unsandboxed and run in an "untrusted" language instance.
    Annotation on a PL/Java class that will either map an existing PostgreSQL type (provided its internal storage form is well understood), or create and map a new PostgreSQL composite type (a/k/a "structured type" in the standards).
    Declares a PostgreSQL OPERATOR.
    Names of several functions predefined in PostgreSQL for estimating the selectivity of operators in restriction clauses or joins.
    Annotation that supplies verbatim commands to be copied into the deployment descriptor.
    Container for multiple SQLAction annotations (in case it is convenient to hang more than one on a given program element).
    Optionally annotates a Java method parameter, to supply an explicit SQL type for use in the SQL function declaration in place of the automatically mapped type, and/or to supply an SQL default value.
    Annotation, only used in @Function(triggers=...), to specify what trigger(s) the function will be called for.
    Whether the trigger is invoked before or after the specified event.
    Deferrability (only applies to constraint triggers).
    Types of event that can occasion a trigger.
    Whether the trigger will occur only once for a statement of interest, or once for each row affected by the statement.