Package org.postgresql.pljava.annotation
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 thejavac
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 namejava
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 namejavaU
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), itsinstall
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'sprovides
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.
-
ClassDescriptionDeclares a PostgreSQL aggregate.Declares the effect of the
finish
function in aPlan
.Specifies one "plan" for evaluating the aggregate; one must always be specified (asplan
), and a second may be specified (asmovingPlan
).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 theBaseUDT.category()
annotation element.The supported TOAST strategies for the type's stored representation.Declares a PostgreSQLCAST
.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, orINOUT
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 PostgreSQLOPERATOR
.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 multipleSQLAction
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.