pljava-1.4.3/0000755000014500000120000000000011634451404012123 5ustar johannstaffpljava-1.4.3/Makefile0000644000014500000120000000746411634451404013576 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren # # Top level Makefile for PLJava # # To compile a PLJava for PostgreSQL 8.x the makefile system will utilize # the PostgreSQL pgxs system. The only prerequisite for such a compile is # that a PostgreSQL 8.x is installed on the system and that the PATH is set # so that the binaries of this installed can be executed. # # The following options are recognized (aside from normal options like # CFLAGS etc.) # # PGSQLDIR= For old style (not pgxs based) compilation # USE_GCJ=1 Builds a shared object file containing both # C and Java code. Requires GCJ 3.4 or later. # #------------------------------------------------------------------------- # Can't use $(shell pwd) directly since /bin/pwd doesn't handle mounted # volumes very well on some systems. The /mnt/hgfs that enables host shares # in a vmware client is one example of this. # export PROJDIR := $(shell $(SHELL) -c pwd) export PG_CONFIG ?= pg_config export PGXS ?= $(shell $(PG_CONFIG) --pgxs) export PGSQLSRC := $(dir $(PGXS)).. export top_builddir := $(PGSQLSRC)/.. export TARGETDIR := $(PROJDIR)/build export OBJDIR := $(TARGETDIR)/objs export JNIDIR := $(TARGETDIR)/jni export CLASSDIR := $(TARGETDIR)/classes export PLJAVA_MAJOR_VER := 1 export PLJAVA_MINOR_VER := 4 export PLJAVA_PATCH_VER := 3 export PLJAVA_VERSION := $(PLJAVA_MAJOR_VER).$(PLJAVA_MINOR_VER).$(PLJAVA_PATCH_VER) export TAR := /bin/tar OS := $(shell uname -s) MACHINE := $(shell uname -m) ifndef USE_GCJ JAVA_VERSION_MSG=PL/Java ${PLJAVA_VERSION} can only be compiled with JDK 1.4 or 1.5 JAVA_VERSION := $(or $(filter 1.4% 1.5%, $(shell java -version 2>&1)),$(error ${JAVA_VERSION_MSG})) endif .PHONY: all clean docs javadoc source_tarball maven_bundle install uninstall depend release \ c_all c_install c_uninstall c_depend \ pljava_all pljava_javadoc \ deploy_all deploy_javadoc \ examples_all examples_javadoc \ test_all test_javadoc all: pljava_all deploy_all c_all examples_all install: c_install uninstall: c_uninstall depend: c_depend docs: @-mkdir -p $(TARGETDIR) @find docs \( \ -name CVS \ -o -name .cvsignore \ \) -prune -o \( -type f -exec cp --parents {} $(TARGETDIR) \; \) @cp COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: pljava_javadoc deploy_javadoc examples_javadoc clean: @-rm -rf $(TARGETDIR) pljava_all pljava_javadoc: pljava_%: @-mkdir -p $(CLASSDIR)/pljava @$(MAKE) -r -C $(CLASSDIR)/pljava -f $(PROJDIR)/src/java/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* deploy_all deploy_javadoc: deploy_%: @-mkdir -p $(CLASSDIR)/deploy @$(MAKE) -r -C $(CLASSDIR)/deploy -f $(PROJDIR)/src/java/deploy/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* examples_all: examples_%: pljava_all @-mkdir -p $(CLASSDIR)/examples @$(MAKE) -r -C $(CLASSDIR)/examples -f $(PROJDIR)/src/java/examples/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* test_all: test_%: @-mkdir -p $(CLASSDIR)/test @$(MAKE) -r -C $(CLASSDIR)/test -f $(PROJDIR)/src/java/test/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* c_all c_install c_uninstall c_depend: c_%: @-mkdir -p $(OBJDIR) @$(MAKE) -r -C $(OBJDIR) -f $(PROJDIR)/src/C/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/C build_$* source_tarball: @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ release: all docs javadoc @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ maven_bundle: pljava_all @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ pljava-1.4.3/.#Makefile.1.420000644000014500000120000000747611634451404014305 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren # # Top level Makefile for PLJava # # To compile a PLJava for PostgreSQL 8.x the makefile system will utilize # the PostgreSQL pgxs system. The only prerequisite for such a compile is # that a PostgreSQL 8.x is installed on the system and that the PATH is set # so that the binaries of this installed can be executed. # # The following options are recognized (aside from normal options like # CFLAGS etc.) # # PGSQLDIR= For old style (not pgxs based) compilation # USE_GCJ=1 Builds a shared object file containing both # C and Java code. Requires GCJ 3.4 or later. # #------------------------------------------------------------------------- # Can't use $(shell pwd) directly since /bin/pwd doesn't handle mounted # volumes very well on some systems. The /mnt/hgfs that enables host shares # in a vmware client is one example of this. # export PROJDIR := $(shell $(SHELL) -c pwd) export PG_CONFIG = pg_config export PGXS := $(shell $(PG_CONFIG) --pgxs) export PGSQLSRC := $(dir $(PGXS)).. export top_builddir := $(PGSQLSRC)/.. export TARGETDIR := $(PROJDIR)/build export OBJDIR := $(TARGETDIR)/objs export JNIDIR := $(TARGETDIR)/jni export CLASSDIR := $(TARGETDIR)/classes export PLJAVA_MAJOR_VER := 1 export PLJAVA_MINOR_VER := 4 export PLJAVA_PATCH_VER := 2 export PLJAVA_VERSION := $(PLJAVA_MAJOR_VER).$(PLJAVA_MINOR_VER).$(PLJAVA_PATCH_VER) export TAR := /bin/tar OS := $(shell uname -s) MACHINE := $(shell uname -m) ifndef USE_GCJ JAVA_VERSION_MSG=PL/Java ${PLJAVA_VERSION} can only be compiled with JDK 1.4 or 1.5 JAVA_VERSION := $(or $(filter 1.4% 1.5%, $(shell java -version 2>&1)),$(error ${JAVA_VERSION_MSG})) endif .PHONY: all clean docs javadoc source_tarball maven_bundle install uninstall depend release \ c_all c_install c_uninstall c_depend \ pljava_all pljava_javadoc \ deploy_all deploy_javadoc \ examples_all examples_javadoc \ test_all test_javadoc all: pljava_all deploy_all c_all examples_all install: c_install uninstall: c_uninstall depend: c_depend docs: @-mkdir -p $(TARGETDIR) @find docs \( \ -name CVS \ -o -name .cvsignore \ \) -prune -o \( -type f -exec cp --parents {} $(TARGETDIR) \; \) @cp COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: pljava_javadoc deploy_javadoc examples_javadoc clean: @-rm -rf $(TARGETDIR) pljava_all pljava_javadoc: pljava_%: @-mkdir -p $(CLASSDIR)/pljava @$(MAKE) -r -C $(CLASSDIR)/pljava -f $(PROJDIR)/src/java/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* deploy_all deploy_javadoc: deploy_%: @-mkdir -p $(CLASSDIR)/deploy @$(MAKE) -r -C $(CLASSDIR)/deploy -f $(PROJDIR)/src/java/deploy/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* examples_all: examples_%: pljava_all @-mkdir -p $(CLASSDIR)/examples @$(MAKE) -r -C $(CLASSDIR)/examples -f $(PROJDIR)/src/java/examples/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* test_all: test_%: @-mkdir -p $(CLASSDIR)/test @$(MAKE) -r -C $(CLASSDIR)/test -f $(PROJDIR)/src/java/test/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* c_all c_install c_uninstall c_depend: c_%: pljava_all @-mkdir -p $(OBJDIR) @$(MAKE) -r -C $(OBJDIR) -f $(PROJDIR)/src/C/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/C build_$* source_tarball: @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ release: all docs javadoc @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ maven_bundle: pljava_all @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ pljava-1.4.3/.classpath0000644000014500000120000000121311634451404014103 0ustar johannstaff pljava-1.4.3/docs/0000755000014500000120000000000011634451404013053 5ustar johannstaffpljava-1.4.3/docs/readme.html0000644000014500000120000002201311634451404015174 0ustar johannstaff PL/Java readme

PL/Java, 1.2.x

Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries.

A source tarball and four of binary builds can be found in the download area. Two for Windows and two for i386 based Linux. Although all pre-compiled binaries must run using a standard JVM, the PL/Java can also be compiled and linked using GNU GCJ. PL/Java has no pre-compiled binaries for GCJ but the make system contain what's needed to make the build easy.

Please note that all use of GCJ should be regarded as experimental. Due to limitations in the implementation of java.security in some versions of GCJ the PL/Java trusted language implementation is, in fact, not trusted. At present this applies regardless of what GCJ version since the code is conditionally compiled.

Prerequisites

Get the binary distribution of PL/Java for your platform. Unzip it into a directory of your own choice.

Postmaster configuration

Get the PostgreSQL environment up and running. You will need to modify the postgresql.conf file. In order to find the PL/Java shared object, you can do one of two things. Either you install the shared object in a directory already searched by the postmaster (such as the data directory) or you tell the postmaster where to find it using the dynamic_library_path. I.e. you have a setting similar to this:

dynamic_library_path = '$libdir:<pljava installation>'

Note that on the win32 platform you need to use a semicolon as a path separator and double backslashes (since backslash is the escape character in the postgresql.conf file) as directory separators.

In order to see the logging from the tests add the following:

log_min_messages = info

Add the following entry:

custom_variable_classes = 'pljava'

System classpath

Normally, all Java code is loaded into the database using the install_jar/replace_jar SQL functions. Most of PL/Java (those functions included) is however implemented in Java. Unless you use GCJ, where this Java code is compiled and linked with the pljava shared object module, this hen and egg problem needs to be resolved using the system classpath. Add the following entry to the postgresql.conf file:

pljava.classpath = <pljava installation>/pljava.jar

Shared object issues

Unless you use GCJ, the postmaster must be made aware of the location of the shared objects used by the Java Runtime Environment (JRE). Please note that this applies to the postmaster, i.e. the backend process, and not to the client. The client will not need these settings.

Linux/Unix

Setting the LD_LIBRARY_PATH environment will work on most Linux/Unix platforms:

export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/i386:$JAVA_HOME/jre/lib/i386/client.

Apparently, on some Linux platforms you will also need to include $JAVA_HOME/jre/lib/i386/native_threads.

An alternative to use LD_LIBRARY_PATH is to edit the /etc/ld.so.conf file and then use /sbin/ldconfig on some Linux/Unix systems.

zlib conflict

On some platforms there will be a conflict between the libzip.so included in the JRE and the libz.so used by PostgreSQL (the JRE libzip.so includes a libz.so). The symptom is an InternalError in the java.util.zip.Inflater.init when an attempt is made to load the first class. You can verify the version of libzip.so using the following command:

strings libzip.so | fgrep Copyright

The problem can be resolved in one of the following ways depending on your needs and ability to recompile:

Windows

A standard install on a Windows box would be to add the following entries to you PATH environment:

set PATH=%PATH%;%JAVA_HOME%\jre\bin;%JAVA_HOME%\jre\bin\client

You are now ready to start the postmaster.

Deploying the PL/Java

PL/Java adds a schema named SQLJ to the database (the naming is from the proposed SQL standard for Java backend mapping) and adds a couple of tables and functions to that schema. The deployment can be done in one of two ways. The simplest way is probably to just execute the file install.sql as a super user (the uninstall.sql will remove the PL/Java installation). PL/Java also comes with deploy program that lets you install, reinstall, or uninstall PL/Java. This program will assert that you indeed are a super user and then execute the correct commands using jdbc. In order to run this program, you must see to that the PostgreSQL jdbc driver package postgresql.jar and the deploy.jar file is in your CLASSPATH, then run:

java org.postgresql.pljava.deploy.Deployer
This will result in a list of options. Typically you would use something like:
java org.postgresql.pljava.deploy.Deployer -install

That's all there's to it. You are now ready to start using the PL/Java system.

Run the example tests

The tests are divided into two jar files. One is the client part found in the test.jar. It contains some methods that executes SQL statements and prints the output (all contained there can of course also be executed from psql or any other client). The other is the examples.jar which contains the sample code that runs in the backend. The latter must be installed in the database in order to function. An easy way to do this is to use psql and issue the command:

SELECT sqlj.install_jar('file:///some/directory/examples.jar', 'samples',  true);

Please note that the deployment descriptor stored in examples.jar will attempt to create the schema javatest so the user that executes the sqlj.install_jar must have permission to do that. If this command succeeds, everything is working correctly. You may get a couple of errors here though.

Once loaded, you must also set the classpath used by the PL/Java runtime. This classpath is set per schema (namespace). A schema that lacks a classpath will default to the classpath that has been set for the public schema. The tests will use the schema javatest. To define the classpath for this schema, simply use psql and issue the command:

SELECT sqlj.set_classpath('javatest', 'samples');

The first argument is the name of the schema, the second is a colon separated list of jar names. The names must reflect jars that are installed in the system.

NOTE: If you don't use schemas, you must still issue the set_classpath command to assign a correct classpath to the 'public' schema. This can only be done by a super user.

 

Now, you should be able to run the tests:

java org.postgresql.pljava.test.Tester

Building

Building should be very stright forward:

pljava-1.4.3/docs/solutions.html0000644000014500000120000000750611634451404016010 0ustar johannstaff Some problems and their solution

Some problems and their solution.

Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries.

When writing the PL/Java, mapping the JVM into the same process-space as the PostgreSQL backend code, some concerns have been raised regarding multiple threads, exception handling, and memory management. Here is a brief text explaining how these issues where resolved.

Multi threading.

Problem

Java is inherently multi threaded. The PostgreSQL backend is not. There’s nothing stopping a developer from utilizing multiple Threads class in the Java code. Finalizers that call out to the backend might have been spawned from a background Garbage Collection thread. Several third party Java-packages that are likely to be used make use of multiple threads. How can this model coexist with the PostgreSQL backend in the same process without creating havoc?

Solution

The solution is simple. PL/Java defines a special object called the Backend.THREADLOCK. When PL/Java is initialized, the backend will immediately grab this objects monitor (i.e. it will synchronize on this object). When the backend calls a Java function, the monitor is released and then immediately regained when the call returns. All calls from Java out to backend code are synchronized on the same lock. This ensures that only one thread at a time can call the backend from Java, and only at a time when the backend is awaiting the return of a Java function call.

Exception handling

Problem

Java makes frequent use of try/catch/finally blocks. PostgreSQL sometimes use an exception mechanism that calls longjmp to transfer control to a known state. Such a jump would normally effectively bypass the JVM. Prior to PostgreSQL version 8.0, the error was propagated before the actual jump and then discarded, thus there was no way to catch and handle the error.

Solution

The backend now allows errors to be caught using the macros PG_TRY/PG_CATCH/PG_END_TRY and in the catch block, the error can be examined using the ErrorData structure. PL/Java implements a java.sql.SQLException subclass called org.postgresql.pljava.ServerException. The ErrorData can be retrieved and examined from that exception. A catch handler is allowed to issue a rollback to a savepoint. After a successful rollback, execution can continue.

Java Garbage Collector versus palloc() and stack allocation.

Problem

Primitive types will be passed by value always. This includes the String type (this is a must since Java uses double byte characters). Complex types are however often wrapped in Java objects and passed by reference. I.e, a Java object will contain a pointer to a palloc’ed or stack allocated memory and use native JNI calls to extract and manipulate data. Such data will become “stale” once a call has ended. Further attempts to access such data will at best give very unpredictable results but more likely cause a memory fault and a crash.

Solution

The PL/Java contains code that ensures that stale pointers are cleared when the MemoryContext or stack where they where allocated goes out of scope. The Java wrapper objects might live on but any attempt to use them will result in a “stale native handle” exception.

pljava-1.4.3/docs/intro.html0000644000014500000120000001304011634451404015072 0ustar johannstaff PL/Java 1.1.0
 
The project is sponsored by:

PL/Java 1.2.0 released

Bringing the power of Java to PostgreSQL Functions and Triggers.

Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries.
PostgreSQL™ is a trademark of PostgreSQL Inc and Regents of the University of California.

PL/Java is an add on module to the PostgreSQL backend. It falls into the same category as PL/SQL, PL/TCL, PL/Perl, PL/Python, and PL/R. When installed, functions and triggers can be written in Java using your favorite Java IDE and installed into the database.

The PL/Java 1.2.0 release of PL/Java provides the following features.

  1. Ability to write both functions and triggers using Java 1.4 or higher.
  2. Standardized utilities (modeled after the SQL 2003 proposal) to install and maintain Java code in the database.
  3. Standardized mappings of parameters and result. Complex types as well as sets are supported.
  4. An embedded, high performance, JDBC driver utilizing the internal PostgreSQL SPI routines.
  5. Metadata support for the JDBC driver. Both DatabaseMetaData and ResultSetMetaData is included.
  6. The ability to return a ResultSet that origins from a query as an alternative to build a ResultSet row by row
  7. Full support for PostgreSQL 8.0 savepoints and exception handling.
  8. Ability to use IN, INOUT, and OUT parameters when used with PostgreSQL 8.1
  9. Two language handlers, one TRUSTED (the default) and one that is not TRUSTED (language tag is javaU to conform with the defacto standard)
  10. Transaction and Savepoint listeners enabling code execution when a transaction or savepoint is commited or rolled back.
  11. Integration with GNU GCJ on selected platforms.

PL/Java in brief

A function or trigger in SQL will appoint a static method in a Java class. In order for the function to execute, the appointed class must be installed in the database. PL/Java adds a set of functions that helps installing and maintaining the java classes. Classes are stored in normal Java archives (AKA jars). A Jar may optionally contain a deployment descriptor that in turn contains SQL commands to be executed when the jar is deployed/undeployed. The functions are modeled after the standards proposed for SQL 2003.

PL/Java implements a standardized way of passing parameters and return values. Complex types and sets are passed using the standard JDBC ResultSet class. Great care has been taken not to introduce any proprietary interfaces unless absolutely necessary so that Java code written using PL/Java becomes as database agnostic as possible.

A JDBC driver is included in PL/Java. This driver is written directly on top of the PostgreSQL internal SPI routines. This driver is essential since it's very common for functions and triggers to reuse the database. When they do, they must use the same transactional boundaries that where used by the caller.

PL/Java is optimized for performance. The Java virtual machine executes within the same process as the backend itself. This vouches for a very low call overhead. PL/Java is designed with the objective to enable the power of Java to the database itself so that database intensive business logic can execute as close to the actual data as possible.

The standard Java Native Interface (JNI) is used when bridging calls from the backend into the Java VM and vice versa. Please read the rationale behind the choice of technology and a more in-depth discussion about some implementation details. PL/Java is primarily targeted to the new 8.1 version but will run with PostgreSQL 8.0 versions too albeit with some limitations.

For info on how to get started, please read the release notes. A user guide contains more information on how to create and manage our Java functions and triggers.

Source and selected binaries are available for download. See the download page for more details.

pljava-1.4.3/docs/jni_rationale.html0000644000014500000120000002163411634451404016565 0ustar johannstaff Rationale behind using JNI as opposed to threads in a remote JVM process

Rationale behind using JNI as opposed to threads in a remote JVM process.

Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries.

Reasons to use a high level language like Java™ in the backend

A large part of the reason why JNI was chosen in favor of an RPC based, single JVM solution was due to the expected use-cases. Enterprise systems today are almost always 3-tier or n-tier. Database functions, triggers, and stored procedures are mechanisms that extend the functionality of the backend tier. They typically rely on a tight integration with the database due to a very high rate of interactions and execute inside of the database largely to limit the number of interactions between the middle tier and the backend tier. Some typical use-cases:

One might argue that since a JVM often is present running an app-server in the middle tier, would it not be more efficient if that JVM also executed the database functions and triggers? In my opinion, this would be very bad. One major reason for moving execution down to the database is performance (by minimizing the number of roundtrips between the app-server and the database) another is separation of concern. Referential data integrity and other ways to extend the functionality of the database should not be the app-servers concern, it belongs in the backend tier. Other aspects like database versus app-server administration, replication of code and permission changes for functions, and running different tiers on different servers, makes it even worse.

Resource consumption

Having one JVM per connection instead of one thread per connection running in the same JVM will undoubtedly consume more resources. There are however a couple of facts that must be remembered:

Connection pooling

In the Java community you are very likely to use a connection pool. The pool will ensure that the number of connections stays as low as possible and that connections are reused (instead of closed and reestablished). New JVMs are started rarely.

Connection isolation

Separate JVMs gives you a much higher degree of isolation. This brings a number of advantages:

Transaction visibility

In order to maintain the correct visibility, the transaction must somehow be propagated to the Java layer. I can see two solutions for this using RPC. Either an XA aware JDBC-driver is used (requires XA support from PostgreSQL) or a JDBC driver is written so that it calls back to the SPI functions in the invoking process. Both choices results in an increased number of RPC calls and a negative performance impact.

The PL/Java approach is to use the underlying SPI interfaces directly through JNI by providing a "pseudo connection" that implements the JDBC interfaces. The mapping is thus very direct. Data need never be serialized nor duplicated.

RPC performance

Remote procedure calls are extremely expensive compared to in-process calls. Relying on an RPC mechanism for Java calls will cripple the usefulness of such an implementation a great deal. Here are two examples:

Using JNI to directly access structures like TriggerData, Relation, TupleDesc, and HeapTuple minimizes the amount of data that needs to be copied. Parameters and return values that are primitives need not even become Java objects. A 32-bit int4 Datum can be directly passed as a Java int (jint in JNI).

Simplicity

I've have some experience of work involving CORBA and other RPCs. They add a fair amount of complexity to the process. JNI however, is invisible to the user.

pljava-1.4.3/docs/userguide.html0000644000014500000120000007353611634451404015753 0ustar johannstaff PL/Java 1.2 User Guide

PL/Java 1.2 User Guide

Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries.

Table of contents

Utilities
   Deployer
 SQLJ functions
    install_jar
    replace_jar
    remove_jar
    get_classpath
    set_classpath
Writing Java functions
    Type mapping
    Returning complex types
    Functions returning sets
    Using JDBC
    Exception handling
    Savepoints
    Logging
Security
    Installation
    Trusted language
    Execution of the deployment descriptor
    Classpath manipulation
Module Configuration

Utilities

Deployer

When running the deployer, you must use a classpath that can see the deploy.jar found in the PL/Java distribution and the postgresql.jar from the PostgreSQL distribution. The former contains the code for the deployer command and the second includes the PostgreSQL JDBC driver. You then run the deployer with the command:

java -cp <your classpath> org.postgresql.pljava.deploy.Deployer [ options ]

It's recommended that create a shell script or a .bat script that does this for you so that you don't have to do this over and over again.

Deployer options

-install Installs the Java™ language along with the sqlj procedures. The deployer will fail if the language is installed already.
-reinstall Reinstalls the Java™ language and the sqlj procedures. This will effectively drop all jar files that have been loaded.
-remove Drops the Java™ language and the sqlj procedures and loaded jars.
-user <user name> Name of user that connects to the database. Default is the current user.
-password <password> Password of user that connects to the database. Default is no password.
-database <database> The name of the database to connect to. Default is to use the user name.
-host <host name> Name of the host. Default is "localhost".
-port <port number> Port number. Default is blank.
-cygwin Use this option if the host runs on a Cygwin based windows platform. Affects the name used for the PL/Java dynamic library.

NOTE This option should not be used when running native the Win32 port.

 

Deploying using SQL

An alternative to using the deployer is to run the install.sql and uninstall.sql scripts that are included in the distribution.

SQLJ functions

Deployment descriptor

The install_jar, replace_jar, and remove_jar can act on a deployment descriptor allowing SQL commands to be executed after the jar has been installed or prior to removal. The format of the deployment descriptor is stipulated by ISO/IEC 9075-13:2003.

The descriptor is added as a normal text file to your jar file. In the Manifest of the jar there must be an entry that appoints the file as the SQLJ deployment descriptor.

Name: <deployment descriptor entry in the jar>SQLJDeploymentDescriptor: TRUE

The deployment descriptor must have the following form:

<descriptor file> ::=
SQLActions <left bracket> <rightbracket> <equal sign>
{ [ <double quote> <action group> <double quote>
  [ <comma> <double quote> <action group> <double quote> ] ] }
<action group> ::=
    <install actions>
  | <remove actions>
<install actions> ::=
  BEGIN INSTALL [ <command> <semicolon> ]... END INSTALL
<remove actions> ::=
  BEGIN REMOVE [ <command> <semicolon> ]... END REMOVE
<command> ::=
    <SQL statement>
  | <implementor block>
<SQL statement> ::= !! <SQL token>...
<implementor block> ::=
  BEGIN <implementor name> <SQL token>... END <implementor name>
<implementor name> ::= <identifier>
<SQL token> ::= !! an SQL lexical unit specified by the term "<token>" in Sub clause 5.2, "<token> and <separator>", in ISO/IEC 9075-2.

If implementor blocks are used, PL/Java will consider only those with implementor name PostgreSQL (case insensitive). Here is a small sample of a deployment descriptor:

SQLActions[] = {
  "BEGIN INSTALL
    CREATE FUNCTION javatest.java_getTimestamp()
      RETURNS timestamp
      AS 'org.postgresql.pljava.example.Parameters.getTimestamp'
      LANGUAGE java;
      END INSTALL",
  "BEGIN REMOVE
    DROP FUNCTION javatest.java_getTimestamp();
  END REMOVE"
}

install_jar

The install_jar command loads a jarfile from a location appointed by an URL into the SQLJ jar repository. It is an error if a jar with the given name already exists in the repository.

Usage

SELECT sqlj.install_jar(<jar_url>, <jar_name>, <deploy>);

Parameters

jar_url The URL that denotes the location of the jar that should be loaded.
jar_name This is the name by which this jar can be referenced once it has been loaded.
deploy True if the jar should be deployed according to a deployment descriptor, false otherwise.

replace_jar

The replace_jar will replace a loaded jar with another jar. Use this command to update already loaded files. It's an error if the jar is not found.

Usage

SELECT sqlj.replace_jar(<jar_url>, <jar_name>, <redeploy>);

Parameters

jar_url The URL that denotes the location of the jar that should be loaded.
jar_name The name of the jar to be replaced.
redeploy True if the jar should be undeployed according to the deployment descriptor of the old jar and deployed according to the deployment descriptor of the new jar, false otherwise.

remove_jar

The remove_jar will drop the jar from the jar repository. Any classpath that references this jar will be updated accordingly. It's an error if the jar is not found.

Usage

SELECT sqlj.remove_jar(<jar_name>, <undeploy>);

Parameters

jar_name The name of the jar to be removed.
undeploy True if the jar should be undeployed according to a deployment descriptor, false otherwise.

get_classpath

The get_classpath will return the classpath that has been defined for the given schema or NULL if the schema has no classpath. It's an error if the given schema does not exist.

Usage

SELECT sqlj.get_classpath(<schema>);

Parameters

schema The name of the schema.

set_classpath

The set_classpath will define a classpath for the given schema. A classpath consists of a colon separated list of jar names. It's an error if the given schema does not exist or if one or more jar names references non existent jars.

Usage

SELECT sqlj.set_classpath(<schema>, <classpath>);

Parameters

schema The name of the schema.
classpath The colon separated list of jar names.

Writing Java functions

SQL declaration

A Java function is declared with the name of a class and a static method on that class. The class will be resolved using the classpath that has been defined for the schema where the function is declared. If no classpath has been defined for that schema, the "public" schema is used. If no classpath is found there either, the class is resolved using the system classloader.

The following function can be declared to access the static method getProperty on java.lang.System class:

CREATE FUNCTION getsysprop(VARCHAR)
  RETURNS VARCHAR
  AS 'java.lang.System.getProperty'
  LANGUAGE java;
SELECT getsysprop('user.home');

Type mapping

Scalar types are mapped in a straight forward way. Here's a table of the current mappings (will be updated as more mappings are implemented).

PostgreSQL Java
bool boolean
'char' byte
int2 short
int4 int
int8 long
float4 float
float8 double
varchar java.lang.String
text java.lang.String
bytea byte[]
date java.sql.Date
time java.sql.Time (stored value treated as local time)
timetz java.sql.Time
timestamp java.sql.Timestamp (stored value treated as local time)
timestamptz java.sql.Timestamp
complex java.sql.ResultSet
setof complex java.sql.ResultSet

All other types are currently mapped to java.lang.String and will utilize the standard textin/textout routines registered for respective type.

NULL handling

The scalar types that map to Java primitives can not be passed as null values. To enable this, those types can have an alternative mapping. You enable this mapping by explicitly denoting it in the method reference.

CREATE FUNCTION trueIfEvenOrNull(integer)
  RETURNS bool
  AS 'foo.fee.Fum.trueIfEvenOrNull(java.lang.Integer)'
  LANGUAGE java;

In java, you would have something like:

package foo.fee;
public class Fum
{
  static boolean trueIfEvenOrNull(Integer value)
  {
    return (value == null)
      ? true
      : (value.intValue() % 1) == 0;
  }
}

The following two statements should both yield true:

SELECT trueIfEvenOrNull(NULL);
SELECT trueIfEvenOrNull(4);

In order to return null values from a Java method, you simply use the object type that corresponds to the primitive (i.e. you return java.lang.Integer instead of int). The PL/Java resolve mechanism will find the method regardless. Since Java cannot have different return types for methods with the same name, this does not introduce any ambiguity.

Complex types

A complex type will always be passed as a read-only java.sql.ResultSet with exaclty one row. The ResultSet will be positioned on its row so no call to next should be made. The values of the complex type are retrieved using the standard getter methods of the ResultSet.

Example:

CREATE TYPE complexTest
  AS(base integer, incbase integer, ctime timestamptz);

CREATE FUNCTION useComplexTest(complexTest)
  RETURNS VARCHAR
  AS 'foo.fee.Fum.useComplexTest'
  IMMUTABLE LANGUAGE java;

In class Fum we add the static following static method:

public static String useComplexTest(ResultSet complexTest)
throws SQLException
{
  int base = complexTest.getInt(1);
  int incbase = complexTest.getInt(2);
  Timestamp ctime = complexTest.getTimestamp(3);
  return "Base = \"" + base +
    "\", incbase = \"" + incbase +
    "\", ctime = \"" + ctime + "\"";
}

Returning complex types

Java does not stipulate any way to create a ResultSet from scratch. Hence, returning a ResultSet is not an option. The SQL-2003 draft suggest that a complex return value instead is handled as an IN/OUT parameter and PL/Java implements it that way. If you declare a function that returns a complex type, you will need to use a Java method with boolean return type with a last parameter of type java.sql.ResultSet. The parameter will be initialized to an empty updateable ResultSet that contains exactly one row.

Assume that we still have the complexTest type created above.

CREATE FUNCTION createComplexTest(int, int)
  RETURNS complexTest
  AS 'foo.fee.Fum.createComplexTest'
  IMMUTABLE LANGUAGE java;

The PL/Java method resolve will now find the following method in the Fum class:

public static boolean complexReturn(int base, int increment, ResultSet receiver)
throws SQLException
{
  receiver.updateInt(1, base);
  receiver.updateInt(2, base + increment);
  receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis()));
  return true;
}

The return value denotes if the receiver should be considered as a valid tuple (true) or NULL (false).

Functions returning sets

Returning sets is tricky. You don't want to first build a set and then return it since large sets would eat too much resources. Its far better to produce one row at a time. Incidentally, that's exactly what the PostgreSQL backend expects a function with SETOF return to do. You can return a SETOF a scalar type such as an int, float or varchar, or you can return a SETOF a complex type.

 

Returning a SETOF <scalar type>

In order to return a set of a scalar type, you need create a Java method that returns something that implements the java.util.Iterator interface. Here's an example of a method that returns a SETOF varchar:

CREATE FUNCTION javatest.getSystemProperties()
  RETURNS SETOF varchar
  AS 'foo.fee.Bar.getNames'
  IMMUTABLE LANGUAGE java;

The very rudimentary java method that returns an interator:

package foo.fee;
import java.util.Iterator;

public class Bar
{
    public static Iterator getNames()
    {
        ArrayList names = new ArrayList();
        names.add("Lisa");
        names.add("Bob");
        names.add("Bill");
        names.add("Sally");
        return names.iterator();
    }
}

Returning a SETOF <complex type>

A method returning a SETOF <complex type> must use either the interface org.postgresql.pljava.ResultSetProvider or org.postgresql.pljava.ResultSetHandle. The reason for having two interfaces is that they cater for optimal handling of two distinct use cases. The former is great when you want to dynamically create each row that is to be returned from the SETOF function. The latter makes sense when you want to return the result of an executed query.

Using the ResultSetProvider interface

This interface has two methods. The boolean assignRowValues(java.sql.ResultSet tupleBuilder, int rowNumber) and the void close() method. The PostgreSQL query evaluator will call the assignRowValues repeatedly until it returns false or until the evaluator decides that it does not need any more rows. It will then call close.

You can use this interface the following way:

CREATE FUNCTION javatest.listComplexTests(int, int)
  RETURNS SETOF complexTest
  AS 'foo.fee.Fum.listComplexTest'
  IMMUTABLE LANGUAGE java;

The function maps to a static java method that returns an instance that implements the ResultSetProvider interface.

public class Fum implements ResultSetProvider
{
  private final int m_base;
  private final int m_increment;
  public Fum(int base, int increment)
  {
    m_base = base;
    m_increment = increment;
  }
  public boolean assignRowValues(ResultSet receiver, int currentRow)
  throws SQLException
  {
    // Stop when we reach 12 rows.
    //
    if(currentRow >= 12)
      return false;
    receiver.updateInt(1, m_base);
    receiver.updateInt(2, m_base + m_increment * currentRow);
    receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis()));
    return true;
  }
  public void close()
  {
  	// Nothing needed in this example
  }
  public static ResultSetProvider listComplexTests(int base, int increment)
  throws SQLException
  {
    return new Fum(base, increment);
  }
}

The listComplextTests method is called once. It may return null if no results are available or an instance of the ResultSetProvider. Here the Fum implements this interface so it returns an instance of itself. The method assignRowValues will then be called repeatedly until it returns false. At that time, close will be called

Using the ResultSetHandle interface

This interface is similar to the ResultSetProvider interface in that it has a close() method that will be called at the end. But instead of having the evaluator call a method that builds one row at a time, this method has a method that returns a ResultSet. The query evaluator will iterate over this set and deliver it's contents, one tuple at a time, to the caller until a call to next() returns false or the evaluator decides that no more rows are needed.

Here is an example that executes a query using a statement that it obtained using the default connection. The SQL suitable for the deployment descriptor looks like this:

CREATE FUNCTION javatest.listSupers()
  RETURNS SETOF pg_user
  AS 'org.postgresql.pljava.example.Users.listSupers'
  LANGUAGE java;

CREATE FUNCTION javatest.listNonSupers()
  RETURNS SETOF pg_user
  AS 'org.postgresql.pljava.example.Users.listNonSupers'
  LANGUAGE java;
And in the Java package org.postgresql.pljava.example a class Users is added:
public class Users implements ResultSetHandle
{
  private final String m_filter;
  private Statement m_statement;

  public Users(String filter)
  {
    m_filter = filter;
  }

  public ResultSet getResultSet()
  throws SQLException
  {
    m_statement = DriverManager.getConnection("jdbc:default:connection").createStatement();
    return m_statement.executeQuery("SELECT * FROM pg_user WHERE " + m_filter);
  }

  public void close()
  throws SQLException
  {
    m_statement.close();
  }

  public static ResultSetHandle listSupers()
  {
    return new Users("usesuper = true");
  }

  public static ResultSetHandle listNonSupers()
  {
    return new Users("usesuper = false");
  }
}

Triggers

The method signature of a trigger is predefined. A trigger method must always return void and have a org.postgresql.pljava.TriggerData parameter. No more, no less. The TriggerData interface provides access to two ResultSet instances; one representing the old row and one representing the new. The old row is read-only, the new row is updateable.

The sets are only available for triggers that are fired ON EACH ROW. Delete triggers have no new row, and insert triggers have no old row. Only update triggers have both.

In addition to the sets, several boolean methods exists to gain more information about the trigger. Here's an example trigger:

CREATE TABLE mdt (
  id int4,
  idesc text,
  moddate timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL);

CREATE FUNCTION moddatetime()
  RETURNS trigger
  AS 'org.postgresql.pljava.example.Triggers.moddatetime'
  LANGUAGE java";

CREATE TRIGGER mdt_moddatetime
  BEFORE UPDATE ON mdt
  FOR EACH ROW
  EXECUTE PROCEDURE moddatetime (moddate);

The Java method in class org.postgresql.pljava.example.Triggers looks like this:

/**
 * Update a modification time when the row is updated.
 */
static void moddatetime(TriggerData td)
throws SQLException
{
  if(td.isFiredForStatement())
    throw new TriggerException(td, "can't process STATEMENT events");

  if(td.isFiredAfter())
    throw new TriggerException(td, "must be fired before event");

  if(!td.isFiredByUpdate())
    throw new TriggerException(td, "can only process UPDATE events");

  ResultSet _new = td.getNew();
  String[] args = td.getArguments();
  if(args.length != 1)
    throw new TriggerException(td, "one argument was expected");

  _new.updateTimestamp(args[0], new Timestamp(System.currentTimeMillis()));
}

Using JDBC

PL/Java contains a JDBC driver that maps to the PostgreSQL SPI functions. A connection that maps to the current transaction can be obtained using the following statement:

Connection conn = DriverManager.getConnection("jdbc:default:connection"); 

From there on, you can prepare and execute statements, just like with any other JDBC connection. There are a couple of limitations though:

Exception handling

You can catch and handle an exception in the Postgres backend just like any other exceptoin. The backend ErrorData structure is exposed as a property in a class called org.postgresql.pljava.ServerException (derived from java.sql.SQLException) and the Java try/catch mechanism is synchronized with the backend mechanism.

Important Note:

You will not be able to continue executing backend functions until your function has returned and the error has been propagated when the backend has generated an exception unless you have used a savepoint. When a savepoint is rolled back, the exceptional condition is reset and you can continue your execution.

Savepoints

PostgreSQL savepoints are exposed using the java.sql.Connection interface. Two restrictions apply.

Logging

PL/Java uses the standard Java 1.4 Logger. Hence, you can write things like:

Logger.getAnonymousLogger().info( "Time is " + new Date(System.currentTimeMillis()));

At present, the logger is hardwired to a handler that maps the current state of the PostgreSQL configuration setting log_min_messages to a valid Logger level and that outputs all messages using the backend function elog(). The following mapping apply between the Logger levels and the PostgreSQL backend levels.

java.util.logging.Level PostgreSQL level
SEVERE ERROR
WARNING WARNING
INFO INFO
FINE DEBUG1
FINER DEBUG2
FINEST DEBUG3

Security

Installation

Only a PostgreSQL super user can install PL/Java. The PL/Java utility functions are installed using SECURITY DEFINER so that they execute with the access permissions that where granted to the creator of the functions.

Trusted language

PL/Java is now a TRUSTED language. PostgreSQL stipulates that a language marked as trusted has no access to the filesystem and PL/Java enforces this. Any user can create and access functions or triggers in a trusted language. PL/Java also installs a language handler for the language "javaU". This version is not trusted and only a superuser can create new functions that use it. Any user can still call the functions.

Execution of the deployment descriptor

The install_jar, replace_jar, and remove_jar, optionally executes commands found in a SQL deployment descriptor. Such commands are executed with the permissions of the caller. In other words, although the utility function is declared with SECURITY DEFINER, it switches back to the session user during execution of the deployment descriptor commands.

Classpath manipulation

The function set_classpath requires the caller of the function has been granted CREATE permission on the affected schema.

Module Configuration.

PL/Java makes use of PostgreSQL custom variable classes in the postgresql.conf configuration file to add some configuration parameters. PL/Java introduces a custom variable class named "pljava". Here's a sample postgresql.conf entry using all (3) of the variables currently introduced:

 

# define "pljava" as a custom variable class. This is a comma separated
# list of names.
#
custom_variable_classes = 'pljava'

# define the class path that the JVM will use when loading the
# initial library. Only meaningful for non GCJ installations
#
pljava.classpath = '/home/Tada/pljava/build/pljava.jar'

# Set the size of the prepared statement MRU cache
#
pljava.statement_cache_size = 10

# If true, lingering savepoints will be released on function exit. If false,
# the will be rolled back
#
pljava.release_lingering_savepoints = true

# Define startup options for the Java VM.
#
pljava.vmoptions = '-Xmx64M'

# Setting debug to true will cause the postgres process to go
# into a sleep(1) loop on its first call to java. This variable is
# only useful if you want to debug the PL/Java internal C code.
#
pljava.debug = false
pljava-1.4.3/Makefile~0000644000014500000120000000746411634451404013774 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren # # Top level Makefile for PLJava # # To compile a PLJava for PostgreSQL 8.x the makefile system will utilize # the PostgreSQL pgxs system. The only prerequisite for such a compile is # that a PostgreSQL 8.x is installed on the system and that the PATH is set # so that the binaries of this installed can be executed. # # The following options are recognized (aside from normal options like # CFLAGS etc.) # # PGSQLDIR= For old style (not pgxs based) compilation # USE_GCJ=1 Builds a shared object file containing both # C and Java code. Requires GCJ 3.4 or later. # #------------------------------------------------------------------------- # Can't use $(shell pwd) directly since /bin/pwd doesn't handle mounted # volumes very well on some systems. The /mnt/hgfs that enables host shares # in a vmware client is one example of this. # export PROJDIR := $(shell $(SHELL) -c pwd) export PG_CONFIG ?= pg_config export PGXS ?= $(shell $(PG_CONFIG) --pgxs) export PGSQLSRC := $(dir $(PGXS)).. export top_builddir := $(PGSQLSRC)/.. export TARGETDIR := $(PROJDIR)/build export OBJDIR := $(TARGETDIR)/objs export JNIDIR := $(TARGETDIR)/jni export CLASSDIR := $(TARGETDIR)/classes export PLJAVA_MAJOR_VER := 1 export PLJAVA_MINOR_VER := 4 export PLJAVA_PATCH_VER := 2 export PLJAVA_VERSION := $(PLJAVA_MAJOR_VER).$(PLJAVA_MINOR_VER).$(PLJAVA_PATCH_VER) export TAR := /bin/tar OS := $(shell uname -s) MACHINE := $(shell uname -m) ifndef USE_GCJ JAVA_VERSION_MSG=PL/Java ${PLJAVA_VERSION} can only be compiled with JDK 1.4 or 1.5 JAVA_VERSION := $(or $(filter 1.4% 1.5%, $(shell java -version 2>&1)),$(error ${JAVA_VERSION_MSG})) endif .PHONY: all clean docs javadoc source_tarball maven_bundle install uninstall depend release \ c_all c_install c_uninstall c_depend \ pljava_all pljava_javadoc \ deploy_all deploy_javadoc \ examples_all examples_javadoc \ test_all test_javadoc all: pljava_all deploy_all c_all examples_all install: c_install uninstall: c_uninstall depend: c_depend docs: @-mkdir -p $(TARGETDIR) @find docs \( \ -name CVS \ -o -name .cvsignore \ \) -prune -o \( -type f -exec cp --parents {} $(TARGETDIR) \; \) @cp COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: pljava_javadoc deploy_javadoc examples_javadoc clean: @-rm -rf $(TARGETDIR) pljava_all pljava_javadoc: pljava_%: @-mkdir -p $(CLASSDIR)/pljava @$(MAKE) -r -C $(CLASSDIR)/pljava -f $(PROJDIR)/src/java/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* deploy_all deploy_javadoc: deploy_%: @-mkdir -p $(CLASSDIR)/deploy @$(MAKE) -r -C $(CLASSDIR)/deploy -f $(PROJDIR)/src/java/deploy/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* examples_all: examples_%: pljava_all @-mkdir -p $(CLASSDIR)/examples @$(MAKE) -r -C $(CLASSDIR)/examples -f $(PROJDIR)/src/java/examples/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* test_all: test_%: @-mkdir -p $(CLASSDIR)/test @$(MAKE) -r -C $(CLASSDIR)/test -f $(PROJDIR)/src/java/test/Makefile \ MODULEROOT=$(PROJDIR)/src/java $* c_all c_install c_uninstall c_depend: c_%: @-mkdir -p $(OBJDIR) @$(MAKE) -r -C $(OBJDIR) -f $(PROJDIR)/src/C/pljava/Makefile \ MODULEROOT=$(PROJDIR)/src/C build_$* source_tarball: @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ release: all docs javadoc @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ maven_bundle: pljava_all @-mkdir -p $(TARGETDIR)/distrib @$(MAKE) -r -C $(TARGETDIR) -f $(PROJDIR)/packaging/Makefile $@ pljava-1.4.3/src/0000755000014500000120000000000011634451404012712 5ustar johannstaffpljava-1.4.3/src/java5/0000755000014500000120000000000011634451403013717 5ustar johannstaffpljava-1.4.3/src/java5/pljava/0000755000014500000120000000000011634451403015174 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/0000755000014500000120000000000011634451403015763 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/postgresql/0000755000014500000120000000000011634451403020166 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/postgresql/pljava/0000755000014500000120000000000011634451403021443 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/0000755000014500000120000000000011634451403022734 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/MalformedTriggerException.java0000644000014500000120000000103311634451403030705 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; /** * @author Thomas Hallgren */ public class MalformedTriggerException extends RuntimeException { MalformedTriggerException(FunctionVisitor function) { super("Function " + function.getClassName() + '.' + function.getMethodName() + " is not a valid trigger function"); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/DefaultAnnotationVisitor.java0000644000014500000120000000150111634451403030573 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import org.objectweb.asm.AnnotationVisitor; /** * An AnnotationVisitor implementation that does nothing at all. * It is intended to be subclassed. * @author Thomas Hallgren */ public class DefaultAnnotationVisitor implements AnnotationVisitor { public void visit(String name, Object value) { } public void visitEnum(String name, String desc, String value) { } public AnnotationVisitor visitAnnotation(String name, String desc) { return null; } public AnnotationVisitor visitArray(String name) { return null; } public void visitEnd() { } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/SQLGenerator.java0000644000014500000120000000511311634451403026105 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; import org.postgresql.pljava.annotation.Function; import org.objectweb.asm.ClassReader; /** * @author Thomas Hallgren */ public class SQLGenerator { // Test function... public static void main(String[] argv) { try { StringWriter buf = new StringWriter(); PrintWriter writer = new PrintWriter(buf); for(String arg : argv) { ClassReader reader = new ClassReader(arg); PLJavaClassVisitor pljavaVisitor = new PLJavaClassVisitor(); reader.accept(pljavaVisitor, true); List functions = pljavaVisitor.getFunctions(); int top = functions.size(); for(int idx = 0; idx < top; ++idx) { FunctionVisitor function = functions.get(idx); emittFunction(function, writer); writer.println(";"); } } writer.flush(); String stmt = buf.toString(); System.out.println(stmt); } catch(Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void emittFunction(FunctionVisitor function, PrintWriter writer) { writer.print("CREATE OR REPLACE FUNCTION "); writer.print(function.getName()); writer.print('('); String[] argTypes = function.getArgumentTypes(); int top = argTypes.length; if(top > 0) { writer.print(argTypes[0]); for(int idx = 1; idx < top; ++idx) { writer.print(','); writer.print(argTypes[idx]); } } writer.println(")"); writer.print("\tRETURNS "); writer.println(function.getReturnType()); writer.println("\tLANGUAGE java"); switch(function.getType()) { case STABLE: writer.println("\tSTABLE"); break; case IMMUTABLE: writer.println("\tIMMUTABLE"); } if(function.getOnNullInput() == Function.OnNullInput.RETURNS_NULL) writer.println("\tRETURNS NULL ON NULL INPUT"); if(function.getSecurity() == Function.Security.DEFINER) writer.println("\tSECURITY DEFINER"); writer.print("\tAS '"); writer.print(function.getClassName()); writer.print('.'); writer.print(function.getMethodName()); writer.print('('); String[] paramTypes = function.getParameterTypes(); top = paramTypes.length; if(top > 0) { writer.print(paramTypes[0]); for(int idx = 1; idx < top; ++idx) { writer.print(','); writer.print(paramTypes[idx]); } } writer.print(")'"); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/GenericType.java0000644000014500000120000000647111634451403026025 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.util.ArrayList; import org.objectweb.asm.Type; /** * @author Thomas Hallgren */ public class GenericType { private final Type m_type; private final GenericType m_genericType; public static GenericType forType(String typeDesc, String signature) { return parseType(typeDesc, 0, new int[1]); } public final GenericType getGeneric() { return m_genericType; } public final Type getType() { return m_type; } public static GenericType[] getArgumentTypes(String methodDescriptor) { final ArrayList args = new ArrayList(); if(methodDescriptor.charAt(0) == '(') { int[] endPos = new int[1]; int top = methodDescriptor.length(); int pos = 1; while(pos < top) { if(methodDescriptor.charAt(pos) == ')') return args.toArray(new GenericType[args.size()]); args.add(parseType(methodDescriptor, pos, endPos)); pos = endPos[0]; } } throw new RuntimeException("Malformed method descriptor " + methodDescriptor); } public static GenericType getReturnType(String methodDescriptor) { return parseType( methodDescriptor.substring( methodDescriptor.indexOf(')') + 1), 0, new int[1]); } public String getClassName() { if(m_genericType == null) return m_type.getClassName(); StringBuilder bld = new StringBuilder(); this.printClassNameTo(bld); return bld.toString(); } public void printClassNameTo(StringBuilder bld) { bld.append(m_type.getClassName()); if(m_genericType != null) { bld.append('<'); m_genericType.printTo(bld); bld.append('>'); } } public String toString() { if(m_genericType == null) return m_type.toString(); StringBuilder bld = new StringBuilder(); this.printTo(bld); return bld.toString(); } public void printTo(StringBuilder bld) { bld.append(m_type.toString()); if(m_genericType != null) { bld.setLength(bld.length()-1); // backspace emitted ';' bld.append('<'); m_genericType.printTo(bld); bld.append('>'); bld.append(';'); } } private GenericType(Type type) { m_type = type; m_genericType = null; } private GenericType(Type type, GenericType genericType) { m_type = type; m_genericType = genericType; } private static GenericType parseType(final String desc, int pos, int[] endPos) { char c = 0; int start = pos; int len = desc.length(); while(pos < len && (c = desc.charAt(pos)) == '[') pos++; if(c != 'L') { Type realType = Type.getType(desc.substring(start, ++pos)); endPos[0] = pos; return new GenericType(realType); } while(++pos < len) { switch(c = desc.charAt(pos)) { case ';': endPos[0] = ++pos; return new GenericType(Type.getType(desc.substring(start, pos))); case '<': Type realType = Type.getType(desc.substring(start, pos) + ';'); GenericType nested = parseType(desc, ++pos, endPos); pos = endPos[0]; assert(desc.charAt(pos) == '>'); assert(desc.charAt(pos+1) == ';'); endPos[0] = pos + 2; return new GenericType(realType, nested); } } throw new RuntimeException("Malformed type " + desc); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/DefaultMethodVisitor.java0000644000014500000120000000414311634451403027706 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; /** * A MethodVisitor implementation that does nothing at all. * It is intended to be subclassed. * @author Thomas Hallgren */ public class DefaultMethodVisitor implements MethodVisitor { public AnnotationVisitor visitAnnotationDefault() { return null; } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { return null; } public void visitAttribute(Attribute attr) { } public void visitInsn(int opcode) { } public void visitIntInsn(int opcode, int operand) { } public void visitVarInsn(int opcode, int var) { } public void visitTypeInsn(int opcode, String desc) { } public void visitFieldInsn(int opcode, String owner, String name, String desc) { } public void visitMethodInsn(int opcode, String owner, String name, String desc) { } public void visitJumpInsn(int opcode, Label label) { } public void visitLabel(Label label) { } public void visitLdcInsn(Object cst) { } public void visitIincInsn(int var, int increment) { } public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { } public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { } public void visitMultiANewArrayInsn(String desc, int dims) { } public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { } public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { } public void visitLineNumber(int line, Label start) { } public void visitMaxs(int maxStack, int maxLocals) { } public void visitEnd() { } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/UnknownTypeException.java0000644000014500000120000000073111634451403027760 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; /** * @author Thomas Hallgren */ public class UnknownTypeException extends RuntimeException { UnknownTypeException(GenericType type) { super("Don't know how to map " + type.getClassName() + " to a SQL type"); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/UnrecognizedAttributeException.java0000644000014500000120000000075711634451403032007 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; /** * @author Thomas Hallgren */ public class UnrecognizedAttributeException extends IllegalArgumentException { UnrecognizedAttributeException(String attributeName) { super("The attribute " + attributeName + " is not recognized"); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/MissingAttributeException.java0000644000014500000120000000074111634451403030755 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; /** * @author Thomas Hallgren */ public class MissingAttributeException extends RuntimeException { MissingAttributeException(String attributeName) { super("The required attribute " + attributeName + " has no value"); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/PLJavaClassVisitor.java0000644000014500000120000000424611634451403027270 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; import org.postgresql.pljava.annotation.Function; /** * @author Thomas Hallgren */ public class PLJavaClassVisitor extends DefaultClassVisitor { private final String FUNCTION = Type.getDescriptor(Function.class); private final ArrayList m_functions = new ArrayList(); private String m_className; class MethodHandler extends DefaultMethodVisitor { private final String m_name; private final String m_signature; private final String m_descriptor; MethodHandler(String name, String descriptor, String signature) { m_name = name; m_signature = signature; m_descriptor = descriptor; } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if(FUNCTION.equals(desc)) { FunctionVisitor function = new FunctionVisitor( m_className, m_name, m_descriptor, m_signature); m_functions.add(function); return function; } // Other annotations may exist but we don't care about them. // return null; } } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { m_className = name.replace('/', '.'); } public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // Where' only interested in methods declared as public and static. // return ((access & ACC_STATIC) != 0 && (access & ACC_PUBLIC) != 0) ? new MethodHandler(name, desc, signature) : null; } public final List getFunctions() { return m_functions; } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/TypeMapper.java0000644000014500000120000000456511634451403025677 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Time; import java.sql.Timestamp; import java.util.HashMap; import java.util.Iterator; import org.objectweb.asm.Type; /** * @author Thomas Hallgren */ public class TypeMapper { private static final TypeMapper s_singleton = new TypeMapper(); private final HashMap m_typeMap = new HashMap(); private final Type ITERATOR = Type.getType(Iterator.class); public static TypeMapper getDefault() { return s_singleton; } public String getSQLType(GenericType type) { String sqlType = m_typeMap.get(type.getType().getDescriptor()); if(sqlType != null) return sqlType; // The type descriptor might contain significant // generic info. // GenericType gType = type.getGeneric(); if(gType == null) throw new UnknownTypeException(type); if(type.getType().equals(ITERATOR)) return "SET OF " + this.getSQLType(gType); throw new UnknownTypeException(type); } private TypeMapper() { // Primitives // this.addMap(boolean.class, "boolean"); this.addMap(Boolean.class, "boolean"); this.addMap(byte.class, "smallint"); this.addMap(Byte.class, "smallint"); this.addMap(char.class, "smallint"); this.addMap(Character.class, "smallint"); this.addMap(double.class, "double precision"); this.addMap(Double.class, "double precision"); this.addMap(float.class, "real"); this.addMap(Float.class, "real"); this.addMap(int.class, "integer"); this.addMap(Integer.class, "integer"); this.addMap(long.class, "bigint"); this.addMap(Long.class, "bigint"); this.addMap(short.class, "smallint"); this.addMap(Short.class, "smallint"); // Known common mappings // this.addMap(String.class, "varchar"); this.addMap(java.util.Date.class, "timestamp"); this.addMap(Timestamp.class, "timestamp"); this.addMap(Time.class, "time"); this.addMap(java.sql.Date.class, "date"); this.addMap(BigInteger.class, "numeric"); this.addMap(BigDecimal.class, "numeric"); this.addMap(byte[].class, "bytea"); } private void addMap(Class c, String sqlType) { m_typeMap.put(Type.getDescriptor(c), sqlType); } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/FunctionVisitor.java0000644000014500000120000001066311634451403026752 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Type; import org.postgresql.pljava.TriggerData; import org.postgresql.pljava.annotation.Function; /** * @author Thomas Hallgren */ public class FunctionVisitor extends DefaultAnnotationVisitor { private final String m_className; private final String m_methodName; private final String m_methodSignature; private final String m_methodDescriptor; private String m_name = ""; private String m_schema = ""; private Function.OnNullInput m_onNullInput = Function.OnNullInput.CALLED; private Function.Security m_security = Function.Security.INVOKER; private Function.Type m_type = Function.Type.VOLATILE; /** * The Triggers that will call this function (if any). */ private ArrayList m_triggers; FunctionVisitor(String className, String methodName, String methodDescriptor, String methodSignature) { m_className = className; m_methodName = methodName; m_methodSignature = methodSignature; m_methodDescriptor = methodDescriptor; } public void visit(String name, Object value) { if("name".equals(name)) m_name = (String)value; else if("schema".equals(name)) m_schema = (String)value; else throw new UnrecognizedAttributeException(name); } public void visitEnum(String name, String desc, String value) { if("onNullInput".equals(name)) m_onNullInput = Function.OnNullInput.valueOf(value); else if("security".equals(name)) m_security = Function.Security.valueOf(value); else if("type".equals(name)) m_type = Function.Type.valueOf(value); else throw new UnrecognizedAttributeException(name); } public AnnotationVisitor visitArray(String name) { if(name.equals("triggers")) { if(m_triggers == null) m_triggers = new ArrayList(); return new DefaultAnnotationVisitor() { public AnnotationVisitor visitAnnotation(String desc, boolean visible) { TriggerVisitor ta = new TriggerVisitor(); m_triggers.add(ta); return ta; } }; } throw new UnrecognizedAttributeException(name); } public void visitEnd() { // Verify that trigger functions have correct signature // if(m_triggers != null) { String desc = this.getMethodDescriptor(); Type[] argTypes = Type.getArgumentTypes(desc); if(!(argTypes.length == 1 && argTypes[0].equals(Type.getType(TriggerData.class)) && Type.getReturnType(desc).equals(Type.VOID_TYPE))) throw new MalformedTriggerException(this); } } public final String getName() { return (m_name.length() == 0) ? m_methodName : m_name; } public String getReturnType() { if(m_triggers != null) return "trigger"; String sign = (m_methodSignature == null) ? m_methodDescriptor : m_methodSignature; return TypeMapper.getDefault().getSQLType( GenericType.getReturnType(sign)); } public String[] getArgumentTypes() { if(m_triggers != null) return new String[0]; TypeMapper mapper = TypeMapper.getDefault(); String sign = (m_methodSignature == null) ? m_methodDescriptor : m_methodSignature; GenericType[] argTypes = GenericType.getArgumentTypes(sign); int idx = argTypes.length; String[] sqlTypes = new String[idx]; while(--idx >= 0) sqlTypes[idx] = mapper.getSQLType(argTypes[idx]); return sqlTypes; } public String[] getParameterTypes() { Type[] argTypes = Type.getArgumentTypes(m_methodDescriptor); int idx = argTypes.length; String[] paramTypes = new String[idx]; while(--idx >= 0) paramTypes[idx] = argTypes[idx].getClassName(); return paramTypes; } public final String getClassName() { return m_className; } public final String getMethodDescriptor() { return m_methodDescriptor; } public final String getMethodName() { return m_methodName; } public final String getMethodSignature() { return m_methodSignature; } public final Function.OnNullInput getOnNullInput() { return m_onNullInput; } public final String getSchema() { return m_schema; } public final Function.Security getSecurity() { return m_security; } public final List getTriggers() { return m_triggers; } public final Function.Type getType() { return m_type; } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/DefaultClassVisitor.java0000644000014500000120000000265311634451403027537 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; /** * A ClassVisitor implementation that does nothing at all. It * is intended to be subclassed. * @author Thomas Hallgren */ public class DefaultClassVisitor implements ClassVisitor { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { } public void visitSource(String source, String debug) { } public void visitOuterClass(String owner, String name, String desc) { } public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } public void visitAttribute(Attribute attr) { } public void visitInnerClass(String name, String outerName, String innerName, int access) { } public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return null; } public void visitEnd() { } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/sqlgen/TriggerVisitor.java0000644000014500000120000000657611634451403026600 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlgen; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; import org.postgresql.pljava.annotation.Trigger; /** * @author Thomas Hallgren */ public class TriggerVisitor extends DefaultAnnotationVisitor { private ArrayList m_arguments; private ArrayList m_events; private String m_name = ""; private String m_schema = ""; private Trigger.Scope m_scope = Trigger.Scope.STATEMENT; private String m_table = ""; private Trigger.When m_when; public void visit(String name, Object value) { if("name".equals(name)) m_name = (String)value; else if("schema".equals(name)) m_schema = (String)value; else if("table".equals(name)) m_table = (String)value; else throw new UnrecognizedAttributeException(name); } public void visitEnum(String name, String desc, String value) { if("scope".equals(name)) m_scope = Trigger.Scope.valueOf(value); else if("when".equals(name)) m_when = Trigger.When.valueOf(value); else throw new UnrecognizedAttributeException(name); } public AnnotationVisitor visitArray(String name) { if("arguments".equals(name)) { if(m_arguments == null) m_arguments = new ArrayList(); return new DefaultAnnotationVisitor() { public void visit(String ignore, Object value) { m_arguments.add((String)value); } }; } if("events".equals(name)) { if(m_events == null) m_events = new ArrayList(); return new DefaultAnnotationVisitor() { public void visitEnum(String ignore, String desc, String value) { m_events.add(Trigger.Event.valueOf(value)); } }; } throw new UnrecognizedAttributeException(name); } public void visitEnd() { if(m_events == null) throw new MissingAttributeException("events"); if(m_table.length() == 0) throw new MissingAttributeException("table"); if(m_when == null) throw new MissingAttributeException("when"); if(m_name.length() == 0) { StringBuilder bld = new StringBuilder(); bld.append("trg_"); bld.append((m_when == Trigger.When.BEFORE) ? 'b' : 'a'); bld.append((m_scope == Trigger.Scope.ROW) ? 'r' : 's'); // Fixed order regardless of order in list. // boolean atDelete = false; boolean atInsert = false; boolean atUpdate = false; int top = m_events.size(); for(int idx = 0; idx < top; ++idx) { switch(m_events.get(idx)) { case DELETE: atDelete = true; break; case INSERT: atInsert = true; break; default: atUpdate = true; } } bld.append('_'); if(atDelete) bld.append('d'); if(atInsert) bld.append('i'); if(atUpdate) bld.append('u'); bld.append('_'); bld.append(m_table); m_name = bld.toString(); } } final List getArguments() { return m_arguments; } final List getEvents() { return m_events; } final String getName() { return m_name; } final String getSchema() { return m_schema; } final Trigger.Scope getScope() { return m_scope; } final String getTable() { return m_table; } final Trigger.When getWhen() { return m_when; } } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/annotation/0000755000014500000120000000000011634451403023615 5ustar johannstaffpljava-1.4.3/src/java5/pljava/org/postgresql/pljava/annotation/Function.java0000644000014500000120000000302211634451403026242 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Thomas Hallgren */ @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.CLASS) public @interface Function { enum OnNullInput { CALLED, RETURNS_NULL }; enum Security { INVOKER, DEFINER }; enum Type { IMMUTABLE, STABLE, VOLATILE }; /** * The element type in case the annotated function returns a * {@link org.postgresql.pljava.ResultSetProvider ResultSetProvider}. */ String complexType() default ""; /** * The name of the function. This is optional. The default is * to use the name of the annotated method. */ String name() default ""; /** * The name of the schema if any. */ String schema() default ""; /** * Defines what should happen when input to the function * is null. */ OnNullInput onNullInput() default OnNullInput.CALLED; /** * Sets the security for the function invocation. */ Security security() default Security.INVOKER; /** * The type of the function. */ Type type() default Type.VOLATILE; /** * The Triggers that will call this function (if any). */ Trigger[] triggers() default {}; } pljava-1.4.3/src/java5/pljava/org/postgresql/pljava/annotation/Trigger.java0000644000014500000120000000235011634451403026063 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Thomas Hallgren */ @Target({}) @Retention(RetentionPolicy.CLASS) public @interface Trigger { enum When { BEFORE, AFTER }; enum Event { DELETE, INSERT, UPDATE }; enum Scope { STATEMENT, ROW }; /** * Arguments to be passed to the trigger function. */ String[] arguments() default {}; /** * The event(s) that will trigger the call. */ Event[] events(); /** * Name of the trigger. If not set, the name will * be generated. */ String name() default ""; /** * The name of the schema where containing the table for * this trigger. */ String schema() default ""; /** * The table that this trigger is tied to. */ String table(); /** * Scope, i.e. statement or row. */ Scope scope() default Scope.STATEMENT; /** * Denotes if the trigger is fired before or after its * scope (row or statement) */ When when(); } pljava-1.4.3/src/java5/org/0000755000014500000120000000000011634451403014506 5ustar johannstaffpljava-1.4.3/src/java5/org/postgresql/0000755000014500000120000000000011634451403016711 5ustar johannstaffpljava-1.4.3/src/java5/org/postgresql/pljava/0000755000014500000120000000000011634451403020166 5ustar johannstaffpljava-1.4.3/src/java5/org/postgresql/pljava/annotation/0000755000014500000120000000000011634451403022340 5ustar johannstaffpljava-1.4.3/src/java5/org/postgresql/pljava/annotation/Function.java0000644000014500000120000000177711634451403025004 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Thomas Hallgren */ @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Function { public enum OnNullInput { CALLED, RETURNS_NULL }; public enum Security { INVOKER, DEFINER }; public enum Type { IMMUTABLE, STABLE, VOLATILE }; String name(); String schema(); OnNullInput onNullInput() default OnNullInput.RETURNS_NULL; Security security() default Security.INVOKER; Type type() default Type.VOLATILE; /** * The Triggers that will call this function (if any). */ Trigger[] triggers(); } pljava-1.4.3/src/java5/org/postgresql/pljava/annotation/Trigger.java0000644000014500000120000000147211634451403024612 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Thomas Hallgren */ @Target({}) @Retention(RetentionPolicy.RUNTIME) public @interface Trigger { enum When { BEFORE, AFTER }; enum Event { DELETE, INSERT, UPDATE }; enum Scope { STATEMENT, ROW }; String[] arguments(); Event[] events(); String name(); String schema(); Scope scope() default Scope.STATEMENT; String table(); When when(); } pljava-1.4.3/src/java5/examples/0000755000014500000120000000000011634451403015535 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/0000755000014500000120000000000011634451403016324 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/postgresql/0000755000014500000120000000000011634451403020527 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/postgresql/pljava/0000755000014500000120000000000011634451403022004 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/0000755000014500000120000000000011634451403023437 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/annotation/0000755000014500000120000000000011634451403025611 5ustar johannstaffpljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/annotation/UsingProperties.java0000644000014500000120000000403711634451403031622 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example.annotation; import org.postgresql.pljava.annotation.Function; import java.io.IOException; import java.io.InputStream; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.logging.Logger; import org.postgresql.pljava.ResultSetProvider; /** * @author Thomas Hallgren */ public class UsingProperties implements ResultSetProvider { private static Logger s_logger = Logger.getAnonymousLogger(); private final Iterator m_propertyIterator; public UsingProperties() throws IOException { Properties v = new Properties(); InputStream propStream = this.getClass().getResourceAsStream("example.properties"); if(propStream == null) { s_logger.fine("example.properties was null"); m_propertyIterator = Collections.EMPTY_SET.iterator(); } else { v.load(propStream); propStream.close(); s_logger.fine("example.properties has " + v.size() + " entries"); m_propertyIterator = v.entrySet().iterator(); } } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(!m_propertyIterator.hasNext()) { s_logger.fine("no more rows, returning false"); return false; } Map.Entry propEntry = (Map.Entry)m_propertyIterator.next(); receiver.updateString(1, (String)propEntry.getKey()); receiver.updateString(2, (String)propEntry.getValue()); // s_logger.fine("next row created, returning true"); return true; } @Function( complexType = "javatest._properties") public static ResultSetProvider getProperties() throws SQLException { try { return new UsingProperties(); } catch(IOException e) { throw new SQLException("Error reading properties", e.getMessage()); } } public void close() { } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootpljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/annotation/UsingPropertiesAsScalarSet.javapljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/annotation/UsingPropertiesAsScalarSet.0000644000014500000120000000310511634451403033041 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example.annotation; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; import org.postgresql.pljava.annotation.Function; /** * This implementation uses another function that returns a set of a complex * type, concatenates the name and value of that type and returns this as * a set of a scalar type. Somewhat cumbersome way to display properties * but it's a good test. * * @author Thomas Hallgren */ public class UsingPropertiesAsScalarSet { @Function public static Iterator getProperties() throws SQLException { StringBuilder bld = new StringBuilder(); ArrayList list = new ArrayList(); Connection conn = DriverManager.getConnection("jdbc:default:connection"); Statement stmt = conn.createStatement(); try { ResultSet rs = stmt.executeQuery("SELECT name, value FROM propertyExample()"); try { while(rs.next()) { bld.setLength(0); bld.append(rs.getString(1)); bld.append(" = "); bld.append(rs.getString(2)); list.add(bld.toString()); } return list.iterator(); } finally { try { rs.close(); } catch(SQLException e) {} } } finally { try { stmt.close(); } catch(SQLException e) {} } } } pljava-1.4.3/src/java5/examples/org/postgresql/pljava/example/annotation/Triggers.java0000644000014500000120000000176511634451403030253 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example.annotation; import java.sql.SQLException; import org.postgresql.pljava.TriggerData; import org.postgresql.pljava.annotation.Function; import org.postgresql.pljava.annotation.Trigger; import static org.postgresql.pljava.annotation.Trigger.When.*; import static org.postgresql.pljava.annotation.Trigger.Event.*; import static org.postgresql.pljava.annotation.Function.Security.*; public class Triggers { /** * insert user name in response to a trigger. */ @Function( schema = "javatest", security = INVOKER, triggers = { @Trigger(when = BEFORE, table = "foobar_1", events = { INSERT } ), @Trigger(when = BEFORE, table = "foobar_2", events = { INSERT } ) }) public static void insertUsername(TriggerData td) throws SQLException { } } pljava-1.4.3/src/java/0000755000014500000120000000000011634451404013633 5ustar johannstaffpljava-1.4.3/src/java/pljava/0000755000014500000120000000000011634451403015107 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/0000755000014500000120000000000011634451403015676 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/0000755000014500000120000000000011634451403020101 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/0000755000014500000120000000000011634451403021356 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/TriggerData.java0000644000014500000120000001122511634451403024417 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.ResultSet; import java.sql.SQLException; /** * The SQL 2003 spec. does not stipulate a standard way of mapping * triggers to functions. The PLJava mapping use this interface. All * functions that are intended to be triggers must be public, static, * return void, and take a TriggerData as their argument. * * @author Thomas Hallgren */ public interface TriggerData { /** * Returns the ResultSet that represents the new row. This ResultSet will * be null for delete triggers and for triggers that was fired for * statement.
The returned set will be updateable and positioned on a * valid row. When the trigger call returns, the trigger manager will see * the changes that has been made to this row and construct a new tuple * which will become the new or updated row. * * @return An updateable ResultSet containing one row or * null. * @throws SQLException * if the contained native buffer has gone stale. */ ResultSet getNew() throws SQLException; /** * Returns the ResultSet that represents the old row. This ResultSet will * be null for insert triggers and for triggers that was fired for * statement.
The returned set will be read-only and positioned on a * valid row. * * @return A read-only ResultSet containing one row or * null. * @throws SQLException * if the contained native buffer has gone stale. */ ResultSet getOld() throws SQLException; /** * Returns the arguments for this trigger (as declared in the CREATE TRIGGER * statement. If the trigger has no arguments, this method will return an * array with size 0. * * @throws SQLException * if the contained native buffer has gone stale. */ String[] getArguments() throws SQLException; /** * Returns the name of the trigger (as declared in the CREATE TRIGGER * statement). * * @throws SQLException * if the contained native buffer has gone stale. */ String getName() throws SQLException; /** * Returns the name of the table for which this trigger was created (as * declared in the CREATE TRIGGERCREATE TRIGGERtrue if the trigger was fired after the statement * or row action that it is associated with. * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredAfter() throws SQLException; /** * Returns true if the trigger was fired before the * statement or row action that it is associated with. * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredBefore() throws SQLException; /** * Returns true if this trigger is fired once for each row * (as opposed to once for the entire statement). * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredForEachRow() throws SQLException; /** * Returns true if this trigger is fired once for the entire * statement (as opposed to once for each row). * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredForStatement() throws SQLException; /** * Returns true if this trigger was fired by a DELETE. * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredByDelete() throws SQLException; /** * Returns true if this trigger was fired by an INSERT. * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredByInsert() throws SQLException; /** * Returns true if this trigger was fired by an UPDATE. * * @throws SQLException * if the contained native buffer has gone stale. */ boolean isFiredByUpdate() throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/0000755000014500000120000000000011634451404022261 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java0000644000014500000120000006106311634451404025605 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.BitSet; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.postgresql.pljava.internal.Oid; import org.postgresql.pljava.internal.PgSavepoint; /** * Provides access to the current connection (session) the Java stored * procedure is running in. It is returned from the driver manager * with * DriverManager.getConnection("jdbc:default:connection"); * and cannot be managed in any way since it's already running inside * a transaction. This means the following methods cannot be used. *
    *
  • commit()
  • *
  • rollback()
  • *
  • setAutoCommit()
  • *
  • setTransactionIsolation()
  • *
* @author Thomas Hallgren */ public class SPIConnection implements Connection { /** * A map from Java classes to java.sql.Types integers. */ private static final HashMap s_sqlType2Class = new HashMap(30); /** * The version number of the currently executing PostgreSQL * server. */ private int[] VERSION_NUMBER = null; static { addType(String.class, Types.VARCHAR); addType(Byte.class, Types.TINYINT); addType(Short.class, Types.SMALLINT); addType(Integer.class, Types.INTEGER); addType(Long.class, Types.BIGINT); addType(Float.class, Types.FLOAT); addType(Double.class, Types.DOUBLE); addType(BigDecimal.class, Types.DECIMAL); addType(BigInteger.class, Types.NUMERIC); addType(Boolean.class, Types.BOOLEAN); addType(Blob.class, Types.BLOB); addType(Clob.class, Types.CLOB); addType(Date.class, Types.DATE); addType(Time.class, Types.TIME); addType(Timestamp.class, Types.TIMESTAMP); addType(java.util.Date.class, Types.TIMESTAMP); addType(byte[].class, Types.VARBINARY); addType(BitSet.class, Types.BIT); addType(URL.class, Types.DATALINK); } private static final void addType(Class clazz, int sqlType) { s_sqlType2Class.put(clazz, new Integer(sqlType)); } /** * Returns a default connection instance. It is the callers responsability * to close this instance. */ public static Connection getDefault() throws SQLException { return new SPIConnection(); } /** * Returns {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. Cursors are actually * closed when a function returns to SQL. */ public int getHoldability() { return ResultSet.CLOSE_CURSORS_AT_COMMIT; } /** * Returns {@link Connection#TRANSACTION_READ_COMMITTED}. */ public int getTransactionIsolation() { return TRANSACTION_READ_COMMITTED; } /** * Warnings are not yet supported. * @throws SQLException indicating that this feature is not supported. */ public void clearWarnings() throws SQLException { throw new UnsupportedFeatureException("Connection.clearWarnings"); } /** * This is a no-op. The default connection never closes. */ public void close() { } /** * It's not legal to do a commit within a call from SQL. * @throws SQLException indicating that this feature is not supported. */ public void commit() throws SQLException { throw new UnsupportedFeatureException("Connection.commit"); } /** * It's not legal to do a rollback within a call from SQL. * @throws SQLException indicating that this feature is not supported. */ public void rollback() throws SQLException { throw new UnsupportedFeatureException("Connection.rollback"); } /** * It is assumed that an SPI call is under transaction control. This method * will always return false. */ public boolean getAutoCommit() { return false; } /** * Will always return false. */ public boolean isClosed() { return false; } /** * Returns false. The SPIConnection is not real-only. */ public boolean isReadOnly() { return false; } /** * Change of holdability is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setHoldability(int holdability) throws SQLException { throw new UnsupportedFeatureException("Connection.setHoldability"); } /** * Change of transaction isolation level is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setTransactionIsolation(int level) throws SQLException { throw new UnsupportedFeatureException("Connection.setTransactionIsolation"); } /** * It is assumed that an SPI call is under transaction control. Changing * that is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setAutoCommit(boolean autoCommit) throws SQLException { throw new UnsupportedFeatureException("Connection.setAutoCommit"); } /** * It is assumed that an inserts and updates can be performed using and * SPIConnection. Changing that is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setReadOnly(boolean readOnly) throws SQLException { throw new UnsupportedFeatureException("Connection.setReadOnly"); } /** * Returns the database in which we are running. */ public String getCatalog() throws SQLException { ResultSet rs = createStatement().executeQuery("SELECT pg_catalog.current_database()"); try { rs.next(); return rs.getString(1); } finally { rs.close(); } } /** * The catalog name cannot be set. * @throws SQLException indicating that this feature is not supported. */ public void setCatalog(String catalog) throws SQLException { throw new UnsupportedFeatureException("Connection.setCatalog"); } /** * Retrieves an instance of {@link SPIDatabaseMetaData} * representing this Connection object. The * metadata includes information about the SQL grammar * supported by PostgreSQL, the capabilities of PL/Java, as * well as the tables and stored procedures for this * connection and so on. * * @return an SPIDatabaseMetaData object for this * Connection object */ public DatabaseMetaData getMetaData() { return new SPIDatabaseMetaData(this); } /** * Warnings are not yet supported. * @throws SQLException indicating that this feature is not supported. */ public SQLWarning getWarnings() throws SQLException { throw new UnsupportedFeatureException("Connection.getWarnings"); } public void releaseSavepoint(Savepoint savepoint) throws SQLException { if(!(savepoint instanceof PgSavepoint)) throw new IllegalArgumentException("Not a PL/Java Savepoint"); PgSavepoint sp = (PgSavepoint)savepoint; sp.release(); forgetSavepoint(sp); } public void rollback(Savepoint savepoint) throws SQLException { if(!(savepoint instanceof PgSavepoint)) throw new IllegalArgumentException("Not a PL/Java Savepoint"); PgSavepoint sp = (PgSavepoint)savepoint; Invocation.clearErrorCondition(); sp.rollback(); forgetSavepoint(sp); } /** * Creates a new instance of SPIStatement. */ public Statement createStatement() throws SQLException { if(this.isClosed()) throw new SQLException("Connection is closed"); return new SPIStatement(this); } /** * Creates a new instance of SPIStatement. * * @throws SQLException * * if the resultSetType differs from * {@link ResultSet#TYPE_FORWARD_ONLY} or if the * resultSetConcurrencty differs from * {@link ResultSet#CONCUR_READ_ONLY}. */ public Statement createStatement( int resultSetType, int resultSetConcurrency) throws SQLException { if(resultSetType != ResultSet.TYPE_FORWARD_ONLY) throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type"); if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency"); return this.createStatement(); } /** * Creates a new instance of SPIStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY}, if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the * resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ public Statement createStatement( int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) throw new UnsupportedOperationException( "CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability"); return this.createStatement(resultSetType, resultSetConcurrency); } /** * Returns null. Type map is not yet imlemented. */ public Map getTypeMap() throws SQLException { return null; } /** * Type map is not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public void setTypeMap(Map map) throws SQLException { throw new UnsupportedOperationException("Type map is not yet implemented"); } /** * Parse the JDBC SQL into PostgreSQL. */ public String nativeSQL(String sql) throws SQLException { return this.nativeSQL(sql, null); } public String nativeSQL(String sql, int[] paramCountRet) { StringBuffer buf = new StringBuffer(); int len = sql.length(); char inQuote = 0; int paramIndex = 1; for(int idx = 0; idx < len; ++idx) { char c = sql.charAt(idx); switch(c) { case '\\': // Next character is escaped. Keep both // escape and the character. // buf.append(c); if(++idx == len) break; c = sql.charAt(idx); break; case '\'': case '"': // Strings within quotes should not be subject // to '?' -> '$n' substitution. // if(inQuote == c) inQuote = 0; else inQuote = c; break; case '?': if(inQuote == 0) { buf.append('$'); buf.append(paramIndex++); continue; } break; default: if(inQuote == 0 && Character.isWhitespace(c)) { // Strip of multiple whitespace outside of // strings. // ++idx; while(idx < len && Character.isWhitespace(sql.charAt(idx))) ++idx; --idx; c = ' '; } } buf.append(c); } if(paramCountRet != null) paramCountRet[0] = paramIndex - 1; return buf.toString(); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall(String sql) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Creates a new instance of SPIPreparedStatement. */ public PreparedStatement prepareStatement(String sql) throws SQLException { if(this.isClosed()) throw new SQLException("Connection is closed"); int[] pcount = new int[] { 0 }; sql = this.nativeSQL(sql, pcount); PreparedStatement stmt = new SPIPreparedStatement(this, sql, pcount[0]); Invocation.current().manageStatement(stmt); return stmt; } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Creates a new instance of SPIPreparedStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY} or if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}. */ public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency) throws SQLException { if(resultSetType != ResultSet.TYPE_FORWARD_ONLY) throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type"); if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency"); return prepareStatement(sql); } /** * Creates a new instance of SPIPreparedStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY}, if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the * resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) throw new UnsupportedOperationException( "CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability"); return this.prepareStatement(sql, resultSetType, resultSetConcurrency); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } public Savepoint setSavepoint() throws SQLException { return this.rememberSavepoint(PgSavepoint.set("anonymous_savepoint")); } public Savepoint setSavepoint(String name) throws SQLException { return this.rememberSavepoint(PgSavepoint.set(name)); } static int getTypeForClass(Class c) { if(c.isArray() && !c.equals(byte[].class)) return Types.ARRAY; Integer sqt = (Integer)s_sqlType2Class.get(c); if(sqt != null) return sqt.intValue(); /* * This is not a well known JDBC type. */ return Types.OTHER; } private Savepoint rememberSavepoint(PgSavepoint sp) throws SQLException { // Remember the first savepoint for each call-level so // that it can be released when the function call ends. Releasing // the first savepoint will release all subsequent savepoints. // Invocation invocation = Invocation.current(); Savepoint old = invocation.getSavepoint(); if(old == null) invocation.setSavepoint(sp); return sp; } private static void forgetSavepoint(PgSavepoint sp) throws SQLException { Invocation invocation = Invocation.current(); if(invocation.getSavepoint() == sp) invocation.setSavepoint(null); } public int[] getVersionNumber() throws SQLException { if (VERSION_NUMBER != null) return VERSION_NUMBER; ResultSet rs = createStatement().executeQuery( "SELECT version()"); try { if (!rs.next()) throw new SQLException( "Cannot retrieve product version number"); String ver = rs.getString(1); Pattern p = Pattern.compile( "^PostgreSQL\\s+(\\d+)\\.(\\d+)(.\\d+)?.*"); Matcher m = p.matcher(ver); if(m.matches()) { VERSION_NUMBER = new int[3]; VERSION_NUMBER[0] = Integer.parseInt(m.group(1)); VERSION_NUMBER[1] = Integer.parseInt(m.group(2)); String bugfix = m.group(3); if(bugfix != null && bugfix.length() > 1) VERSION_NUMBER[2] = Integer.parseInt(bugfix.substring(1)); return VERSION_NUMBER; } throw new SQLException( "Unexpected product version string format: " + ver); } catch (PatternSyntaxException e) { throw new SQLException( "Error in product version string parsing: " + e.getMessage()); } finally { rs.close(); } } /* * This implemetation uses the jdbc3Types array to support the jdbc3 * datatypes. Basically jdbc2 and jdbc3 are the same, except that * jdbc3 adds some */ public int getSQLType(String pgTypeName) { if (pgTypeName == null) return Types.OTHER; for (int i = 0;i < JDBC3_TYPE_NAMES.length;i++) if (pgTypeName.equals(JDBC3_TYPE_NAMES[i])) return JDBC_TYPE_NUMBERS[i]; return Types.OTHER; } /* * This returns the java.sql.Types type for a PG type oid * * @param oid PostgreSQL type oid * @return the java.sql.Types type * @exception SQLException if a database access error occurs */ public int getSQLType(Oid oid) throws SQLException { return getSQLType(getPGType(oid)); } public String getPGType(Oid oid) throws SQLException { String typeName = null; PreparedStatement query = null; ResultSet rs = null; try { query = prepareStatement("SELECT typname FROM pg_catalog.pg_type WHERE oid=?"); query.setObject(1, oid); rs = query.executeQuery(); if (rs.next()) { typeName = rs.getString(1); } else { throw new SQLException("Cannot find PG type with oid=" + oid); } } finally { if (query != null) { query.close(); } } return typeName; } static Object basicCoersion(Class cls, Object value) throws SQLException { if(value == null || cls.isInstance(value)) return value; if(cls == String.class) { if(value instanceof Number || value instanceof Boolean || value instanceof Timestamp || value instanceof Date || value instanceof Time) return value.toString(); } else if(cls == URL.class && value instanceof String) { try { return new URL((String)value); } catch(MalformedURLException e) { throw new SQLException(e.toString()); } } throw new SQLException("Cannot derive a value of class " + cls.getName() + " from an object of class " + value.getClass().getName()); } static Number basicNumericCoersion(Class cls, Object value) throws SQLException { if(value == null || value instanceof Number) return (Number)value; if(cls == int.class || cls == long.class || cls == short.class || cls == byte.class) { if(value instanceof String) return Long.valueOf((String)value); if(value instanceof Boolean) return new Long(((Boolean)value).booleanValue() ? 1 : 0); } else if(cls == BigDecimal.class) { if(value instanceof String) return new BigDecimal((String)value); if(value instanceof Boolean) return new BigDecimal(((Boolean)value).booleanValue() ? 1 : 0); } if(cls == double.class || cls == float.class) { if(value instanceof String) return Double.valueOf((String)value); if(value instanceof Boolean) return new Double(((Boolean)value).booleanValue() ? 1 : 0); } throw new SQLException("Cannot derive a Number from an object of class " + value.getClass().getName()); } static Object basicCalendricalCoersion(Class cls, Object value, Calendar cal) throws SQLException { if(value == null) return value; if(cls.isInstance(value)) return value; if(cls == Timestamp.class) { if(value instanceof Date) { cal.setTime((Date)value); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return new Timestamp(cal.getTimeInMillis()); } else if(value instanceof Time) { cal.setTime((Date)value); cal.set(1970, 0, 1); return new Timestamp(cal.getTimeInMillis()); } else if(value instanceof String) { return Timestamp.valueOf((String)value); } } else if(cls == Date.class) { if(value instanceof Timestamp) { Timestamp ts = (Timestamp)value; cal.setTime(ts); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return new Date(cal.getTimeInMillis()); } else if(value instanceof String) { return Date.valueOf((String)value); } } else if(cls == Time.class) { if(value instanceof Timestamp) { Timestamp ts = (Timestamp)value; cal.setTime(ts); cal.set(1970, 0, 1); return new Time(cal.getTimeInMillis()); } else if(value instanceof String) { return Time.valueOf((String)value); } } throw new SQLException("Cannot derive a value of class " + cls.getName() + " from an object of class " + value.getClass().getName()); } /* * This table holds the org.postgresql names for the types supported. * Any types that map to Types.OTHER (eg POINT) don't go into this table. * They default automatically to Types.OTHER * * Note: This must be in the same order as below. * * Tip: keep these grouped together by the Types. value */ public static final String JDBC3_TYPE_NAMES[] = { "int2", "int4", "oid", "int8", "cash", "money", "numeric", "float4", "float8", "bpchar", "char", "char2", "char4", "char8", "char16", "varchar", "text", "name", "filename", "bytea", "bool", "bit", "date", "time", "timetz", "abstime", "timestamp", "timestamptz", "_bool", "_char", "_int2", "_int4", "_text", "_oid", "_varchar", "_int8", "_float4", "_float8", "_abstime", "_date", "_time", "_timestamp", "_numeric", "_bytea" }; /* * This table holds the JDBC type for each entry above. * * Note: This must be in the same order as above * * Tip: keep these grouped together by the Types. value */ public static final int JDBC_TYPE_NUMBERS[] = { Types.SMALLINT, Types.INTEGER, Types.INTEGER, Types.BIGINT, Types.DOUBLE, Types.DOUBLE, Types.NUMERIC, Types.REAL, Types.DOUBLE, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BINARY, Types.BIT, Types.BIT, Types.DATE, Types.TIME, Types.TIME, Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY }; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/ReadOnlyResultSet.java0000644000014500000120000000576211634451404026526 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSet; import java.sql.SQLException; /** * The ReadOnlyResultSet implements all methods that changes the ResultSet * in any way as methods that yield an {@link UnsupportedFeatureException}. * * @author Thomas Hallgren */ public abstract class ReadOnlyResultSet extends ObjectResultSet { /** * Returns {@link ResultSet#CONCUR_READ_ONLY}. */ public int getConcurrency() throws SQLException { return ResultSet.CONCUR_READ_ONLY; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void cancelRowUpdates() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void deleteRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void insertRow() throws SQLException { throw readOnlyException(); } /** * This is a no-op since the moveToInsertRow() method is * unsupported. */ public void moveToCurrentRow() throws SQLException { } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void moveToInsertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateRow() throws SQLException { throw readOnlyException(); } /** * Always returns false. */ public boolean rowDeleted() throws SQLException { return false; } /** * Always returns false. */ public boolean rowInserted() throws SQLException { return false; } /** * Always returns false. */ public boolean rowUpdated() throws SQLException { return false; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x) throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw readOnlyException(); } private static SQLException readOnlyException() { return new UnsupportedFeatureException("ResultSet is read-only"); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIResultSet.java0000644000014500000120000000774311634451404025445 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSetMetaData; import org.postgresql.pljava.internal.Portal; import org.postgresql.pljava.internal.SPI; import org.postgresql.pljava.internal.TupleTable; import org.postgresql.pljava.internal.Tuple; import org.postgresql.pljava.internal.TupleDesc; /** * A Read-only ResultSet that provides direct access to a {@link * org.postgresql.pljava.internal.Portal Portal}. At present, only * forward positioning is implemented. Attempts to use reverse or * absolute positioning will fail. * * @author Thomas Hallgren */ public class SPIResultSet extends ResultSetBase { private final SPIStatement m_statement; private final Portal m_portal; private final TupleDesc m_tupleDesc; private final int m_maxRows; private Tuple m_currentRow; private Tuple m_nextRow; private TupleTable m_table; private int m_tableRow; SPIResultSet(SPIStatement statement, Portal portal, int maxRows) throws SQLException { super(statement.getFetchSize()); m_statement = statement; m_portal = portal; m_maxRows = maxRows; m_tupleDesc = portal.getTupleDesc(); m_tableRow = -1; } public int getFetchDirection() throws SQLException { return FETCH_FORWARD; } public void close() throws SQLException { if(m_portal.isValid()) { m_portal.close(); m_statement.resultSetClosed(this); m_table = null; m_tableRow = -1; m_currentRow = null; m_nextRow = null; super.close(); } } public boolean isLast() throws SQLException { return m_currentRow != null && this.peekNext() == null; } public boolean next() throws SQLException { m_currentRow = this.peekNext(); m_nextRow = null; boolean result = (m_currentRow != null); this.setRow(result ? this.getRow() + 1 : -1); return result; } public String getCursorName() throws SQLException { return this.getPortal().getName(); } public int findColumn(String columnName) throws SQLException { return m_tupleDesc.getColumnIndex(columnName); } public Statement getStatement() throws SQLException { return m_statement; } protected final Portal getPortal() throws SQLException { if(!m_portal.isValid()) throw new SQLException("ResultSet is closed"); return m_portal; } protected final TupleTable getTupleTable() throws SQLException { if(m_table == null) { Portal portal = this.getPortal(); if(portal.isAtEnd()) return null; int mx; int fetchSize = this.getFetchSize(); if(m_maxRows > 0) { mx = m_maxRows - portal.getPortalPos(); if(mx <= 0) return null; if(mx > fetchSize) mx = fetchSize; } else mx = fetchSize; try { int result = portal.fetch(true, mx); if(result > 0) m_table = SPI.getTupTable(m_tupleDesc); m_tableRow = -1; } finally { SPI.freeTupTable(); } } return m_table; } protected final Tuple getCurrentRow() throws SQLException { if(m_currentRow == null) throw new SQLException("ResultSet is not positioned on a valid row"); return m_currentRow; } protected final Tuple peekNext() throws SQLException { if(m_nextRow != null) return m_nextRow; TupleTable table = this.getTupleTable(); if(table == null) return null; if(m_tableRow >= table.getCount() - 1) { // Current table is exhaused, get the next // one. // m_table = null; table = this.getTupleTable(); if(table == null) return null; } m_nextRow = table.getSlot(++m_tableRow); return m_nextRow; } protected Object getObjectValue(int columnIndex) throws SQLException { return this.getCurrentRow().getObject(m_tupleDesc, columnIndex); } public ResultSetMetaData getMetaData() throws SQLException { return new SPIResultSetMetaData(m_tupleDesc); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/UnsupportedFeatureException.java0000644000014500000120000000131411634451404030646 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class UnsupportedFeatureException extends SQLException { private static final long serialVersionUID = 7956037664745636982L; public static final String FEATURE_NOT_SUPPORTED_EXCEPTION = "0A000"; public UnsupportedFeatureException(String feature) { super("Feature not supported: " + feature, FEATURE_NOT_SUPPORTED_EXCEPTION); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SQLInputFromTuple.java0000644000014500000120000001124411634451404026443 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.Time; import java.sql.Timestamp; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.JavaWrapper; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, updateable ResultSet specially made for triggers. The * changes made to this ResultSet are remembered and converted to a * SPI_modify_tuple call prior to function return. * * @author Thomas Hallgren */ public class SQLInputFromTuple extends JavaWrapper implements SQLInput { private int m_index; private final TupleDesc m_tupleDesc; private boolean m_wasNull; public SQLInputFromTuple(long heapTupleHeaderPointer, TupleDesc tupleDesc) throws SQLException { super(heapTupleHeaderPointer); m_tupleDesc = tupleDesc; m_index = 0; m_wasNull = false; } public Array readArray() throws SQLException { return (Array)this.readValue(Array.class); } public InputStream readAsciiStream() throws SQLException { Clob c = this.readClob(); return (c == null) ? null : c.getAsciiStream(); } public BigDecimal readBigDecimal() throws SQLException { return (BigDecimal)this.readValue(BigDecimal.class); } public InputStream readBinaryStream() throws SQLException { Blob b = this.readBlob(); return (b == null) ? null : b.getBinaryStream(); } public Blob readBlob() throws SQLException { byte[] bytes = this.readBytes(); return (bytes == null) ? null : new BlobValue(bytes); } public boolean readBoolean() throws SQLException { Boolean b = (Boolean)this.readValue(Boolean.class); return (b == null) ? false : b.booleanValue(); } public byte readByte() throws SQLException { Number b = this.readNumber(byte.class); return (b == null) ? 0 : b.byteValue(); } public byte[] readBytes() throws SQLException { return (byte[])this.readValue(byte[].class); } public Reader readCharacterStream() throws SQLException { Clob c = this.readClob(); return (c == null) ? null : c.getCharacterStream(); } public Clob readClob() throws SQLException { String str = this.readString(); return (str == null) ? null : new ClobValue(str); } public Date readDate() throws SQLException { return (Date)this.readValue(Date.class); } public double readDouble() throws SQLException { Number d = this.readNumber(double.class); return (d == null) ? 0 : d.doubleValue(); } public float readFloat() throws SQLException { Number f = this.readNumber(float.class); return (f == null) ? 0 : f.floatValue(); } public int readInt() throws SQLException { Number i = this.readNumber(int.class); return (i == null) ? 0 : i.intValue(); } public long readLong() throws SQLException { Number l = this.readNumber(long.class); return (l == null) ? 0 : l.longValue(); } public Object readObject() throws SQLException { if(m_index < m_tupleDesc.size()) { Object v; synchronized(Backend.THREADLOCK) { v = _getObject(this.getNativePointer(), m_tupleDesc.getNativePointer(), ++m_index); } m_wasNull = v == null; return v; } throw new SQLException("Tuple has no more columns"); } public Ref readRef() throws SQLException { return (Ref)this.readValue(Ref.class); } public short readShort() throws SQLException { Number s = this.readNumber(short.class); return (s == null) ? 0 : s.shortValue(); } public String readString() throws SQLException { return (String)this.readValue(String.class); } public Time readTime() throws SQLException { return (Time)this.readValue(Time.class); } public Timestamp readTimestamp() throws SQLException { return (Timestamp)this.readValue(Timestamp.class); } public URL readURL() throws SQLException { return (URL)this.readValue(URL.class); } public boolean wasNull() throws SQLException { return m_wasNull; } private Number readNumber(Class numberClass) throws SQLException { return SPIConnection.basicNumericCoersion(numberClass, this.readObject()); } private Object readValue(Class valueClass) throws SQLException { return SPIConnection.basicCoersion(valueClass, this.readObject()); } protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetBase.java0000644000014500000120000000711611634451404025656 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; /** * A ResultSet base provides methods that are common both for * a SyntheticResultSet (which is not associated with a * statement) and SPIResultSet. * * @author Filip Hrbek */ abstract class ResultSetBase extends ReadOnlyResultSet { private int m_fetchSize; private int m_row; ResultSetBase(int fetchSize) { m_fetchSize = fetchSize; m_row = 0; // First row is 1 so 0 is on undefined position. } public int getFetchDirection() throws SQLException { return FETCH_FORWARD; } public final int getFetchSize() throws SQLException { return m_fetchSize; } public final int getRow() throws SQLException { return m_row; } public int getType() throws SQLException { return TYPE_FORWARD_ONLY; } /** * Cursor positoning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public void afterLast() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Cursor positoning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public void beforeFirst() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } public void close() throws SQLException { m_row = -1; } /** * Cursor positioning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public boolean first() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } public boolean isAfterLast() throws SQLException { return m_row < 0; } public boolean isBeforeFirst() throws SQLException { return m_row == 0; } public boolean isFirst() throws SQLException { return m_row == 1; } /** * Cursor positioning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public boolean last() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Reverse positioning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public boolean previous() throws SQLException { throw new UnsupportedFeatureException("Reverse positioning"); } /** * Cursor positioning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public boolean absolute(int row) throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Cursor positioning is not implemented yet. * @throws SQLException indicating that this feature is not supported. */ public boolean relative(int rows) throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Only {@link java.sql.ResultSet#FETCH_FORWARD} is supported. * @throws SQLException indicating that this feature is not supported * for other values on direction. */ public void setFetchDirection(int direction) throws SQLException { if(direction != FETCH_FORWARD) throw new UnsupportedFeatureException("Non forward fetch direction"); } public void setFetchSize(int fetchSize) throws SQLException { if(fetchSize <= 0) throw new IllegalArgumentException("Illegal fetch size for ResultSet"); m_fetchSize = fetchSize; } final void setRow(int row) { m_row = row; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SQLOutputToTuple.java0000644000014500000120000001113611634451404026323 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLData; import java.sql.SQLException; import java.sql.SQLOutput; import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import org.postgresql.pljava.internal.Tuple; import org.postgresql.pljava.internal.TupleDesc; /** * @author Thomas Hallgren */ public class SQLOutputToTuple implements SQLOutput { private final Object[] m_values; private final TupleDesc m_tupleDesc; private int m_index; private Tuple m_tuple; public SQLOutputToTuple(TupleDesc tupleDesc) { m_tupleDesc = tupleDesc; m_values = new Object[tupleDesc.size()]; m_index = 0; } /** * Creates a tuple from the written values and returns its native pointer. * All values must have been written. This method is called automatically by * the trigger handler and should not be called in any other way. * * @return The Tuple reflecting the current row values. * @throws SQLException */ public long getTuple() throws SQLException { if(m_tuple != null) return m_tuple.getNativePointer(); if(m_index < m_values.length) throw new SQLException("Too few values have been written"); m_tuple = m_tupleDesc.formTuple(m_values); return m_tuple.getNativePointer(); } public void writeArray(Array value) throws SQLException { this.writeValue(value); } public void writeAsciiStream(InputStream value) throws SQLException { try { Reader rdr = new BufferedReader(new InputStreamReader(value, "US-ASCII")); this.writeClob(new ClobValue(rdr, ClobValue.getReaderLength(rdr))); } catch(UnsupportedEncodingException e) { throw new SQLException(e.toString()); } } public void writeBigDecimal(BigDecimal value) throws SQLException { this.writeValue(value); } public void writeBinaryStream(InputStream value) throws SQLException { if(!value.markSupported()) value = new BufferedInputStream(value); this.writeBlob(new BlobValue(value, BlobValue.getStreamLength(value))); } public void writeBlob(Blob value) throws SQLException { this.writeValue(value); } public void writeBoolean(boolean value) throws SQLException { this.writeValue(value ? Boolean.TRUE : Boolean.FALSE); } public void writeByte(byte value) throws SQLException { this.writeValue(new Byte(value)); } public void writeBytes(byte[] value) throws SQLException { this.writeValue(value); } public void writeCharacterStream(Reader value) throws SQLException { if(!value.markSupported()) value = new BufferedReader(value); this.writeClob(new ClobValue(value, ClobValue.getReaderLength(value))); } public void writeClob(Clob value) throws SQLException { this.writeValue(value); } public void writeDate(Date value) throws SQLException { this.writeValue(value); } public void writeDouble(double value) throws SQLException { this.writeValue(new Double(value)); } public void writeFloat(float value) throws SQLException { this.writeValue(new Float(value)); } public void writeInt(int value) throws SQLException { this.writeValue(new Integer(value)); } public void writeLong(long value) throws SQLException { this.writeValue(new Long(value)); } public void writeObject(SQLData value) throws SQLException { this.writeValue(value); } public void writeRef(Ref value) throws SQLException { this.writeValue(value); } public void writeShort(short value) throws SQLException { this.writeValue(new Short(value)); } public void writeString(String value) throws SQLException { this.writeValue(value); } public void writeStruct(Struct value) throws SQLException { this.writeValue(value); } public void writeTime(Time value) throws SQLException { this.writeValue(value); } public void writeTimestamp(Timestamp value) throws SQLException { this.writeValue(value); } public void writeURL(URL value) throws SQLException { this.writeValue(value.toString()); } private void writeValue(Object value) throws SQLException { if(m_index >= m_values.length) throw new SQLException("Tuple cannot take more values"); m_values[m_index++] = value; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/TypeOid.java0000644000014500000120000000237411634451404024507 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import org.postgresql.pljava.internal.Oid; /** * Provides constants for well-known backend OIDs for the types we commonly * use. */ public class TypeOid { public static final Oid INVALID = new Oid(0); public static final Oid INT2 = new Oid(21); public static final Oid INT4 = new Oid(23); public static final Oid INT8 = new Oid(20); public static final Oid TEXT = new Oid(25); public static final Oid NUMERIC = new Oid(1700); public static final Oid FLOAT4 = new Oid(700); public static final Oid FLOAT8 = new Oid(701); public static final Oid BOOL = new Oid(16); public static final Oid DATE = new Oid(1082); public static final Oid TIME = new Oid(1083); public static final Oid TIMESTAMP = new Oid(1114); public static final Oid TIMESTAMPTZ = new Oid(1184); public static final Oid BYTEA = new Oid(17); public static final Oid VARCHAR = new Oid(1043); public static final Oid OID = new Oid(26); public static final Oid BPCHAR = new Oid(1042); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/TypeOid.java~0000644000014500000120000000237411634451404024705 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import org.postgresql.pljava.internal.Oid; /** * Provides constants for well-known backend OIDs for the types we commonly * use. */ public class TypeOid { public static final Oid INVALID = new Oid(0); public static final Oid INT2 = new Oid(21); public static final Oid INT4 = new Oid(23); public static final Oid INT8 = new Oid(20); public static final Oid TEXT = new Oid(25); public static final Oid NUMERIC = new Oid(1700); public static final Oid FLOAT4 = new Oid(700); public static final Oid FLOAT8 = new Oid(701); public static final Oid BOOL = new Oid(16); public static final Oid DATE = new Oid(1082); public static final Oid TIME = new Oid(1083); public static final Oid TIMESTAMP = new Oid(1114); public static final Oid TIMESTAMPTZ = new Oid(1184); public static final Oid BYTEA = new Oid(17); public static final Oid VARCHAR = new Oid(1043); public static final Oid OID = new Oid(26); public static final Oid BPCHAR = new Oid(1042); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SQLOutputToChunk.java0000644000014500000120000001413211634451404026301 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLData; import java.sql.SQLException; import java.sql.SQLOutput; import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import org.postgresql.pljava.internal.Backend; /** * The SQLOutputToChunk uses JNI to build a PostgreSQL StringInfo buffer in * memory. A user should never make an attempt to create an instance of this * class. Only internal JNI routines can do that. An instance is propagated * in a call from the internal JNI layer to the Java layer will only survive * during that single call. The handle of the instance will be invalidated when * the call returns and subsequent use of the instance will yield a * SQLException with the message "Stream is closed". * * @author Thomas Hallgren */ public class SQLOutputToChunk implements SQLOutput { private static final byte[] s_byteBuffer = new byte[8]; private long m_handle; public SQLOutputToChunk(long handle) { m_handle = handle; } public void writeArray(Array value) throws SQLException { throw new UnsupportedOperationException("writeArray"); } public void writeAsciiStream(InputStream value) throws SQLException { throw new UnsupportedOperationException("writeAsciiStream"); } public void writeBigDecimal(BigDecimal value) throws SQLException { this.writeString(value.toString()); } public void writeBinaryStream(InputStream value) throws SQLException { byte[] buf = new byte[1024]; ByteArrayOutputStream bld = new ByteArrayOutputStream(); int count; try { while((count = value.read(buf)) > 0) bld.write(buf, 0, count); this.writeBytes(bld.toByteArray()); } catch(IOException e) { throw new SQLException(e.getMessage()); } } public void writeBlob(Blob value) throws SQLException { throw new UnsupportedOperationException("writeBlob"); } public void writeBoolean(boolean value) throws SQLException { this.write(value ? 1 : 0); } public void writeByte(byte value) throws SQLException { this.write(value); } public void writeBytes(byte[] buffer) throws SQLException { int len = buffer.length; if(len > 0) { if(len > 65535) throw new SQLException("Byte buffer exceeds maximum size of 65535 bytes"); synchronized(Backend.THREADLOCK) { if(m_handle == 0) throw new SQLException("Stream is closed"); s_byteBuffer[0] = (byte)((len >> 8) & 0xff); s_byteBuffer[1] = (byte)(len & 0xff); _writeBytes(m_handle, s_byteBuffer, 2); _writeBytes(m_handle, buffer, len); } } } public void writeCharacterStream(Reader value) throws SQLException { char[] buf = new char[1024]; StringWriter bld = new StringWriter(); int count; try { while((count = value.read(buf)) > 0) bld.write(buf, 0, count); this.writeString(bld.toString()); } catch(IOException e) { throw new SQLException(e.getMessage()); } } public void writeClob(Clob value) throws SQLException { throw new UnsupportedOperationException("writeClob"); } public void writeDate(Date value) throws SQLException { this.writeLong(value.getTime()); } public void writeDouble(double value) throws SQLException { this.writeLong(Double.doubleToLongBits(value)); } public void writeFloat(float value) throws SQLException { this.writeInt(Float.floatToIntBits(value)); } public void writeInt(int value) throws SQLException { synchronized(Backend.THREADLOCK) { s_byteBuffer[0] = (byte)(value >>> 24); s_byteBuffer[1] = (byte)(value >>> 16); s_byteBuffer[2] = (byte)(value >>> 8); s_byteBuffer[3] = (byte)(value >>> 0); _writeBytes(m_handle, s_byteBuffer, 4); } } public void writeLong(long value) throws SQLException { synchronized(Backend.THREADLOCK) { s_byteBuffer[0] = (byte)(value >>> 56); s_byteBuffer[1] = (byte)(value >>> 48); s_byteBuffer[2] = (byte)(value >>> 40); s_byteBuffer[3] = (byte)(value >>> 32); s_byteBuffer[4] = (byte)(value >>> 24); s_byteBuffer[5] = (byte)(value >>> 16); s_byteBuffer[6] = (byte)(value >>> 8); s_byteBuffer[7] = (byte)(value >>> 0); _writeBytes(m_handle, s_byteBuffer, 8); } } public void writeObject(SQLData value) throws SQLException { throw new UnsupportedOperationException("writeObject"); } public void writeRef(Ref value) throws SQLException { throw new UnsupportedOperationException("writeRef"); } public void writeShort(short value) throws SQLException { synchronized(Backend.THREADLOCK) { s_byteBuffer[0] = (byte)(value >>> 8); s_byteBuffer[1] = (byte)(value >>> 0); _writeBytes(m_handle, s_byteBuffer, 2); } } public void writeString(String value) throws SQLException { try { this.writeBytes(value.getBytes("UTF8")); } catch(UnsupportedEncodingException e) { throw new SQLException("UTF8 encoding not supported by JVM"); } } public void writeStruct(Struct value) throws SQLException { throw new UnsupportedOperationException("writeStruct"); } public void writeTime(Time value) throws SQLException { this.writeLong(value.getTime()); } public void writeTimestamp(Timestamp value) throws SQLException { this.writeLong(value.getTime()); } public void writeURL(URL value) throws SQLException { this.writeString(value.toString()); } void close() { m_handle = 0; } private void write(int b) throws SQLException { synchronized(Backend.THREADLOCK) { if(m_handle == 0) throw new SQLException("Stream is closed"); _writeByte(m_handle, b); } } private static native void _writeByte(long handle, int theByte); private static native void _writeBytes(long handle, byte[] src, int len); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SingleRowReader.java0000644000014500000120000000671111634451404026165 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, read-only ResultSet, specially made for functions and * procedures that takes complex types as arguments (PostgreSQL 7.5 * and later). * * @author Thomas Hallgren */ public class SingleRowReader extends SingleRowResultSet { private final TupleDesc m_tupleDesc; private final long m_pointer; public SingleRowReader(long pointer, TupleDesc tupleDesc) throws SQLException { m_pointer = pointer; m_tupleDesc = tupleDesc; } public void close() { } public void finalize() { synchronized(Backend.THREADLOCK) { _free(m_pointer); } } protected Object getObjectValue(int columnIndex) throws SQLException { synchronized(Backend.THREADLOCK) { return _getObject(m_pointer, m_tupleDesc.getNativePointer(), columnIndex); } } /** * Returns {@link ResultSet#CONCUR_READ_ONLY}. */ public int getConcurrency() throws SQLException { return ResultSet.CONCUR_READ_ONLY; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void cancelRowUpdates() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void deleteRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void insertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void moveToInsertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateRow() throws SQLException { throw readOnlyException(); } /** * Always returns false. */ public boolean rowUpdated() throws SQLException { return false; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x) throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw readOnlyException(); } private static SQLException readOnlyException() { return new UnsupportedFeatureException("ResultSet is read-only"); } protected final TupleDesc getTupleDesc() { return m_tupleDesc; } protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SQLInputFromChunk.java0000644000014500000120000001454411634451404026430 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.Time; import java.sql.Timestamp; import org.postgresql.pljava.internal.Backend; /** * The SQLInputToChunk uses JNI to read from memory that has been allocated by * the PostgreSQL backend. A user should never make an attempt to create an * instance of this class. Only internal JNI routines can do that. An instance * is propagated in a call from the internal JNI layer to the Java layer will * only survive during that single call. The handle of the instance will be * invalidated when the call returns and subsequent use of the instance will * yield a SQLException with the message "Stream is closed". * * @author Thomas Hallgren */ public class SQLInputFromChunk implements SQLInput { private static final byte[] s_byteBuffer = new byte[8]; private final int m_chunkSize; private int m_position; private long m_handle; public SQLInputFromChunk(long handle, int chunkSize) { m_handle = handle; m_chunkSize = chunkSize; m_position = 0; } public Array readArray() throws SQLException { throw new UnsupportedOperationException("readArray"); } public InputStream readAsciiStream() throws SQLException { throw new UnsupportedOperationException("readAsciiStream"); } public BigDecimal readBigDecimal() throws SQLException { return new BigDecimal(this.readString()); } public InputStream readBinaryStream() throws SQLException { return new ByteArrayInputStream(this.readBytes()); } public Blob readBlob() throws SQLException { throw new UnsupportedOperationException("readBlob"); } public boolean readBoolean() throws SQLException { int c = this.read(); if(c < 0) throw new SQLException("Unexpected EOF on data input"); return c != 0; } public byte readByte() throws SQLException { int c = this.read(); if(c < 0) throw new SQLException("Unexpected EOF on data input"); return (byte)c; } public byte[] readBytes() throws SQLException { synchronized(Backend.THREADLOCK) { if(m_handle == 0) throw new SQLException("Stream is closed"); if(m_chunkSize - m_position < 2) throw new SQLException("Unexpected EOF on data input"); _readBytes(m_handle, m_position, s_byteBuffer, 2); m_position += 2; int len = ((s_byteBuffer[0] & 0xff) << 8) | (s_byteBuffer[1] & 0xff); byte[] buffer = new byte[len]; if(len > 0) { _readBytes(m_handle, m_position, buffer, len); m_position += len; } return buffer; } } public Reader readCharacterStream() throws SQLException { return new StringReader(this.readString()); } public Clob readClob() throws SQLException { throw new UnsupportedOperationException("readClob"); } public Date readDate() throws SQLException { return new Date(this.readLong()); } public double readDouble() throws SQLException { return Double.longBitsToDouble(this.readLong()); } public float readFloat() throws SQLException { return Float.intBitsToFloat(readInt()); } public int readInt() throws SQLException { synchronized(Backend.THREADLOCK) { if(m_chunkSize - m_position < 4) throw new SQLException("Unexpected EOF on data input"); _readBytes(m_handle, m_position, s_byteBuffer, 4); m_position += 4; return ((s_byteBuffer[0] & 0xff) << 24) | ((s_byteBuffer[1] & 0xff) << 16) | ((s_byteBuffer[2] & 0xff) << 8) | (s_byteBuffer[3] & 0xff); } } public long readLong() throws SQLException { synchronized(Backend.THREADLOCK) { if(m_chunkSize - m_position < 8) throw new SQLException("Unexpected EOF on data input"); _readBytes(m_handle, m_position, s_byteBuffer, 8); m_position += 8; return ((long)(s_byteBuffer[0] & 0xff) << 56) | ((long)(s_byteBuffer[1] & 0xff) << 48) | ((long)(s_byteBuffer[2] & 0xff) << 40) | ((long)(s_byteBuffer[3] & 0xff) << 32) | ((long)(s_byteBuffer[4] & 0xff) << 24) | ((s_byteBuffer[5] & 0xff) << 16) | ((s_byteBuffer[6] & 0xff) << 8) | (s_byteBuffer[7] & 0xff); } } public Object readObject() throws SQLException { throw new UnsupportedOperationException("readObject"); } public Ref readRef() throws SQLException { throw new UnsupportedOperationException("readRef"); } public short readShort() throws SQLException { synchronized(Backend.THREADLOCK) { if(m_chunkSize - m_position < 2) throw new SQLException("Unexpected EOF on data input"); _readBytes(m_handle, m_position, s_byteBuffer, 2); m_position += 2; return (short)(((s_byteBuffer[0] & 0xff) << 8) | (s_byteBuffer[1] & 0xff)); } } public String readString() throws SQLException { try { return new String(this.readBytes(), "UTF8"); } catch(UnsupportedEncodingException e) { throw new SQLException("UTF8 encoding not supported by JVM"); } } public Time readTime() throws SQLException { return new Time(this.readLong()); } public Timestamp readTimestamp() throws SQLException { return new Timestamp(this.readLong()); } public URL readURL() throws SQLException { try { return new URL(this.readString()); } catch(MalformedURLException e) { throw new SQLException(e.getMessage()); } } public boolean wasNull() throws SQLException { return false; } void close() { m_handle = 0; } private int read() throws SQLException { if(m_position < m_chunkSize) { synchronized(Backend.THREADLOCK) { if(m_handle == 0) throw new SQLException("Stream is closed"); return _readByte(m_handle, m_position++); } } return -1; } private static native int _readByte(long handle, int position); private static native void _readBytes(long handle, int position, byte[] dest, int len); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIParameterMetaData.java0000644000014500000120000000410311634451404027017 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ParameterMetaData; import java.sql.SQLException; /** * * @author Thomas Hallgren */ public class SPIParameterMetaData implements ParameterMetaData { private final int[] m_sqlTypes; SPIParameterMetaData(int[] sqlTypes) { m_sqlTypes = sqlTypes; } public int getParameterCount() throws SQLException { return m_sqlTypes == null ? 0 : m_sqlTypes.length; } public int isNullable(int arg0) throws SQLException { return parameterNullableUnknown; } public boolean isSigned(int arg0) throws SQLException { return true; } public int getPrecision(int arg0) throws SQLException { return 0; } public int getScale(int arg0) throws SQLException { return 0; } public int getParameterType(int paramIndex) throws SQLException { if(paramIndex < 1 || paramIndex > this.getParameterCount()) throw new SQLException("Parameter index out of range"); return m_sqlTypes[paramIndex-1]; } /** * This feature is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public String getParameterTypeName(int arg0) throws SQLException { throw new UnsupportedFeatureException("Parameter type name support not yet implemented"); } /** * This feature is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public String getParameterClassName(int arg0) throws SQLException { throw new UnsupportedFeatureException("Parameter class name support not yet implemented"); } /** * Returns {@link ParameterMetaData#parameterModeIn} always since this * is the only supported type at this time. */ public int getParameterMode(int paramIndex) throws SQLException { if(paramIndex < 1 || paramIndex > this.getParameterCount()) throw new SQLException("Parameter index out of range"); return parameterModeIn; } }pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSetMetaData.java0000644000014500000120000003017111634451404030145 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Connection; import java.sql.DriverManager; import org.postgresql.pljava.internal.Oid; /** * Implementation of ResultSetMetaData for SyntheticResultSet * * @author Filip Hrbek */ public abstract class AbstractResultSetMetaData implements ResultSetMetaData { private Connection m_conn; /** * Constructor. */ public AbstractResultSetMetaData() { m_conn = null; } /** * Returns the number of columns in this ResultSet object. * * @return the number of columns * @exception SQLException if a database access error occurs */ public abstract int getColumnCount() throws SQLException; /** * Indicates whether the designated column is automatically numbered, thus read-only. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public abstract boolean isAutoIncrement(int column) throws SQLException; /** * Indicates whether a column's case matters. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isCaseSensitive(int column) throws SQLException { checkColumnIndex(column); Oid oid = this.getOid(column); return ( oid.equals(TypeOid.TEXT) || oid.equals(TypeOid.BYTEA) || oid.equals(TypeOid.VARCHAR) || oid.equals(TypeOid.BPCHAR)); } /** * Indicates whether the designated column can be used in a where clause. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isSearchable(int column) throws SQLException { checkColumnIndex(column); return true; } /** * Indicates whether the designated column is a cash value. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isCurrency(int column) throws SQLException { checkColumnIndex(column); //For now it is false; it can be changed when TypeOid.java //contains currency data types. return false; } /** * Indicates the nullability of values in the designated column. * * @param column the first column is 1, the second is 2, ... * @return the nullability status of the given column; one of columnNoNulls, * columnNullable or columnNullableUnknown * @exception SQLException if a database access error occurs */ public final int isNullable(int column) throws SQLException { checkColumnIndex(column); return columnNullableUnknown; } /** * Indicates whether values in the designated column are signed numbers. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isSigned(int column) throws SQLException { checkColumnIndex(column); Oid oid = this.getOid(column); return ( oid.equals(TypeOid.INT2) || oid.equals(TypeOid.INT4) || oid.equals(TypeOid.INT8) || oid.equals(TypeOid.FLOAT4) || oid.equals(TypeOid.FLOAT8)); } /** * Indicates the designated column's normal maximum width in characters. * * @param column the first column is 1, the second is 2, ... * @return the normal maximum number of characters allowed as the width * of the designated column * @exception SQLException if a database access error occurs */ public final int getColumnDisplaySize(int column) throws SQLException { checkColumnIndex(column); Oid oid = this.getOid(column); if(oid.equals(TypeOid.INT2)) return 6; if(oid.equals(TypeOid.INT4) || oid.equals(TypeOid.FLOAT4)) return 11; if(oid.equals(TypeOid.INT8) || oid.equals(TypeOid.NUMERIC) || oid.equals(TypeOid.FLOAT8) || oid.equals(TypeOid.OID)) return 20; if(oid.equals(TypeOid.BOOL)) return 3; if(oid.equals(TypeOid.DATE)) return 13; if(oid.equals(TypeOid.TIME)) return 10; if(oid.equals(TypeOid.TIMESTAMP) || oid.equals(TypeOid.TIMESTAMPTZ)) return 25; return getFieldLength(column); } /** * Gets the designated column's suggested title for use in printouts and * displays. * * @param column the first column is 1, the second is 2, ... * @return the suggested column title * @exception SQLException if a database access error occurs */ public abstract String getColumnLabel(int column) throws SQLException; /** * Get the designated column's name. * * @param column the first column is 1, the second is 2, ... * @return column name * @exception SQLException if a database access error occurs */ public String getColumnName(int column) throws SQLException { checkColumnIndex(column); return getColumnLabel(column); } /** * Get the designated column's table's schema. * * @param column the first column is 1, the second is 2, ... * @return schema name or "" if not applicable * @exception SQLException if a database access error occurs */ public final String getSchemaName(int column) throws SQLException { checkColumnIndex(column); return ""; } /** * Get the designated column's number of decimal digits. * * @param column the first column is 1, the second is 2, ... * @return precision * @exception SQLException if a database access error occurs */ public final int getPrecision(int column) throws SQLException { checkColumnIndex(column); Oid oid = this.getOid(column); if(oid.equals(TypeOid.INT2)) return 5; if(oid.equals(TypeOid.INT4)) return 10; if(oid.equals(TypeOid.INT8) || oid.equals(TypeOid.OID)) return 20; if(oid.equals(TypeOid.FLOAT4)) return 8; if(oid.equals(TypeOid.FLOAT8)) return 16; if(oid.equals(TypeOid.BOOL)) return 1; if(oid.equals(TypeOid.NUMERIC)) return -1; return 0; } /** * Gets the designated column's number of digits to right of the decimal point. * * @param column the first column is 1, the second is 2, ... * @return scale * @exception SQLException if a database access error occurs */ public final int getScale(int column) throws SQLException { checkColumnIndex(column); Oid oid = this.getOid(column); if(oid.equals(TypeOid.FLOAT4)) return 8; if(oid.equals(TypeOid.FLOAT8)) return 16; if(oid.equals(TypeOid.NUMERIC)) return -1; return 0; } /** * Gets the designated column's table name. * * @param column the first column is 1, the second is 2, ... * @return table name or "" if not applicable * @exception SQLException if a database access error occurs */ public final String getTableName(int column) throws SQLException { checkColumnIndex(column); return ""; } /** * Gets the designated column's table's catalog name. * * @param column the first column is 1, the second is 2, ... * @return the name of the catalog for the table in which the given column * appears or "" if not applicable * @exception SQLException if a database access error occurs */ public final String getCatalogName(int column) throws SQLException { checkColumnIndex(column); return ""; } /** * Retrieves the designated column's SQL type. * * @param column the first column is 1, the second is 2, ... * @return SQL type from {@link java.sql.Types} * @exception SQLException if a database access error occurs * @see java.sql.Types */ public final int getColumnType(int column) throws SQLException { checkColumnIndex(column); return ((SPIConnection)getDefaultConnection()).getSQLType(getOid(column)); } /** * Retrieves the designated column's database-specific type name. * * @param column the first column is 1, the second is 2, ... * @return type name used by the database. If the column type is * a user-defined type, then a fully-qualified type name is returned. * @exception SQLException if a database access error occurs */ public final String getColumnTypeName(int column) throws SQLException { checkColumnIndex(column); return ((SPIConnection)getDefaultConnection()).getPGType(getOid(column)); } /** * Indicates whether the designated column is definitely not writable. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isReadOnly(int column) throws SQLException { checkColumnIndex(column); return true; } /** * Indicates whether it is possible for a write on the designated column to succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isWritable(int column) throws SQLException { checkColumnIndex(column); return false; } /** * Indicates whether a write on the designated column will definitely succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isDefinitelyWritable(int column) throws SQLException { checkColumnIndex(column); return false; } //--------------------------JDBC 2.0----------------------------------- /** *

Returns the fully-qualified name of the Java class whose instances * are manufactured if the method ResultSet.getObject * is called to retrieve a value * from the column. ResultSet.getObject may return a subclass of the * class returned by this method. * * @param column the first column is 1, the second is 2, ... * @return the fully-qualified name of the class in the Java programming * language that would be used by the method * ResultSet.getObject to retrieve the value in the specified * column. This is the class name used for custom mapping. * @exception SQLException if a database access error occurs * @since 1.2 */ public abstract String getColumnClassName(int column) throws SQLException; /** * Checks if the column index is valid. * * @param column the first column is 1, the second is 2, ... * @exception SQLException if the column is out of index bounds */ protected abstract void checkColumnIndex(int column) throws SQLException; /** * Gets column OID * @param column Column index * @return column OID * @throws SQLException if an error occurs */ protected abstract Oid getOid(int column) throws SQLException; /** * Gets column length * @param column Column index * @return column length * @throws SQLException if an error occurs */ protected abstract int getFieldLength(int column) throws SQLException; /** * Obtains connection to the database if needed. * Once called, it stores the connection reference to a private variable. */ private Connection getDefaultConnection() throws SQLException { if (m_conn == null) { m_conn = DriverManager.getConnection("jdbc:default:connection"); } return m_conn; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/ClobValue.java0000644000014500000120000001115211634451404025000 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.BufferedInputStream; import java.io.CharConversionException; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.Writer; import java.sql.Clob; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class ClobValue extends Reader implements Clob { public static int getReaderLength(Reader value) throws SQLException { try { value.mark(Integer.MAX_VALUE); long length = value.skip(Long.MAX_VALUE); if(length > Integer.MAX_VALUE) throw new SQLException("stream content too large"); value.reset(); return (int)length; } catch(IOException e) { throw new SQLException(e.getMessage()); } } private long m_markPos; private final long m_nChars; private final Reader m_reader; private long m_readerPos; public ClobValue(Reader reader, long nChars) { m_reader = reader; m_nChars = nChars; m_readerPos = 0L; m_markPos = 0L; } public ClobValue(String value) { this(new StringReader(value), value.length()); } public void close() throws IOException { m_reader.close(); m_readerPos = 0; m_markPos = 0; } public InputStream getAsciiStream() { return new BufferedInputStream(new InputStream() { public int read() throws IOException { int nextChar = ClobValue.this.read(); if(nextChar > 127) throw new CharConversionException( "Non ascii character in Clob data"); return nextChar; } }); } public Reader getCharacterStream() { return this; } public String getSubString(long pos, int length) throws SQLException { if(pos < 0L || length < 0) throw new IllegalArgumentException(); if(length == 0) return ""; if(pos + length > m_nChars) throw new SQLException("Attempt to read beyond end of Clob data"); long skip = pos - m_readerPos; if(skip < 0) throw new SQLException("Cannot position Clob stream backwards"); try { if(skip > 0) this.skip(skip); char[] buf = new char[length]; int nr = this.read(buf); if(nr < length) throw new SQLException("Clob data read not fulfilled"); return new String(buf); } catch(IOException e) { throw new SQLException("Error reading Blob data: " + e.getMessage()); } } public long length() { return m_nChars; } public synchronized void mark(int readLimit) throws IOException { m_reader.mark(readLimit); m_markPos = m_readerPos; } public boolean markSupported() { return m_reader.markSupported(); } /** * Not supported. */ public long position(Clob pattern, long start) { throw new UnsupportedOperationException(); } /** * In this method is not supported by ClobValue */ public long position(String pattern, long start) { throw new UnsupportedOperationException(); } public synchronized int read() throws IOException { int rs = m_reader.read(); m_readerPos++; return rs; } public synchronized int read(char[] b) throws IOException { int rs = m_reader.read(b); m_readerPos += rs; return rs; } public synchronized int read(char[] b, int off, int len) throws IOException { int rs = m_reader.read(b, off, len); m_readerPos += rs; return rs; } public synchronized boolean ready() throws IOException { return m_reader.ready(); } public synchronized void reset() throws IOException { m_reader.reset(); m_readerPos = m_markPos; } /** * In this method is not supported by ClobValue */ public OutputStream setAsciiStream(long pos) { throw new UnsupportedOperationException(); } /** * In this method is not supported by ClobValue */ public Writer setCharacterStream(long pos) { throw new UnsupportedOperationException(); } /** * In this method is not supported by ClobValue */ public int setString(long pos, String str) { throw new UnsupportedOperationException(); } /** * In this method is not supported by ClobValue */ public int setString(long pos, String str, int offset, int len) { throw new UnsupportedOperationException(); } public synchronized long skip(long nBytes) throws IOException { long skipped = m_reader.skip(nBytes); m_readerPos += skipped; return skipped; } /** * In this method is not supported by ClobValue */ public void truncate(long len) { throw new UnsupportedOperationException(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/BlobValue.java0000644000014500000120000001216211634451404025001 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.sql.Blob; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class BlobValue extends InputStream implements Blob { public static int getStreamLength(InputStream value) throws SQLException { try { value.mark(Integer.MAX_VALUE); long length = value.skip(Long.MAX_VALUE); if(length > Integer.MAX_VALUE) throw new SQLException("stream content too large"); value.reset(); return (int)length; } catch(IOException e) { throw new SQLException(e.getMessage()); } } private long m_markPos; private final long m_nBytes; private final InputStream m_stream; private long m_streamPos; public BlobValue(byte[] bytes) { this(new ByteArrayInputStream(bytes), bytes.length); } public BlobValue(InputStream stream, long nBytes) { m_stream = stream; m_nBytes = nBytes; m_streamPos = 0L; m_markPos = 0L; } //*************************************** // Implementation of java.io.InputStream //*************************************** public int available() throws IOException { return m_stream.available(); } public InputStream getBinaryStream() { return this; } public byte[] getBytes(long pos, int length) throws SQLException { if(pos < 0L || length < 0) throw new IllegalArgumentException(); if(length == 0) return new byte[0]; if(pos + length > m_nBytes) throw new SQLException("Attempt to read beyond end of Blob data"); long skip = pos - m_streamPos; if(skip < 0) throw new SQLException("Cannot position Blob stream backwards"); try { if(skip > 0) this.skip(skip); byte[] buf = new byte[length]; this.read(buf); return buf; } catch(IOException e) { throw new SQLException("Error reading Blob data: " + e.getMessage()); } } /** * Called from within... * @param buf a buffer that reflects the internally allocated bytea buffer. * This size of this buffer will be exactly the size returned by a call to * {@link #length()}. * @throws IOException */ public void getContents(ByteBuffer buf) throws IOException { int rs = 0; if(buf.hasArray()) { byte[] bytes = buf.array(); rs = m_stream.read(bytes); } else { byte[] trBuf = new byte[1024]; int br; while((br = m_stream.read(trBuf)) > 0) { buf.put(trBuf, 0, br); rs += br; } } if(rs != m_nBytes) throw new IOException("Not all bytes could be read"); m_streamPos += rs; } //*************************************** // Implementation of java.sql.Blob //*************************************** public long length() { return m_nBytes; } public synchronized void mark(int readLimit) { m_stream.mark(readLimit); m_markPos = m_streamPos; } public boolean markSupported() { return m_stream.markSupported(); } /** * Not supported. */ public long position(Blob pattern, long start) { throw new UnsupportedOperationException(); } /** * Not supported. */ public long position(byte[] pattern, long start) { throw new UnsupportedOperationException(); } public synchronized int read() throws IOException { int rs = m_stream.read(); m_streamPos++; return rs; } public synchronized int read(byte[] b) throws IOException { int rs = m_stream.read(b); m_streamPos += rs; return rs; } public synchronized int read(byte[] b, int off, int len) throws IOException { int rs = m_stream.read(b, off, len); m_streamPos += rs; return rs; } public synchronized void reset() throws IOException { m_stream.reset(); m_streamPos = m_markPos; } //************************************************************************* // Implementation of java.sql.Blob JDK 1.4 methods // // Those method are intended to provide a channel to the underlying data // storage as an alternatvie to the setBinaryStream // on the preparedStatement and are not implemented by the BlobValue. // //************************************************************************* /** * In this method is not supported by BlobValue */ public OutputStream setBinaryStream(long pos) { throw new UnsupportedOperationException(); } /** * In this method is not supported by BlobValue */ public int setBytes(long pos, byte[] bytes) { throw new UnsupportedOperationException(); } /** * In this method is not supported by BlobValue */ public int setBytes(long pos, byte[] bytes, int offset, int len) { throw new UnsupportedOperationException(); } public synchronized long skip(long nBytes) throws IOException { long skipped = m_stream.skip(nBytes); m_streamPos += skipped; return skipped; } /** * In this method is not supported by BlobValue */ public void truncate(long len) { throw new UnsupportedOperationException(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIStatement.java0000644000014500000120000002044411634451404025450 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.sql.SQLException; import java.sql.SQLWarning; import java.util.ArrayList; import org.postgresql.pljava.internal.ExecutionPlan; import org.postgresql.pljava.internal.Portal; import org.postgresql.pljava.internal.SPI; import org.postgresql.pljava.internal.SPIException; /** * * @author Thomas Hallgren */ public class SPIStatement implements Statement { private final SPIConnection m_connection; // Default settings. // private String m_cursorName = null; private int m_fetchSize = 1000; private int m_maxRows = 0; private ResultSet m_resultSet = null; private int m_updateCount = 0; private ArrayList m_batch = null; private boolean m_closed = false; public SPIStatement(SPIConnection conn) { m_connection = conn; } public void addBatch(String statement) throws SQLException { // Statements are converted to native SQL once they // are executed. // this.internalAddBatch(statement); } public void cancel() throws SQLException { } public void clearBatch() throws SQLException { m_batch = null; } public void clearWarnings() throws SQLException { } private void clear() throws SQLException { if(m_resultSet != null) // // The close will call back to the resultSetClosed method // and set the m_resultSet to null. // m_resultSet.close(); m_updateCount = -1; m_cursorName = null; m_batch = null; } public void close() throws SQLException { clear(); m_closed = true; } public boolean execute(String statement) throws SQLException { // Ensure that the last statement is cleaned up // before we re-execute // this.clear(); ExecutionPlan plan = ExecutionPlan.prepare( m_connection.nativeSQL(statement), null); int result = SPI.getResult(); if(plan == null) throw new SPIException(result); try { return this.executePlan(plan, null); } finally { try { plan.close(); } catch(Exception e) {} } } protected boolean executePlan(ExecutionPlan plan, Object[] paramValues) throws SQLException { m_updateCount = -1; m_resultSet = null; boolean isResultSet = plan.isCursorPlan(); if(isResultSet) { Portal portal = plan.cursorOpen(m_cursorName, paramValues); m_resultSet = new SPIResultSet(this, portal, m_maxRows); } else { try { plan.execute(paramValues, m_maxRows); m_updateCount = SPI.getProcessed(); } finally { SPI.freeTupTable(); } } return isResultSet; } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public boolean execute(String statement, int autoGeneratedKeys) throws SQLException { throw new UnsupportedFeatureException("Statement.execute(String,int)"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public boolean execute(String statement, int[] columnIndexes) throws SQLException { throw new UnsupportedFeatureException("Statement.execute(String,int[])"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public boolean execute(String statement, String[] columnNames) throws SQLException { throw new UnsupportedFeatureException("Statement.execute(String,String[])"); } public int[] executeBatch() throws SQLException { int numBatches = (m_batch == null) ? 0 : m_batch.size(); int[] result = new int[numBatches]; for(int idx = 0; idx < numBatches; ++idx) result[idx] = this.executeBatchEntry(m_batch.get(idx)); return result; } public ResultSet executeQuery(String statement) throws SQLException { this.execute(statement); return this.getResultSet(); } public int executeUpdate(String statement) throws SQLException { this.execute(statement); return this.getUpdateCount(); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public int executeUpdate(String statement, int autoGeneratedKeys) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public int executeUpdate(String statement, int[] columnIndexes) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public int executeUpdate(String statement, String[] columnNames) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Returns the Connection from that created this statement. * @throws SQLException if the statement is closed. */ public Connection getConnection() throws SQLException { if(m_connection == null) throw new StatementClosedException(); return m_connection; } public int getFetchDirection() throws SQLException { return ResultSet.FETCH_FORWARD; } public int getFetchSize() throws SQLException { return m_fetchSize; } public ResultSet getGeneratedKeys() throws SQLException { throw new SQLException("JDK 1.4 functionality not yet implemented"); } public int getMaxFieldSize() throws SQLException { return Integer.MAX_VALUE; } public int getMaxRows() throws SQLException { return m_maxRows; } public boolean getMoreResults() throws SQLException { return false; } public boolean getMoreResults(int current) throws SQLException { return false; } public int getQueryTimeout() throws SQLException { return 0; } public ResultSet getResultSet() throws SQLException { return m_resultSet; } public int getResultSetConcurrency() { return ResultSet.CONCUR_READ_ONLY; } public int getResultSetHoldability() throws SQLException { throw new SQLException("JDK 1.4 functionality not yet implemented"); } public int getResultSetType() { return ResultSet.TYPE_FORWARD_ONLY; } public int getUpdateCount() throws SQLException { return m_updateCount; } public SQLWarning getWarnings() throws SQLException { if (m_closed) { throw new SQLException("getWarnings: Statement is closed"); } return null; } public void setCursorName(String cursorName) throws SQLException { m_cursorName = cursorName; } public void setEscapeProcessing(boolean enable) throws SQLException { throw new UnsupportedFeatureException("Statement.setEscapeProcessing"); } /** * Only {@link ResultSet#FETCH_FORWARD} is supported. * @throws SQLException indicating that this feature is not supported * for other values on direction. */ public void setFetchDirection(int direction) throws SQLException { if(direction != ResultSet.FETCH_FORWARD) throw new UnsupportedFeatureException("Non forward fetch direction"); } public void setFetchSize(int size) throws SQLException { m_fetchSize = size; } public void setMaxFieldSize(int size) throws SQLException { throw new UnsupportedFeatureException("Statement.setMaxFieldSize"); } public void setMaxRows(int rows) throws SQLException { m_maxRows = rows; } public void setQueryTimeout(int seconds) throws SQLException { throw new UnsupportedFeatureException("Statement.setQueryTimeout"); } protected void internalAddBatch(Object batch) throws SQLException { if(m_batch == null) m_batch = new ArrayList(); m_batch.add(batch); } protected int executeBatchEntry(Object batchEntry) throws SQLException { int ret = SUCCESS_NO_INFO; if(this.execute(m_connection.nativeSQL((String)batchEntry))) this.getResultSet().close(); else if(m_updateCount >= 0) ret = m_updateCount; return ret; } void resultSetClosed(ResultSet rs) { if(rs == m_resultSet) m_resultSet = null; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/TriggerResultSet.java0000644000014500000120000000650611634451403026410 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import java.util.ArrayList; import org.postgresql.pljava.internal.Tuple; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, updateable ResultSet specially made for triggers. The * changes made to this ResultSet are remembered and converted to a * SPI_modify_tuple call prior to function return. * * @author Thomas Hallgren */ public class TriggerResultSet extends SingleRowResultSet { private ArrayList m_tupleChanges; private final TupleDesc m_tupleDesc; private final Tuple m_tuple; private final boolean m_readOnly; public TriggerResultSet(TupleDesc tupleDesc, Tuple tuple, boolean readOnly) throws SQLException { m_tupleDesc = tupleDesc; m_tuple = tuple; m_readOnly = readOnly; } /** * Cancel all changes made to the Tuple. */ public void cancelRowUpdates() throws SQLException { m_tupleChanges = null; } /** * Cancels all changes but doesn't really close the set. */ public void close() throws SQLException { m_tupleChanges = null; } /** * Returns the concurrency for this ResultSet. * @see java.sql.ResultSet#getConcurrency */ public int getConcurrency() throws SQLException { return m_readOnly ? CONCUR_READ_ONLY : CONCUR_UPDATABLE; } /** * Returns true if this row has been updated. */ public boolean rowUpdated() throws SQLException { return m_tupleChanges != null; } /** * Store this change for later use */ public void updateObject(int columnIndex, Object x) throws SQLException { if(m_readOnly) throw new UnsupportedFeatureException("ResultSet is read-only"); if(m_tupleChanges == null) m_tupleChanges = new ArrayList(); m_tupleChanges.add(new Integer(columnIndex)); m_tupleChanges.add(x); } /** * Return a 2 element array describing the changes that has been made to * the contained Tuple. The first element is an int[] containing * the index of each changed value. The second element is an Object[] * with containing the corresponding values. * * @return The 2 element array or null if no change has been made. */ public Object[] getChangeIndexesAndValues() { ArrayList changes = m_tupleChanges; if(changes == null) return null; int top = changes.size(); if(changes.size() == 0) return null; top /= 2; int[] indexes = new int[top]; Object[] values = new Object[top]; int vIdx = 0; for(int idx = 0; idx < top; ++idx) { indexes[idx] = ((Integer)changes.get(vIdx++)).intValue(); values[idx] = changes.get(vIdx++); } return new Object[] { m_tuple, indexes, values }; } protected Object getObjectValue(int columnIndex) throws SQLException { // Check if this value has been changed. // ArrayList changes = m_tupleChanges; if(changes != null) { int top = changes.size(); for(int idx = 0; idx < top; idx += 2) if(columnIndex == ((Integer)changes.get(idx)).intValue()) return changes.get(idx + 1); } return m_tuple.getObject(this.getTupleDesc(), columnIndex); } protected final TupleDesc getTupleDesc() { return m_tupleDesc; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SyntheticResultSet.java0000644000014500000120000000540611634451404026756 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.HashMap; /** * A Synthetic ResultSet that provides direct access to data stored * in a {@link java.util.ArrayList}. This kind of ResultSet has nothing * common with any statement. * * @author Filip Hrbek */ public class SyntheticResultSet extends ResultSetBase { private final ResultSetField[] m_fields; private final ArrayList m_tuples; private final HashMap m_fieldIndexes; SyntheticResultSet(ResultSetField[] fields, ArrayList tuples) throws SQLException { super(tuples.size()); m_fields = fields; m_tuples = tuples; m_fieldIndexes = new HashMap(); int i = m_fields.length; while(--i >= 0) m_fieldIndexes.put(m_fields[i].getColumnLabel(), new Integer(i+1)); Object[][] tupleTest = (Object[][]) m_tuples.toArray(new Object[0][]); Object value; for (i=0; i < tupleTest.length; i++) { int j = m_fields.length; while(--j >= 0) { value = tupleTest[i][j]; if (value != null && !m_fields[j].canContain(value.getClass())) { throw new SQLException( "Unable to store class " + value.getClass() + " in ResultSetField '" + m_fields[j].getColumnLabel() + "'" + " with OID " + m_fields[j].getOID() + " (expected class: " + m_fields[j].getJavaClass() + ")"); } } } } public void close() throws SQLException { m_tuples.clear(); super.close(); } public int findColumn(String columnName) throws SQLException { Integer idx = (Integer)m_fieldIndexes.get(columnName.toUpperCase()); if(idx != null) { return idx.intValue(); } throw new SQLException("No such field: '" + columnName + "'"); } protected Object getObjectValue(int columnIndex) throws SQLException { return getCurrentRow()[columnIndex-1]; } protected final Object[] getCurrentRow() throws SQLException { int row = this.getRow(); if(row < 1 || row > m_tuples.size()) throw new SQLException("ResultSet is not positioned on a valid row"); return (Object[])m_tuples.get(row-1); } public boolean isLast() throws SQLException { return this.getRow() == m_tuples.size(); } public boolean next() throws SQLException { int row = this.getRow(); if(row < m_tuples.size()) { this.setRow(row+1); return true; } return false; } public ResultSetMetaData getMetaData() throws SQLException { return new SyntheticResultSetMetaData(m_fields); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java0000644000014500000120000036543211634451403026621 0ustar johannstaffpackage org.postgresql.pljava.jdbc; /** * @author Filip Hrbek */ import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import org.postgresql.pljava.internal.AclId; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.Oid; public class SPIDatabaseMetaData implements DatabaseMetaData { public SPIDatabaseMetaData(SPIConnection conn) { m_connection = conn; } private static final String KEYWORDS = "abort,acl,add,aggregate,append,archive," + "arch_store,backward,binary,boolean,change,cluster," + "copy,database,delimiter,delimiters,do,extend," + "explain,forward,heavy,index,inherits,isnull," + "light,listen,load,merge,nothing,notify," + "notnull,oids,purge,rename,replace,retrieve," + "returns,rule,recipe,setof,stdin,stdout,store," + "vacuum,verbose,version"; private final SPIConnection m_connection; // The connection association private static final int VARHDRSZ = 4; // length for int4 private int NAMEDATALEN = 0; // length for name datatype private int INDEX_MAX_KEYS = 0; // maximum number of keys in an index. protected int getMaxIndexKeys() throws SQLException { if(INDEX_MAX_KEYS == 0) INDEX_MAX_KEYS = Integer.parseInt(Backend.getConfigOption("max_index_keys")); return INDEX_MAX_KEYS; } protected int getMaxNameLength() throws SQLException { if(NAMEDATALEN == 0) { String sql = "SELECT t.typlen FROM pg_catalog.pg_type t, pg_catalog.pg_namespace n" + " WHERE t.typnamespace=n.oid" + " AND t.typname='name'" + " AND n.nspname='pg_catalog'"; ResultSet rs = m_connection.createStatement().executeQuery(sql); if(!rs.next()){ throw new SQLException( "Unable to find name datatype in the system catalogs."); } NAMEDATALEN = rs.getInt("typlen"); rs.close(); } return NAMEDATALEN - 1; } /* * Can all the procedures returned by getProcedures be called by the current * user? @return true if so @exception SQLException if a database access * error occurs */ public boolean allProceduresAreCallable() throws SQLException { return true; // For now... } /* * Can all the tables returned by getTable be SELECTed by the current user? * @return true if so @exception SQLException if a database access error * occurs */ public boolean allTablesAreSelectable() throws SQLException { return true; // For now... } /* * What is the URL for this database? @return the url or null if it cannott * be generated @exception SQLException if a database access error occurs */ public String getURL() throws SQLException { return "jdbc:default:connection"; } /* * What is our user name as known to the database? @return our database user * name @exception SQLException if a database access error occurs */ public String getUserName() throws SQLException { return AclId.getUser().getName(); } /* * Is the database in read-only mode? @return true if so @exception * SQLException if a database access error occurs */ public boolean isReadOnly() throws SQLException { return m_connection.isReadOnly(); } /* * Are NULL values sorted high? @return true if so @exception SQLException * if a database access error occurs */ public boolean nullsAreSortedHigh() throws SQLException { return true; } /* * Are NULL values sorted low? @return true if so @exception SQLException if * a database access error occurs */ public boolean nullsAreSortedLow() throws SQLException { return false; } /* * Are NULL values sorted at the start regardless of sort order? @return * true if so @exception SQLException if a database access error occurs */ public boolean nullsAreSortedAtStart() throws SQLException { return false; } /* * Are NULL values sorted at the end regardless of sort order? @return true * if so @exception SQLException if a database access error occurs */ public boolean nullsAreSortedAtEnd() throws SQLException { return false; } /* * What is the name of this database product - we hope that it is * PostgreSQL, so we return that explicitly. @return the database product * name @exception SQLException if a database access error occurs */ public String getDatabaseProductName() throws SQLException { return "PostgreSQL"; } /* * What is the version of this database product. @return the database * version @exception SQLException if a database access error occurs */ public String getDatabaseProductVersion() throws SQLException { int[] ver = m_connection.getVersionNumber(); return ver[0] + "." + ver[1] + "." + ver[2]; } /* * What is the name of this JDBC driver? If we don't know this we are doing * something wrong! @return the JDBC driver name @exception SQLException * why? */ public String getDriverName() throws SQLException { return "PostgreSQL pljava SPI Driver"; } /* * What is the version string of this JDBC driver? Again, this is static. * @return the JDBC driver name. @exception SQLException why? */ public String getDriverVersion() throws SQLException { SPIDriver d = new SPIDriver(); return d.getMajorVersion() + "." + d.getMinorVersion(); } /* * What is this JDBC driver's major version number? @return the JDBC driver * major version */ public int getDriverMajorVersion() { return new SPIDriver().getMajorVersion(); } /* * What is this JDBC driver's minor version number? @return the JDBC driver * minor version */ public int getDriverMinorVersion() { return new SPIDriver().getMinorVersion(); } /* * Does the database store tables in a local file? No - it stores them in a * file on the server. @return true if so @exception SQLException if a * database access error occurs */ public boolean usesLocalFiles() throws SQLException { return false; } /* * Does the database use a file for each table? Well, not really, since it * doesnt use local files. @return true if so @exception SQLException if a * database access error occurs */ public boolean usesLocalFilePerTable() throws SQLException { return false; } /* * Does the database treat mixed case unquoted SQL identifiers as case * sensitive and as a result store them in mixed case? A JDBC-Compliant * driver will always return false. @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsMixedCaseIdentifiers() throws SQLException { return false; } /* * Does the database treat mixed case unquoted SQL identifiers as case * insensitive and store them in upper case? @return true if so */ public boolean storesUpperCaseIdentifiers() throws SQLException { return false; } /* * Does the database treat mixed case unquoted SQL identifiers as case * insensitive and store them in lower case? @return true if so */ public boolean storesLowerCaseIdentifiers() throws SQLException { return true; } /* * Does the database treat mixed case unquoted SQL identifiers as case * insensitive and store them in mixed case? @return true if so */ public boolean storesMixedCaseIdentifiers() throws SQLException { return false; } /* * Does the database treat mixed case quoted SQL identifiers as case * sensitive and as a result store them in mixed case? A JDBC compliant * driver will always return true. @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { return true; } /* * Does the database treat mixed case quoted SQL identifiers as case * insensitive and store them in upper case? @return true if so */ public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { return false; } /* * Does the database treat mixed case quoted SQL identifiers as case * insensitive and store them in lower case? @return true if so */ public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { return false; } /* * Does the database treat mixed case quoted SQL identifiers as case * insensitive and store them in mixed case? @return true if so */ public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { return false; } /* * What is the string used to quote SQL identifiers? This returns a space if * identifier quoting isn't supported. A JDBC Compliant driver will always * use a double quote character. @return the quoting string @exception * SQLException if a database access error occurs */ public String getIdentifierQuoteString() throws SQLException { return "\""; } /* * Get a comma separated list of all a database's SQL keywords that are NOT * also SQL92 keywords.

Within PostgreSQL, the keywords are found in * src/backend/parser/keywords.c

For SQL Keywords, I took the list * provided at * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt which is for * SQL3, not SQL-92, but it is close enough for this purpose. @return a * comma separated list of keywords we use @exception SQLException if a * database access error occurs */ public String getSQLKeywords() throws SQLException { return KEYWORDS; } /** * get supported escaped numeric functions * * @return a comma separated list of function names */ public String getNumericFunctions() throws SQLException { return BuiltinFunctions.ABS + ',' + BuiltinFunctions.ACOS + ',' + BuiltinFunctions.ASIN + ',' + BuiltinFunctions.ATAN + ',' + BuiltinFunctions.ATAN2 + ',' + BuiltinFunctions.CEILING + ',' + BuiltinFunctions.COS + ',' + BuiltinFunctions.COT + ',' + BuiltinFunctions.DEGREES + ',' + BuiltinFunctions.EXP + ',' + BuiltinFunctions.FLOOR + ',' + BuiltinFunctions.LOG + ',' + BuiltinFunctions.LOG10 + ',' + BuiltinFunctions.MOD + ',' + BuiltinFunctions.PI + ',' + BuiltinFunctions.POWER + ',' + BuiltinFunctions.RADIANS + ',' + BuiltinFunctions.RAND + ',' + BuiltinFunctions.ROUND + ',' + BuiltinFunctions.SIGN + ',' + BuiltinFunctions.SIN + ',' + BuiltinFunctions.SQRT + ',' + BuiltinFunctions.TAN + ',' + BuiltinFunctions.TRUNCATE; } public String getStringFunctions() throws SQLException { String funcs = BuiltinFunctions.ASCII + ',' + BuiltinFunctions.CHAR + ',' + BuiltinFunctions.CONCAT + ',' + BuiltinFunctions.LCASE + ',' + BuiltinFunctions.LEFT + ',' + BuiltinFunctions.LENGTH + ',' + BuiltinFunctions.LTRIM + ',' + BuiltinFunctions.REPEAT + ',' + BuiltinFunctions.RTRIM + ',' + BuiltinFunctions.SPACE + ',' + BuiltinFunctions.SUBSTRING + ',' + BuiltinFunctions.UCASE + ',' + BuiltinFunctions.REPLACE; // Currently these don't work correctly with parameterized // arguments, so leave them out. They reorder the arguments // when rewriting the query, but no translation layer is provided, // so a setObject(N, obj) will not go to the correct parameter. // ','+BuiltinFunctions.INSERT+','+BuiltinFunctions.LOCATE+ // ','+BuiltinFunctions.RIGHT+ return funcs; } public String getSystemFunctions() throws SQLException { return BuiltinFunctions.DATABASE + ',' + BuiltinFunctions.IFNULL + ',' + BuiltinFunctions.USER; } public String getTimeDateFunctions() throws SQLException { return BuiltinFunctions.CURDATE + ',' + BuiltinFunctions.CURTIME + ',' + BuiltinFunctions.DAYNAME + ',' + BuiltinFunctions.DAYOFMONTH + ',' + BuiltinFunctions.DAYOFWEEK + ',' + BuiltinFunctions.DAYOFYEAR + ',' + BuiltinFunctions.HOUR + ',' + BuiltinFunctions.MINUTE + ',' + BuiltinFunctions.MONTH + ',' + BuiltinFunctions.MONTHNAME + ',' + BuiltinFunctions.NOW + ',' + BuiltinFunctions.QUARTER + ',' + BuiltinFunctions.SECOND + ',' + BuiltinFunctions.WEEK + ',' + BuiltinFunctions.YEAR; } /* * This is the string that can be used to escape '_' and '%' in a search * string pattern style catalog search parameters @return the string used to * escape wildcard characters @exception SQLException if a database access * error occurs */ public String getSearchStringEscape() throws SQLException { // Java's parse takes off two backslashes // and then pg's input parser takes off another layer // so we need many backslashes here. // // This would work differently if you used a PreparedStatement // and " mycol LIKE ? " which using the V3 protocol would skip // pg's input parser, but I don't know what we can do about that. // return "\\"; } /* * Get all the "extra" characters that can be used in unquoted identifier * names (those beyond a-zA-Z0-9 and _)

From the file * src/backend/parser/scan.l, an identifier is {letter}{letter_or_digit} * which makes it just those listed above. @return a string containing the * extra characters @exception SQLException if a database access error * occurs */ public String getExtraNameCharacters() throws SQLException { return ""; } /* * Is "ALTER TABLE" with an add column supported? Yes for PostgreSQL 6.1 * @return true if so @exception SQLException if a database access error * occurs */ public boolean supportsAlterTableWithAddColumn() throws SQLException { return true; } /* * Is "ALTER TABLE" with a drop column supported? @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsAlterTableWithDropColumn() throws SQLException { return true; } /* * Is column aliasing supported?

If so, the SQL AS clause can be used to * provide names for computed columns or to provide alias names for columns * as required. A JDBC Compliant driver always returns true.

e.g.

	 * select count(C) as C_COUNT from T group by C; 

should return a * column named as C_COUNT instead of count(C) @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsColumnAliasing() throws SQLException { return true; } /* * Are concatenations between NULL and non-NULL values NULL? A JDBC * Compliant driver always returns true @return true if so @exception * SQLException if a database access error occurs */ public boolean nullPlusNonNullIsNull() throws SQLException { return true; } public boolean supportsConvert() throws SQLException { return false; } public boolean supportsConvert(int fromType, int toType) throws SQLException { return false; } /* * Are table correlation names supported? A JDBC Compliant driver always * returns true. @return true if so; false otherwise @exception SQLException - * if a database access error occurs */ public boolean supportsTableCorrelationNames() throws SQLException { return true; } /* * If table correlation names are supported, are they restricted to be * different from the names of the tables? @return true if so; false * otherwise @exception SQLException - if a database access error occurs */ public boolean supportsDifferentTableCorrelationNames() throws SQLException { return false; } /* * Are expressions in "ORDER BY" lists supported?
e.g. select * from t * order by a + b; @return true if so @exception SQLException if a database * access error occurs */ public boolean supportsExpressionsInOrderBy() throws SQLException { return true; } /* * Can an "ORDER BY" clause use columns not in the SELECT? @return true if * so @exception SQLException if a database access error occurs */ public boolean supportsOrderByUnrelated() throws SQLException { return true; } /* * Is some form of "GROUP BY" clause supported? I checked it, and yes it is. * @return true if so @exception SQLException if a database access error * occurs */ public boolean supportsGroupBy() throws SQLException { return true; } /* * Can a "GROUP BY" clause use columns not in the SELECT? @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsGroupByUnrelated() throws SQLException { return true; } /* * Can a "GROUP BY" clause add columns not in the SELECT provided it * specifies all the columns in the SELECT? Does anyone actually understand * what they mean here? (I think this is a subset of the previous function. -- * petere) @return true if so @exception SQLException if a database access * error occurs */ public boolean supportsGroupByBeyondSelect() throws SQLException { return true; } /* * Is the escape character in "LIKE" clauses supported? A JDBC compliant * driver always returns true. @return true if so @exception SQLException if * a database access error occurs */ public boolean supportsLikeEscapeClause() throws SQLException { return true; } /* * Are multiple ResultSets from a single execute supported? Well, I * implemented it, but I dont think this is possible from the back ends * point of view. @return true if so @exception SQLException if a database * access error occurs */ public boolean supportsMultipleResultSets() throws SQLException { return false; } /* * Can we have multiple transactions open at once (on different * connections?) I guess we can have, since Im relying on it. @return true * if so @exception SQLException if a database access error occurs */ public boolean supportsMultipleTransactions() throws SQLException { return true; } /* * Can columns be defined as non-nullable. A JDBC Compliant driver always * returns true.

This changed from false to true in v6.2 of the driver, * as this support was added to the backend. @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsNonNullableColumns() throws SQLException { return true; } /* * Does this driver support the minimum ODBC SQL grammar. This grammar is * defined at:

http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm *

In Appendix C. From this description, we seem to support the ODBC * minimal (Level 0) grammar. @return true if so @exception SQLException if * a database access error occurs */ public boolean supportsMinimumSQLGrammar() throws SQLException { return true; } /* * Does this driver support the Core ODBC SQL grammar. We need SQL-92 * conformance for this. @return true if so @exception SQLException if a * database access error occurs */ public boolean supportsCoreSQLGrammar() throws SQLException { return false; } /* * Does this driver support the Extended (Level 2) ODBC SQL grammar. We * don't conform to the Core (Level 1), so we can't conform to the Extended * SQL Grammar. @return true if so @exception SQLException if a database * access error occurs */ public boolean supportsExtendedSQLGrammar() throws SQLException { return false; } /* * Does this driver support the ANSI-92 entry level SQL grammar? All JDBC * Compliant drivers must return true. We currently report false until * 'schema' support is added. Then this should be changed to return true, * since we will be mostly compliant (probably more compliant than many * other databases) And since this is a requirement for all JDBC drivers we * need to get to the point where we can return true. @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsANSI92EntryLevelSQL() throws SQLException { return true; } /* * Does this driver support the ANSI-92 intermediate level SQL grammar? * @return true if so @exception SQLException if a database access error * occurs */ public boolean supportsANSI92IntermediateSQL() throws SQLException { return false; } /* * Does this driver support the ANSI-92 full SQL grammar? @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsANSI92FullSQL() throws SQLException { return false; } /* * Is the SQL Integrity Enhancement Facility supported? Our best guess is * that this means support for constraints @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsIntegrityEnhancementFacility() throws SQLException { return true; } /* * Is some form of outer join supported? @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsOuterJoins() throws SQLException { return true; } /* * Are full nexted outer joins supported? @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsFullOuterJoins() throws SQLException { return true; } /* * Is there limited support for outer joins? @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsLimitedOuterJoins() throws SQLException { return true; } /* * What is the database vendor's preferred term for "schema"? PostgreSQL * doesn't have schemas, but when it does, we'll use the term "schema". * @return the vendor term @exception SQLException if a database access * error occurs */ public String getSchemaTerm() throws SQLException { return "schema"; } /* * What is the database vendor's preferred term for "procedure"? * Traditionally, "function" has been used. @return the vendor term * @exception SQLException if a database access error occurs */ public String getProcedureTerm() throws SQLException { return "function"; } /* * What is the database vendor's preferred term for "catalog"? @return the * vendor term @exception SQLException if a database access error occurs */ public String getCatalogTerm() throws SQLException { return "database"; } /* * Does a catalog appear at the start of a qualified table name? (Otherwise * it appears at the end). @return true if so @exception SQLException if a * database access error occurs */ public boolean isCatalogAtStart() throws SQLException { // return true here; we return false for every other catalog function // so it won't matter what we return here D.C. return true; } /* * What is the Catalog separator. @return the catalog separator string * @exception SQLException if a database access error occurs */ public String getCatalogSeparator() throws SQLException { // Give them something to work with here // everything else returns false so it won't matter what we return here // D.C. return "."; } /* * Can a schema name be used in a data manipulation statement? @return true * if so @exception SQLException if a database access error occurs */ public boolean supportsSchemasInDataManipulation() throws SQLException { return true; } /* * Can a schema name be used in a procedure call statement? @return true if * so @exception SQLException if a database access error occurs */ public boolean supportsSchemasInProcedureCalls() throws SQLException { return true; } /* * Can a schema be used in a table definition statement? @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsSchemasInTableDefinitions() throws SQLException { return true; } /* * Can a schema name be used in an index definition statement? @return true * if so @exception SQLException if a database access error occurs */ public boolean supportsSchemasInIndexDefinitions() throws SQLException { return true; } /* * Can a schema name be used in a privilege definition statement? @return * true if so @exception SQLException if a database access error occurs */ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { return true; } /* * Can a catalog name be used in a data manipulation statement? @return true * if so @exception SQLException if a database access error occurs */ public boolean supportsCatalogsInDataManipulation() throws SQLException { return false; } /* * Can a catalog name be used in a procedure call statement? @return true if * so @exception SQLException if a database access error occurs */ public boolean supportsCatalogsInProcedureCalls() throws SQLException { return false; } /* * Can a catalog name be used in a table definition statement? @return true * if so @exception SQLException if a database access error occurs */ public boolean supportsCatalogsInTableDefinitions() throws SQLException { return false; } /* * Can a catalog name be used in an index definition? @return true if so * @exception SQLException if a database access error occurs */ public boolean supportsCatalogsInIndexDefinitions() throws SQLException { return false; } /* * Can a catalog name be used in a privilege definition statement? @return * true if so @exception SQLException if a database access error occurs */ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { return false; } /* * We support cursors for gets only it seems. I dont see a method to get a * positioned delete. @return true if so @exception SQLException if a * database access error occurs */ public boolean supportsPositionedDelete() throws SQLException { return false; // For now... } /* * Is positioned UPDATE supported? @return true if so @exception * SQLException if a database access error occurs */ public boolean supportsPositionedUpdate() throws SQLException { return false; // For now... } /* * Is SELECT for UPDATE supported? @return true if so; false otherwise * @exception SQLException - if a database access error occurs */ public boolean supportsSelectForUpdate() throws SQLException { return true; } /* * Are stored procedure calls using the stored procedure escape syntax * supported? @return true if so; false otherwise @exception SQLException - * if a database access error occurs */ public boolean supportsStoredProcedures() throws SQLException { return false; } /* * Are subqueries in comparison expressions supported? A JDBC Compliant * driver always returns true. @return true if so; false otherwise * @exception SQLException - if a database access error occurs */ public boolean supportsSubqueriesInComparisons() throws SQLException { return true; } /* * Are subqueries in 'exists' expressions supported? A JDBC Compliant driver * always returns true. @return true if so; false otherwise @exception * SQLException - if a database access error occurs */ public boolean supportsSubqueriesInExists() throws SQLException { return true; } /* * Are subqueries in 'in' statements supported? A JDBC Compliant driver * always returns true. @return true if so; false otherwise @exception * SQLException - if a database access error occurs */ public boolean supportsSubqueriesInIns() throws SQLException { return true; } /* * Are subqueries in quantified expressions supported? A JDBC Compliant * driver always returns true. (No idea what this is, but we support a good * deal of subquerying.) @return true if so; false otherwise @exception * SQLException - if a database access error occurs */ public boolean supportsSubqueriesInQuantifieds() throws SQLException { return true; } /* * Are correlated subqueries supported? A JDBC Compliant driver always * returns true. (a.k.a. subselect in from?) @return true if so; false * otherwise @exception SQLException - if a database access error occurs */ public boolean supportsCorrelatedSubqueries() throws SQLException { return true; } /* * Is SQL UNION supported? @return true if so @exception SQLException if a * database access error occurs */ public boolean supportsUnion() throws SQLException { return true; } /* * Is SQL UNION ALL supported? @return true if so @exception SQLException if * a database access error occurs */ public boolean supportsUnionAll() throws SQLException { return true; } /* * In PostgreSQL, Cursors are only open within transactions. @return true if * so @exception SQLException if a database access error occurs */ public boolean supportsOpenCursorsAcrossCommit() throws SQLException { return false; } /* * Do we support open cursors across multiple transactions? @return true if * so @exception SQLException if a database access error occurs */ public boolean supportsOpenCursorsAcrossRollback() throws SQLException { return false; } /* * Can statements remain open across commits? They may, but this driver * cannot guarentee that. In further reflection. we are talking a Statement * object here, so the answer is yes, since the Statement is only a vehicle * to ExecSQL() @return true if they always remain open; false otherwise * @exception SQLException if a database access error occurs */ public boolean supportsOpenStatementsAcrossCommit() throws SQLException { return true; } /* * Can statements remain open across rollbacks? They may, but this driver * cannot guarentee that. In further contemplation, we are talking a * Statement object here, so the answer is yes, since the Statement is only * a vehicle to ExecSQL() in Connection @return true if they always remain * open; false otherwise @exception SQLException if a database access error * occurs */ public boolean supportsOpenStatementsAcrossRollback() throws SQLException { return true; } /* * How many hex characters can you have in an inline binary literal @return * the max literal length @exception SQLException if a database access error * occurs */ public int getMaxBinaryLiteralLength() throws SQLException { return 0; // no limit } /* * What is the maximum length for a character literal I suppose it is 8190 * (8192 - 2 for the quotes) @return the max literal length @exception * SQLException if a database access error occurs */ public int getMaxCharLiteralLength() throws SQLException { return 0; // no limit } /* * Whats the limit on column name length. @return the maximum column name * length @exception SQLException if a database access error occurs */ public int getMaxColumnNameLength() throws SQLException { return getMaxNameLength(); } /* * What is the maximum number of columns in a "GROUP BY" clause? @return the * max number of columns @exception SQLException if a database access error * occurs */ public int getMaxColumnsInGroupBy() throws SQLException { return 0; // no limit } /* * What's the maximum number of columns allowed in an index? @return max * number of columns @exception SQLException if a database access error * occurs */ public int getMaxColumnsInIndex() throws SQLException { return getMaxIndexKeys(); } /* * What's the maximum number of columns in an "ORDER BY clause? @return the * max columns @exception SQLException if a database access error occurs */ public int getMaxColumnsInOrderBy() throws SQLException { return 0; // no limit } /* * What is the maximum number of columns in a "SELECT" list? @return the max * columns @exception SQLException if a database access error occurs */ public int getMaxColumnsInSelect() throws SQLException { return 0; // no limit } /* * What is the maximum number of columns in a table? From the CREATE TABLE * reference page...

"The new class is created as a heap with no initial * data. A class can have no more than 1600 attributes (realistically, this * is limited by the fact that tuple sizes must be less than 8192 bytes)..." * @return the max columns @exception SQLException if a database access * error occurs */ public int getMaxColumnsInTable() throws SQLException { return 1600; } /* * How many active connection can we have at a time to this database? Well, * since it depends on postmaster, which just does a listen() followed by an * accept() and fork(), its basically very high. Unless the system runs out * of processes, it can be 65535 (the number of aux. ports on a TCP/IP * system). I will return 8192 since that is what even the largest system * can realistically handle, @return the maximum number of connections * @exception SQLException if a database access error occurs */ public int getMaxConnections() throws SQLException { return 8192; } /* * What is the maximum cursor name length @return max cursor name length in * bytes @exception SQLException if a database access error occurs */ public int getMaxCursorNameLength() throws SQLException { return getMaxNameLength(); } /* * Retrieves the maximum number of bytes for an index, including all of the * parts of the index. @return max index length in bytes, which includes the * composite of all the constituent parts of the index; a result of zero * means that there is no limit or the limit is not known @exception * SQLException if a database access error occurs */ public int getMaxIndexLength() throws SQLException { return 0; // no limit (larger than an int anyway) } public int getMaxSchemaNameLength() throws SQLException { return getMaxNameLength(); } /* * What is the maximum length of a procedure name @return the max name * length in bytes @exception SQLException if a database access error occurs */ public int getMaxProcedureNameLength() throws SQLException { return getMaxNameLength(); } public int getMaxCatalogNameLength() throws SQLException { return getMaxNameLength(); } /* * What is the maximum length of a single row? @return max row size in bytes * @exception SQLException if a database access error occurs */ public int getMaxRowSize() throws SQLException { return 1073741824; // 1 GB } /* * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs? We don't * handle blobs yet @return true if so @exception SQLException if a database * access error occurs */ public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { return false; } /* * What is the maximum length of a SQL statement? @return max length in * bytes @exception SQLException if a database access error occurs */ public int getMaxStatementLength() throws SQLException { return 0; // actually whatever fits in size_t } /* * How many active statements can we have open at one time to this database? * Basically, since each Statement downloads the results as the query is * executed, we can have many. However, we can only really have one * statement per connection going at once (since they are executed serially) - * so we return one. @return the maximum @exception SQLException if a * database access error occurs */ public int getMaxStatements() throws SQLException { return 1; } /* * What is the maximum length of a table name @return max name length in * bytes @exception SQLException if a database access error occurs */ public int getMaxTableNameLength() throws SQLException { return getMaxNameLength(); } /* * What is the maximum number of tables that can be specified in a SELECT? * @return the maximum @exception SQLException if a database access error * occurs */ public int getMaxTablesInSelect() throws SQLException { return 0; // no limit } /* * What is the maximum length of a user name @return the max name length in * bytes @exception SQLException if a database access error occurs */ public int getMaxUserNameLength() throws SQLException { return getMaxNameLength(); } /* * What is the database's default transaction isolation level? We do not * support this, so all transactions are SERIALIZABLE. @return the default * isolation level @exception SQLException if a database access error occurs * * @see Connection */ public int getDefaultTransactionIsolation() throws SQLException { return Connection.TRANSACTION_READ_COMMITTED; } /* * Are transactions supported? If not, commit and rollback are noops and the * isolation level is TRANSACTION_NONE. We do support transactions. @return * true if transactions are supported @exception SQLException if a database * access error occurs */ public boolean supportsTransactions() throws SQLException { return true; } /* * Does the database support the given transaction isolation level? We only * support TRANSACTION_SERIALIZABLE and TRANSACTION_READ_COMMITTED @param * level the values are defined in java.sql.Connection @return true if so * @exception SQLException if a database access error occurs * * @see Connection */ public boolean supportsTransactionIsolationLevel(int level) throws SQLException { if(level == Connection.TRANSACTION_SERIALIZABLE || level == Connection.TRANSACTION_READ_COMMITTED) return true; if(this.getDatabaseMajorVersion() >= 8 && (level == Connection.TRANSACTION_READ_UNCOMMITTED || level == Connection.TRANSACTION_REPEATABLE_READ)) return true; return false; } /* * Are both data definition and data manipulation transactions supported? * @return true if so @exception SQLException if a database access error * occurs */ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { return true; } /* * Are only data manipulation statements withing a transaction supported? * @return true if so @exception SQLException if a database access error * occurs */ public boolean supportsDataManipulationTransactionsOnly() throws SQLException { return false; } /* * Does a data definition statement within a transaction force the * transaction to commit? I think this means something like:

	 * CREATE TABLE T (A INT); INSERT INTO T (A) VALUES (2); BEGIN; UPDATE T SET
	 * A = A + 1; CREATE TABLE X (A INT); SELECT A FROM T INTO X; COMMIT; 

* does the CREATE TABLE call cause a commit? The answer is no. @return true * if so @exception SQLException if a database access error occurs */ public boolean dataDefinitionCausesTransactionCommit() throws SQLException { return false; } /* * Is a data definition statement within a transaction ignored? @return true * if so @exception SQLException if a database access error occurs */ public boolean dataDefinitionIgnoredInTransactions() throws SQLException { return false; } /** * Escape single quotes with another single quote. */ private static String escapeQuotes(String s) { if (s == null) { return null; } StringBuffer sb = new StringBuffer(); int length = s.length(); char prevChar = ' '; char prevPrevChar = ' '; for(int i = 0; i < length; i++) { char c = s.charAt(i); sb.append(c); if(c == '\'' && (prevChar != '\\' || (prevChar == '\\' && prevPrevChar == '\\'))) { sb.append("'"); } prevPrevChar = prevChar; prevChar = c; } return sb.toString(); } /** * Creates a condition with the specified operator * based on schema specification:
*

    *
  • schema is specified => search in this schema only
  • *
  • schema is equal to "" => search in the 'public' schema
  • *
  • schema is null => search in all schemas
  • *
*/ private static String resolveSchemaConditionWithOperator( String expr, String schema, String operator) { //schema is null => search in current_schemas(true) if (schema == null) { //This means that only "visible" schemas are searched. //It was approved to change to *all* schemas. //return expr + " " + operator + " ANY (current_schemas(true))"; return "1=1"; } //schema is specified => search in this schema else if(!"".equals(schema)) { return expr + " " + operator + " '" + escapeQuotes(schema) + "' "; } //schema is "" => search in the 'public' schema else { return expr + " " + operator + " 'public' "; } } /** * Creates an equality condition based on schema specification:
*
    *
  • schema is specified => search in this schema only
  • *
  • schema is equal to "" => search in the 'public' schema
  • *
  • schema is null => search in all schemas
  • *
*/ private static String resolveSchemaCondition(String expr, String schema) { return resolveSchemaConditionWithOperator(expr, schema, "="); } /** * Creates a pattern condition based on schema specification:
*
    *
  • schema is specified => search in this schema only
  • *
  • schema is equal to "" => search in the 'public' schema
  • *
  • schema is null => search in all schemas
  • *
*/ private static String resolveSchemaPatternCondition( String expr, String schema) { return resolveSchemaConditionWithOperator(expr, schema, "LIKE"); } /* * Get a description of stored procedures available in a catalog

Only * procedure descriptions matching the schema and procedure name criteria * are returned. They are ordered by PROCEDURE_SCHEM and PROCEDURE_NAME

Each * procedure description has the following columns:

  1. PROCEDURE_CAT * String => procedure catalog (may be null)
  2. PROCEDURE_SCHEM * String => procedure schema (may be null)
  3. PROCEDURE_NAME * String => procedure name
  4. ResultSetField 4 reserved (make it * null)
  5. ResultSetField 5 reserved (make it null)
  6. ResultSetField * 6 reserved (make it null)
  7. REMARKS String => explanatory * comment on the procedure
  8. PROCEDURE_TYPE short => kind of * procedure
    • procedureResultUnknown - May return a result
    • * procedureNoResult - Does not return a result
    • procedureReturnsResult - * Returns a result
@param catalog - a catalog name; "" * retrieves those without a catalog; null means drop catalog name from * criteria @param schemaParrern - a schema name pattern; "" retrieves those * without a schema - we ignore this parameter @param procedureNamePattern - * a procedure name pattern @return ResultSet - each row is a procedure * description @exception SQLException if a database access error occurs */ public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { String sql = "SELECT NULL AS PROCEDURE_CAT, n.nspname AS PROCEDURE_SCHEM, p.proname AS PROCEDURE_NAME, NULL, NULL, NULL, d.description AS REMARKS, " + java.sql.DatabaseMetaData.procedureReturnsResult + " AS PROCEDURE_TYPE " + " FROM pg_catalog.pg_namespace n, pg_catalog.pg_proc p " + " LEFT JOIN pg_catalog.pg_description d ON (p.oid=d.objoid) " + " LEFT JOIN pg_catalog.pg_class c ON (d.classoid=c.oid AND c.relname='pg_proc') " + " LEFT JOIN pg_catalog.pg_namespace pn ON (c.relnamespace=pn.oid AND pn.nspname='pg_catalog') " + " WHERE p.pronamespace=n.oid " + " AND " + resolveSchemaPatternCondition( "n.nspname", schemaPattern); if(procedureNamePattern != null) { sql += " AND p.proname LIKE '" + escapeQuotes(procedureNamePattern) + "' "; } sql += " ORDER BY PROCEDURE_SCHEM, PROCEDURE_NAME "; return createMetaDataStatement().executeQuery(sql); } /* * Get a description of a catalog's stored procedure parameters and result * columns.

Only descriptions matching the schema, procedure and * parameter name criteria are returned. They are ordered by PROCEDURE_SCHEM * and PROCEDURE_NAME. Within this, the return value, if any, is first. Next * are the parameter descriptions in call order. The column descriptions * follow in column number order.

Each row in the ResultSet is a * parameter description or column description with the following fields: *

  1. PROCEDURE_CAT String => procedure catalog (may be null) *
  2. PROCEDURE_SCHEM String => procedure schema (may be null) *
  3. PROCEDURE_NAME String => procedure name
  4. COLUMN_NAME * String => column/parameter name
  5. COLUMN_TYPE Short => kind of * column/parameter:
    • procedureColumnUnknown - nobody knows
    • procedureColumnIn - * IN parameter
    • procedureColumnInOut - INOUT parameter
    • procedureColumnOut - * OUT parameter
    • procedureColumnReturn - procedure return value
    • procedureColumnResult - * result column in ResultSet
  6. DATA_TYPE short => SQL type * from java.sql.Types
  7. TYPE_NAME String => Data source specific * type name
  8. PRECISION int => precision
  9. LENGTH int => * length in bytes of data
  10. SCALE short => scale
  11. RADIX * short => radix
  12. NULLABLE short => can it contain NULL?
    • procedureNoNulls - * does not allow NULL values
    • procedureNullable - allows NULL values *
    • procedureNullableUnknown - nullability unknown
    • REMARKS * String => comment describing parameter/column
@param catalog This * is ignored in org.postgresql, advise this is set to null @param * schemaPattern @param procedureNamePattern a procedure name pattern @param * columnNamePattern a column name pattern, this is currently ignored * because postgresql does not name procedure parameters. @return each row * is a stored procedure parameter or column description @exception * SQLException if a database-access error occurs * * @see #getSearchStringEscape */ // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { ResultSetField f[] = new ResultSetField[13]; ArrayList v = new ArrayList(); // The new ResultSet tuple stuff f[0] = new ResultSetField("PROCEDURE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("PROCEDURE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("PROCEDURE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[3] = new ResultSetField("COLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("COLUMN_TYPE", TypeOid.INT2, 2); f[5] = new ResultSetField("DATA_TYPE", TypeOid.INT2, 2); f[6] = new ResultSetField("TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[7] = new ResultSetField("PRECISION", TypeOid.INT4, 4); f[8] = new ResultSetField("LENGTH", TypeOid.INT4, 4); f[9] = new ResultSetField("SCALE", TypeOid.INT2, 2); f[10] = new ResultSetField("RADIX", TypeOid.INT2, 2); f[11] = new ResultSetField("NULLABLE", TypeOid.INT2, 2); f[12] = new ResultSetField("REMARKS", TypeOid.VARCHAR, getMaxNameLength()); String sql = "SELECT n.nspname,p.proname,p.prorettype,p.proargtypes, t.typtype::varchar,t.typrelid " + " FROM pg_catalog.pg_proc p,pg_catalog.pg_namespace n, pg_catalog.pg_type t " + " WHERE p.pronamespace=n.oid AND p.prorettype=t.oid " + " AND " + resolveSchemaPatternCondition( "n.nspname", schemaPattern); if(procedureNamePattern != null) { sql += " AND p.proname LIKE '" + escapeQuotes(procedureNamePattern) + "' "; } sql += " ORDER BY n.nspname, p.proname "; ResultSet rs = m_connection.createStatement().executeQuery(sql); String schema = null; String procedureName = null; Oid returnType = null; String returnTypeType = null; Oid returnTypeRelid = null; Oid[] argTypes = null; while(rs.next()) { schema = rs.getString("nspname"); procedureName = rs.getString("proname"); returnType = (Oid)rs.getObject("prorettype"); returnTypeType = rs.getString("typtype"); returnTypeRelid = (Oid)rs.getObject("typrelid"); argTypes = (Oid[])rs.getObject("proargtypes"); // decide if we are returning a single column result. if(!returnTypeType.equals("c")) { Object[] tuple = new Object[13]; tuple[0] = null; tuple[1] = schema; tuple[2] = procedureName; tuple[3] = "returnValue"; tuple[4] = new Short((short)java.sql.DatabaseMetaData.procedureColumnReturn); tuple[5] = new Short((short)m_connection.getSQLType(returnType)); tuple[6] = m_connection.getPGType(returnType); tuple[7] = null; tuple[8] = null; tuple[9] = null; tuple[10] = null; tuple[11] = new Short((short)java.sql.DatabaseMetaData.procedureNullableUnknown); tuple[12] = null; v.add(tuple); } // Add a row for each argument. for(int i = 0; i < argTypes.length; i++) { Oid argOid = argTypes[i]; Object[] tuple = new Object[13]; tuple[0] = null; tuple[1] = schema; tuple[2] = procedureName; tuple[3] = "$" + (i + 1); tuple[4] = new Short((short)java.sql.DatabaseMetaData.procedureColumnIn); tuple[5] = new Short((short)m_connection.getSQLType(argOid)); tuple[6] = m_connection.getPGType(argOid); tuple[7] = null; tuple[8] = null; tuple[9] = null; tuple[10] = null; tuple[11] = new Short((short)java.sql.DatabaseMetaData.procedureNullableUnknown); tuple[12] = null; v.add(tuple); } // if we are returning a multi-column result. if(returnTypeType.equals("c")) { String columnsql = "SELECT a.attname,a.atttypid FROM pg_catalog.pg_attribute a WHERE a.attrelid = ? ORDER BY a.attnum "; PreparedStatement stmt = m_connection.prepareStatement(columnsql); stmt.setObject(1, returnTypeRelid); ResultSet columnrs = stmt.executeQuery(columnsql); while(columnrs.next()) { Oid columnTypeOid = (Oid)columnrs.getObject("atttypid"); Object[] tuple = new Object[13]; tuple[0] = null; tuple[1] = schema; tuple[2] = procedureName; tuple[3] = columnrs.getString("attname"); tuple[4] = new Short((short)java.sql.DatabaseMetaData.procedureColumnResult); tuple[5] = new Short((short)m_connection.getSQLType(columnTypeOid)); tuple[6] = m_connection.getPGType(columnTypeOid); tuple[7] = null; tuple[8] = null; tuple[9] = null; tuple[10] = null; tuple[11] = new Short((short)java.sql.DatabaseMetaData.procedureNullableUnknown); tuple[12] = null; v.add(tuple); } columnrs.close(); stmt.close(); } } rs.close(); return createSyntheticResultSet(f, v); } /* * Get a description of tables available in a catalog.

Only table * descriptions matching the catalog, schema, table name and type criteria * are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and TABLE_NAME. *

Each table description has the following columns:

  1. TABLE_CAT * String => table catalog (may be null)
  2. TABLE_SCHEM String => * table schema (may be null)
  3. TABLE_NAME String => table name *
  4. TABLE_TYPE String => table type. Typical types are "TABLE", * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", * "SYNONYM".
  5. REMARKS String => explanatory comment on the * table

The valid values for the types parameter are: "TABLE", * "INDEX", "SEQUENCE", "VIEW", "SYSTEM TABLE", "SYSTEM INDEX", "SYSTEM * VIEW", "SYSTEM TOAST TABLE", "SYSTEM TOAST INDEX", "TEMPORARY TABLE", and * "TEMPORARY VIEW" @param catalog a catalog name; For org.postgresql, this * is ignored, and should be set to null @param schemaPattern a schema name * pattern @param tableNamePattern a table name pattern. For all tables this * should be "%" @param types a list of table types to include; null returns * all types @return each row is a table description @exception SQLException * if a database-access error occurs. */ public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException { String useSchemas = "SCHEMAS"; String select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME, " + " CASE n.nspname LIKE 'pg!_%' ESCAPE '!' OR n.nspname = 'information_schema' " + " WHEN true THEN CASE " + " WHEN n.nspname = 'pg_catalog' OR n.nspname = 'information_schema' THEN CASE c.relkind " + " WHEN 'r' THEN 'SYSTEM TABLE' " + " WHEN 'v' THEN 'SYSTEM VIEW' " + " WHEN 'i' THEN 'SYSTEM INDEX' " + " ELSE NULL " + " END " + " WHEN n.nspname = 'pg_toast' THEN CASE c.relkind " + " WHEN 'r' THEN 'SYSTEM TOAST TABLE' " + " WHEN 'i' THEN 'SYSTEM TOAST INDEX' " + " ELSE NULL " + " END " + " ELSE CASE c.relkind " + " WHEN 'r' THEN 'TEMPORARY TABLE' " + " WHEN 'i' THEN 'TEMPORARY INDEX' " + " ELSE NULL " + " END " + " END " + " WHEN false THEN CASE c.relkind " + " WHEN 'r' THEN 'TABLE' " + " WHEN 'i' THEN 'INDEX' " + " WHEN 'S' THEN 'SEQUENCE' " + " WHEN 'v' THEN 'VIEW' " + " ELSE NULL " + " END " + " ELSE NULL " + " END " + " AS TABLE_TYPE, d.description AS REMARKS " + " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c " + " LEFT JOIN pg_catalog.pg_description d ON (c.oid = d.objoid AND d.objsubid = 0) " + " LEFT JOIN pg_catalog.pg_class dc ON (d.classoid=dc.oid AND dc.relname='pg_class') " + " LEFT JOIN pg_catalog.pg_namespace dn ON (dn.oid=dc.relnamespace AND dn.nspname='pg_catalog') " + " WHERE c.relnamespace = n.oid " + " AND " + resolveSchemaPatternCondition( "n.nspname", schemaPattern); String orderby = " ORDER BY TABLE_TYPE,TABLE_SCHEM,TABLE_NAME "; if(types == null) { types = s_defaultTableTypes; } if(tableNamePattern != null) { select += " AND c.relname LIKE '" + escapeQuotes(tableNamePattern) + "' "; } String sql = select; sql += " AND (false "; for(int i = 0; i < types.length; i++) { HashMap clauses = (HashMap)s_tableTypeClauses.get(types[i]); if(clauses != null) { String clause = (String)clauses.get(useSchemas); sql += " OR ( " + clause + " ) "; } } sql += ") "; sql += orderby; return createMetaDataStatement().executeQuery(sql); } private static final HashMap s_tableTypeClauses; static { s_tableTypeClauses = new HashMap(); HashMap ht = new HashMap(); s_tableTypeClauses.put("TABLE", ht); ht.put("SCHEMAS", "c.relkind = 'r' AND n.nspname NOT LIKE 'pg!_%' ESCAPE '!' AND n.nspname <> 'information_schema'"); ht.put("NOSCHEMAS", "c.relkind = 'r' AND c.relname NOT LIKE 'pg!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("VIEW", ht); ht.put("SCHEMAS", "c.relkind = 'v' AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema'"); ht.put("NOSCHEMAS", "c.relkind = 'v' AND c.relname NOT LIKE 'pg!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("INDEX", ht); ht.put("SCHEMAS", "c.relkind = 'i' AND n.nspname NOT LIKE 'pg!_%' ESCAPE '!' AND n.nspname <> 'information_schema'"); ht.put("NOSCHEMAS", "c.relkind = 'i' AND c.relname NOT LIKE 'pg!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("SEQUENCE", ht); ht.put("SCHEMAS", "c.relkind = 'S'"); ht.put("NOSCHEMAS", "c.relkind = 'S'"); ht = new HashMap(); s_tableTypeClauses.put("SYSTEM TABLE", ht); ht.put("SCHEMAS", "c.relkind = 'r' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema')"); ht.put("NOSCHEMAS", "c.relkind = 'r' AND c.relname LIKE 'pg!_%' ESCAPE '!' AND c.relname NOT LIKE 'pgLIKE 'pg!_toast!_%' ESCAPE '!'toast!_%' ESCAPE '!' AND c.relname NOT LIKE 'pg!_temp!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("SYSTEM TOAST TABLE", ht); ht.put("SCHEMAS", "c.relkind = 'r' AND n.nspname = 'pg_toast'"); ht.put("NOSCHEMAS", "c.relkind = 'r' AND c.relname LIKE 'pg!_toast!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("SYSTEM TOAST INDEX", ht); ht.put("SCHEMAS", "c.relkind = 'i' AND n.nspname = 'pg_toast'"); ht.put("NOSCHEMAS", "c.relkind = 'i' AND c.relname LIKE 'pg!_toast!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("SYSTEM VIEW", ht); ht.put("SCHEMAS", "c.relkind = 'v' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema') "); ht.put("NOSCHEMAS", "c.relkind = 'v' AND c.relname LIKE 'pg!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("SYSTEM INDEX", ht); ht.put("SCHEMAS", "c.relkind = 'i' AND (n.nspname = 'pg_catalog' OR n.nspname = 'information_schema') "); ht.put("NOSCHEMAS", "c.relkind = 'v' AND c.relname LIKE 'pg!_%' ESCAPE '!' AND c.relname NOT LIKE 'pg!_toast!_%' ESCAPE '!' AND c.relname NOT LIKE 'pg!_temp!_%' ESCAPE '!'"); ht = new HashMap(); s_tableTypeClauses.put("TEMPORARY TABLE", ht); ht.put("SCHEMAS", "c.relkind = 'r' AND n.nspname LIKE 'pg!_temp!_%' ESCAPE '!' "); ht.put("NOSCHEMAS", "c.relkind = 'r' AND c.relname LIKE 'pg!_temp!_%' ESCAPE '!' "); ht = new HashMap(); s_tableTypeClauses.put("TEMPORARY INDEX", ht); ht.put("SCHEMAS", "c.relkind = 'i' AND n.nspname LIKE 'pg!_temp!_%' ESCAPE '!' "); ht.put("NOSCHEMAS", "c.relkind = 'i' AND c.relname LIKE 'pg!_temp!_%' ESCAPE '!' "); } // These are the default tables, used when NULL is passed to getTables // The choice of these provide the same behaviour as psql's \d private static final String s_defaultTableTypes[] = { "TABLE", "VIEW", "INDEX", "SEQUENCE", "TEMPORARY TABLE" }; /* * Get the schema names available in this database. The results are ordered * by schema name.

The schema column is:

  1. TABLE_SCHEM * String => schema name
@return ResultSet each row has a single * String column that is a schema name */ public java.sql.ResultSet getSchemas() throws SQLException { String sql = "SELECT nspname AS TABLE_SCHEM FROM pg_catalog.pg_namespace WHERE nspname <> 'pg_toast' AND nspname NOT LIKE 'pg!_temp!_%' ESCAPE '!' ORDER BY TABLE_SCHEM"; return createMetaDataStatement().executeQuery(sql); } /* * Get the catalog names available in this database. The results are ordered * by catalog name.

The catalog column is:

  1. TABLE_CAT * String => catalog name
@return ResultSet each row has a single * String column that is a catalog name */ public java.sql.ResultSet getCatalogs() throws SQLException { String sql = "SELECT datname AS TABLE_CAT FROM pg_catalog.pg_database ORDER BY TABLE_CAT"; return createMetaDataStatement().executeQuery(sql); } /* * Get the table types available in this database. The results are ordered * by table type.

The table type is:

  1. TABLE_TYPE String => * table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL * TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
@return * ResultSet each row has a single String column that is a table type */ public java.sql.ResultSet getTableTypes() throws SQLException { String types[] = (String[])s_tableTypeClauses.keySet().toArray(new String[s_tableTypeClauses.size()]); sortStringArray(types); ResultSetField f[] = new ResultSetField[1]; ArrayList v = new ArrayList(); f[0] = new ResultSetField(new String("TABLE_TYPE"), TypeOid.VARCHAR, getMaxNameLength()); for(int i = 0; i < types.length; i++) { Object[] tuple = new Object[1]; tuple[0] = types[i]; v.add(tuple); } return createSyntheticResultSet(f, v); } /* * Get a description of table columns available in a catalog.

Only * column descriptions matching the catalog, schema, table and column name * criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME and * ORDINAL_POSITION.

Each column description has the following columns: *

  1. TABLE_CAT String => table catalog (may be null)
  2. TABLE_SCHEM * String => table schema (may be null)
  3. TABLE_NAME String => * table name
  4. COLUMN_NAME String => column name
  5. DATA_TYPE * short => SQL type from java.sql.Types
  6. TYPE_NAME String => * Data source dependent type name
  7. COLUMN_SIZE int => column * size. For char or date types this is the maximum number of characters, * for numeric or decimal types this is precision.
  8. BUFFER_LENGTH * is not used.
  9. DECIMAL_DIGITS int => the number of fractional * digits
  10. NUM_PREC_RADIX int => Radix (typically either 10 or * 2)
  11. NULLABLE int => is NULL allowed?
    • columnNoNulls - * might not allow NULL values
    • columnNullable - definitely allows NULL * values
    • columnNullableUnknown - nullability unknown
  12. REMARKS * String => comment describing column (may be null)
  13. COLUMN_DEF * String => default value (may be null)
  14. SQL_DATA_TYPE int => * unused
  15. SQL_DATETIME_SUB int => unused
  16. CHAR_OCTET_LENGTH * int => for char types the maximum number of bytes in the column
  17. ORDINAL_POSITION * int => index of column in table (starting at 1)
  18. IS_NULLABLE * String => "NO" means column definitely does not allow NULL values; "YES" * means the column might allow NULL values. An empty string means nobody * knows.
@param catalog a catalog name; "" retrieves those without a * catalog @param schemaPattern a schema name pattern; "" retrieves those * without a schema @param tableNamePattern a table name pattern @param * columnNamePattern a column name pattern @return ResultSet each row is a * column description * * @see #getSearchStringEscape */ public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { ArrayList v = new ArrayList(); // The new ResultSet tuple stuff ResultSetField f[] = new ResultSetField[18]; // The field descriptors // for the new ResultSet f[0] = new ResultSetField("TABLE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("TABLE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("TABLE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[3] = new ResultSetField("COLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("DATA_TYPE", TypeOid.INT2, 2); f[5] = new ResultSetField("TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[6] = new ResultSetField("COLUMN_SIZE", TypeOid.INT4, 4); f[7] = new ResultSetField("BUFFER_LENGTH", TypeOid.VARCHAR, getMaxNameLength()); f[8] = new ResultSetField("DECIMAL_DIGITS", TypeOid.INT4, 4); f[9] = new ResultSetField("NUM_PREC_RADIX", TypeOid.INT4, 4); f[10] = new ResultSetField("NULLABLE", TypeOid.INT4, 4); f[11] = new ResultSetField("REMARKS", TypeOid.VARCHAR, getMaxNameLength()); f[12] = new ResultSetField("COLUMN_DEF", TypeOid.VARCHAR, getMaxNameLength()); f[13] = new ResultSetField("SQL_DATA_TYPE", TypeOid.INT4, 4); f[14] = new ResultSetField("SQL_DATETIME_SUB", TypeOid.INT4, 4); f[15] = new ResultSetField("CHAR_OCTET_LENGTH", TypeOid.INT4, 4); f[16] = new ResultSetField("ORDINAL_POSITION", TypeOid.INT4, 4); f[17] = new ResultSetField("IS_NULLABLE", TypeOid.VARCHAR, getMaxNameLength()); String sql = "SELECT n.nspname,c.relname,a.attname," + " a.atttypid as atttypid,a.attnotnull,a.atttypmod," + " a.attlen::int4 as attlen,a.attnum,def.adsrc,dsc.description " + " FROM pg_catalog.pg_namespace n " + " JOIN pg_catalog.pg_class c ON (c.relnamespace = n.oid) " + " JOIN pg_catalog.pg_attribute a ON (a.attrelid=c.oid) " + " LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid=def.adrelid AND a.attnum = def.adnum) " + " LEFT JOIN pg_catalog.pg_description dsc ON (c.oid=dsc.objoid AND a.attnum = dsc.objsubid) " + " LEFT JOIN pg_catalog.pg_class dc ON (dc.oid=dsc.classoid AND dc.relname='pg_class') " + " LEFT JOIN pg_catalog.pg_namespace dn ON (dc.relnamespace=dn.oid AND dn.nspname='pg_catalog') " + " WHERE a.attnum > 0 AND NOT a.attisdropped " + " AND " + resolveSchemaPatternCondition( "n.nspname", schemaPattern); if(tableNamePattern != null && !"".equals(tableNamePattern)) { sql += " AND c.relname LIKE '" + escapeQuotes(tableNamePattern) + "' "; } if(columnNamePattern != null && !"".equals(columnNamePattern)) { sql += " AND a.attname LIKE '" + escapeQuotes(columnNamePattern) + "' "; } sql += " ORDER BY nspname,relname,attnum "; ResultSet rs = m_connection.createStatement().executeQuery(sql); while(rs.next()) { Object[] tuple = new Object[18]; Oid typeOid = (Oid)rs.getObject("atttypid"); tuple[0] = null; // Catalog name, not supported tuple[1] = rs.getString("nspname"); // Schema tuple[2] = rs.getString("relname"); // Table name tuple[3] = rs.getString("attname"); // Column name tuple[4] = new Short((short)m_connection.getSQLType(typeOid)); String pgType = m_connection.getPGType(typeOid); tuple[5] = m_connection.getPGType(typeOid); // Type name String defval = rs.getString("adsrc"); if(defval != null) { if(pgType.equals("int4")) { if(defval.indexOf("nextval(") != -1) tuple[5] = "serial"; // Type name == // serial } else if(pgType.equals("int8")) { if(defval.indexOf("nextval(") != -1) tuple[5] = "bigserial"; // Type name == // bigserial } } // by default no decimal_digits // if the type is numeric or decimal we will // overwrite later. tuple[8] = new Integer(0); if(pgType.equals("bpchar") || pgType.equals("varchar")) { int atttypmod = rs.getInt("atttypmod"); tuple[6] = new Integer(atttypmod != -1 ? atttypmod - VARHDRSZ : 0); } else if(pgType.equals("numeric") || pgType.equals("decimal")) { int attypmod = rs.getInt("atttypmod") - VARHDRSZ; tuple[6] = new Integer ((attypmod >> 16) & 0xffff); tuple[8] = new Integer (attypmod & 0xffff); tuple[9] = new Integer(10); } else if(pgType.equals("bit") || pgType.equals("varbit")) { tuple[6] = rs.getObject("atttypmod"); tuple[9] = new Integer(2); } else { tuple[6] = rs.getObject("attlen"); tuple[9] = new Integer(10); } tuple[7] = null; // Buffer length tuple[10] = new Integer(rs .getBoolean("attnotnull") ? java.sql.DatabaseMetaData.columnNoNulls : java.sql.DatabaseMetaData.columnNullable); // Nullable tuple[11] = rs.getString("description"); // Description (if any) tuple[12] = rs.getString("adsrc"); // Column default tuple[13] = null; // sql data type (unused) tuple[14] = null; // sql datetime sub (unused) tuple[15] = tuple[6]; // char octet length tuple[16] = new Integer(rs.getInt("attnum")); // ordinal position tuple[17] = rs.getBoolean("attnotnull") ? "NO" : "YES"; // Is // nullable v.add(tuple); } rs.close(); return createSyntheticResultSet(f, v); } /* * Get a description of the access rights for a table's columns.

Only * privileges matching the column name criteria are returned. They are * ordered by COLUMN_NAME and PRIVILEGE.

Each privilige description has * the following columns:

  1. TABLE_CAT String => table * catalog (may be null)
  2. TABLE_SCHEM String => table schema * (may be null)
  3. TABLE_NAME String => table name
  4. COLUMN_NAME * String => column name
  5. GRANTOR => grantor of access (may be * null)
  6. GRANTEE String => grantee of access
  7. PRIVILEGE * String => name of access (SELECT, INSERT, UPDATE, REFRENCES, ...)
  8. IS_GRANTABLE * String => "YES" if grantee is permitted to grant to others; "NO" if not; * null if unknown
@param catalog a catalog name; "" retrieves those * without a catalog @param schema a schema name; "" retrieves those without * a schema @param table a table name @param columnNamePattern a column name * pattern @return ResultSet each row is a column privilege description * * @see #getSearchStringEscape */ public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { ResultSetField f[] = new ResultSetField[8]; ArrayList v = new ArrayList(); if(table == null) table = "%"; if(columnNamePattern == null) columnNamePattern = "%"; f[0] = new ResultSetField("TABLE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("TABLE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("TABLE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[3] = new ResultSetField("COLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("GRANTOR", TypeOid.VARCHAR, getMaxNameLength()); f[5] = new ResultSetField("GRANTEE", TypeOid.VARCHAR, getMaxNameLength()); f[6] = new ResultSetField("PRIVILEGE", TypeOid.VARCHAR, getMaxNameLength()); f[7] = new ResultSetField("IS_GRANTABLE", TypeOid.VARCHAR, getMaxNameLength()); String sql = "SELECT n.nspname,c.relname,u.usename,c.relacl,a.attname " + " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_attribute a " + " WHERE c.relnamespace = n.oid " + " AND u.usesysid = c.relowner " + " AND c.oid = a.attrelid " + " AND c.relkind = 'r' " + " AND a.attnum > 0 AND NOT a.attisdropped " + " AND " + resolveSchemaCondition( "n.nspname", schema); sql += " AND c.relname = '" + escapeQuotes(table) + "' "; if(columnNamePattern != null && !"".equals(columnNamePattern)) { sql += " AND a.attname LIKE '" + escapeQuotes(columnNamePattern) + "' "; } sql += " ORDER BY attname "; ResultSet rs = m_connection.createStatement().executeQuery(sql); String schemaName = null; String tableName = null; String column = null; String owner = null; String[] acls = null; HashMap permissions = null; String permNames[] = null; while(rs.next()) { schemaName = rs.getString("nspname"); tableName = rs.getString("relname"); column = rs.getString("attname"); owner = rs.getString("usename"); acls = (String[])rs.getObject("relacl"); permissions = parseACL(acls, owner); permNames = (String[])permissions.keySet().toArray(new String[permissions.size()]); sortStringArray(permNames); for(int i = 0; i < permNames.length; i++) { ArrayList grantees = (ArrayList)permissions.get(permNames[i]); for(int j = 0; j < grantees.size(); j++) { String grantee = (String)grantees.get(j); String grantable = owner.equals(grantee) ? "YES" : "NO"; Object[] tuple = new Object[8]; tuple[0] = null; tuple[1] = schemaName; tuple[2] = tableName; tuple[3] = column; tuple[4] = owner; tuple[5] = grantee; tuple[6] = permNames[i]; tuple[7] = grantable; v.add(tuple); } } } rs.close(); return createSyntheticResultSet(f, v); } /* * Get a description of the access rights for each table available in a * catalog. This method is currently unimplemented.

Only privileges * matching the schema and table name criteria are returned. They are * ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.

Each privilige * description has the following columns:

  1. TABLE_CAT String => * table catalog (may be null)
  2. TABLE_SCHEM String => table * schema (may be null)
  3. TABLE_NAME String => table name
  4. GRANTOR => * grantor of access (may be null)
  5. GRANTEE String => grantee of * access
  6. PRIVILEGE String => name of access (SELECT, INSERT, * UPDATE, REFRENCES, ...)
  7. IS_GRANTABLE String => "YES" if * grantee is permitted to grant to others; "NO" if not; null if unknown *
@param catalog a catalog name; "" retrieves those without a catalog * @param schemaPattern a schema name pattern; "" retrieves those without a * schema @param tableNamePattern a table name pattern @return ResultSet * each row is a table privilege description * * @see #getSearchStringEscape */ public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { ResultSetField f[] = new ResultSetField[7]; ArrayList v = new ArrayList(); f[0] = new ResultSetField("TABLE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("TABLE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("TABLE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[3] = new ResultSetField("GRANTOR", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("GRANTEE", TypeOid.VARCHAR, getMaxNameLength()); f[5] = new ResultSetField("PRIVILEGE", TypeOid.VARCHAR, getMaxNameLength()); f[6] = new ResultSetField("IS_GRANTABLE", TypeOid.VARCHAR, getMaxNameLength()); String sql = "SELECT n.nspname,c.relname,u.usename,c.relacl " + " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c, pg_catalog.pg_user u " + " WHERE c.relnamespace = n.oid " + " AND u.usesysid = c.relowner " + " AND c.relkind = 'r' " + " AND " + resolveSchemaPatternCondition( "n.nspname", schemaPattern); if(tableNamePattern != null && !"".equals(tableNamePattern)) { sql += " AND c.relname LIKE '" + escapeQuotes(tableNamePattern) + "' "; } sql += " ORDER BY nspname, relname "; ResultSet rs = m_connection.createStatement().executeQuery(sql); String schema = null; String table = null; String owner = null; String[] acls = null; HashMap permissions = null; String permNames[] = null; while(rs.next()) { schema = rs.getString("nspname"); table = rs.getString("relname"); owner = rs.getString("usename"); acls = (String[])rs.getObject("relacl"); permissions = parseACL(acls, owner); permNames = (String[])permissions.keySet().toArray(new String[permissions.size()]); sortStringArray(permNames); for(int i = 0; i < permNames.length; i++) { ArrayList grantees = (ArrayList)permissions.get(permNames[i]); for(int j = 0; j < grantees.size(); j++) { String grantee = (String)grantees.get(j); String grantable = owner.equals(grantee) ? "YES" : "NO"; Object[] tuple = new Object[7]; tuple[0] = null; tuple[1] = schema; tuple[2] = table; tuple[3] = owner; tuple[4] = grantee; tuple[5] = permNames[i]; tuple[6] = grantable; v.add(tuple); } } } rs.close(); return createSyntheticResultSet(f, v); } private static void sortStringArray(String s[]) { for(int i = 0; i < s.length - 1; i++) { for(int j = i + 1; j < s.length; j++) { if(s[i].compareTo(s[j]) > 0) { String tmp = s[i]; s[i] = s[j]; s[j] = tmp; } } } } /** * Add the user described by the given acl to the ArrayLists of users with the * privileges described by the acl. */ private void addACLPrivileges(String acl, HashMap privileges) { int equalIndex = acl.lastIndexOf("="); String name = acl.substring(0, equalIndex); if(name.length() == 0) { name = "PUBLIC"; } String privs = acl.substring(equalIndex + 1); for(int i = 0; i < privs.length(); i++) { char c = privs.charAt(i); String sqlpriv; switch(c) { case 'a': sqlpriv = "INSERT"; break; case 'r': sqlpriv = "SELECT"; break; case 'w': sqlpriv = "UPDATE"; break; case 'd': sqlpriv = "DELETE"; break; case 'R': sqlpriv = "RULE"; break; case 'x': sqlpriv = "REFERENCES"; break; case 't': sqlpriv = "TRIGGER"; break; // the folloowing can't be granted to a table, but // we'll keep them for completeness. case 'X': sqlpriv = "EXECUTE"; break; case 'U': sqlpriv = "USAGE"; break; case 'C': sqlpriv = "CREATE"; break; case 'T': sqlpriv = "CREATE TEMP"; break; default: sqlpriv = "UNKNOWN"; } ArrayList usersWithPermission = (ArrayList)privileges.get(sqlpriv); if(usersWithPermission == null) { usersWithPermission = new ArrayList(); privileges.put(sqlpriv, usersWithPermission); } usersWithPermission.add(name); } } /** * Take the a String representing an array of ACLs and return a HashMap * mapping the SQL permission name to a ArrayList of usernames who have that * permission. */ protected HashMap parseACL(String[] aclArray, String owner) { if(aclArray == null || aclArray.length == 0) { // null acl is a shortcut for owner having full privs aclArray = new String[] { owner + "=arwdRxt" }; } HashMap privileges = new HashMap(); for(int i = 0; i < aclArray.length; i++) { String acl = aclArray[i]; addACLPrivileges(acl, privileges); } return privileges; } /* * Get a description of a table's optimal set of columns that uniquely * identifies a row. They are ordered by SCOPE.

Each column description * has the following columns:

  1. SCOPE short => actual scope * of result
    • bestRowTemporary - very temporary, while using row *
    • bestRowTransaction - valid for remainder of current transaction
    • * bestRowSession - valid for remainder of current session
  2. COLUMN_NAME * String => column name
  3. DATA_TYPE short => SQL data type from * java.sql.Types
  4. TYPE_NAME String => Data source dependent * type name
  5. COLUMN_SIZE int => precision
  6. BUFFER_LENGTH * int => not used
  7. DECIMAL_DIGITS short => scale
  8. PSEUDO_COLUMN * short => is this a pseudo column like an Oracle ROWID
    • * bestRowUnknown - may or may not be pseudo column
    • bestRowNotPseudo - * is NOT a pseudo column
    • bestRowPseudo - is a pseudo column
    *
@param catalog a catalog name; "" retrieves those without a catalog * @param schema a schema name; "" retrieves those without a schema @param * table a table name @param scope the scope of interest; use same values as * SCOPE @param nullable include columns that are nullable? @return * ResultSet each row is a column description */ // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException { ResultSetField f[] = new ResultSetField[8]; ArrayList v = new ArrayList(); // The new ResultSet tuple stuff f[0] = new ResultSetField("SCOPE", TypeOid.INT2, 2); f[1] = new ResultSetField("COLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("DATA_TYPE", TypeOid.INT2, 2); f[3] = new ResultSetField("TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("COLUMN_SIZE", TypeOid.INT4, 4); f[5] = new ResultSetField("BUFFER_LENGTH", TypeOid.INT4, 4); f[6] = new ResultSetField("DECIMAL_DIGITS", TypeOid.INT2, 2); f[7] = new ResultSetField("PSEUDO_COLUMN", TypeOid.INT2, 2); /* * At the moment this simply returns a table's primary key, if there is * one. I believe other unique indexes, ctid, and oid should also be * considered. -KJ */ String where = ""; String from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i "; where = " AND ct.relnamespace = n.oid " + " AND " + resolveSchemaCondition( "n.nspname", schema); String sql = "SELECT a.attname, a.atttypid as atttypid " + from + " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid " + " AND a.attrelid=ci.oid AND i.indisprimary " + " AND ct.relname = '" + escapeQuotes(table) + "' " + where + " ORDER BY a.attnum "; ResultSet rs = m_connection.createStatement().executeQuery(sql); while(rs.next()) { Object[] tuple = new Object[8]; Oid columnTypeOid = (Oid)rs.getObject("atttypid"); tuple[0] = new Short((short)scope); tuple[1] = rs.getString("attname"); tuple[2] = new Short((short)m_connection.getSQLType(columnTypeOid)); tuple[3] = m_connection.getPGType(columnTypeOid); tuple[4] = null; tuple[5] = null; tuple[6] = null; tuple[7] = new Short((short)java.sql.DatabaseMetaData.bestRowNotPseudo); v.add(tuple); } return createSyntheticResultSet(f, v); } /* * Get a description of a table's columns that are automatically updated * when any value in a row is updated. They are unordered.

Each column * description has the following columns:

  1. SCOPE short => * is not used
  2. COLUMN_NAME String => column name
  3. DATA_TYPE * short => SQL data type from java.sql.Types
  4. TYPE_NAME String => * Data source dependent type name
  5. COLUMN_SIZE int => precision *
  6. BUFFER_LENGTH int => length of column value in bytes
  7. DECIMAL_DIGITS * short => scale
  8. PSEUDO_COLUMN short => is this a pseudo * column like an Oracle ROWID
    • versionColumnUnknown - may or may * not be pseudo column
    • versionColumnNotPseudo - is NOT a pseudo column *
    • versionColumnPseudo - is a pseudo column
@param catalog * a catalog name; "" retrieves those without a catalog @param schema a * schema name; "" retrieves those without a schema @param table a table * name @return ResultSet each row is a column description */ public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { ResultSetField f[] = new ResultSetField[8]; ArrayList v = new ArrayList(); // The new ResultSet tuple stuff f[0] = new ResultSetField("SCOPE", TypeOid.INT2, 2); f[1] = new ResultSetField("COLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("DATA_TYPE", TypeOid.INT2, 2); f[3] = new ResultSetField("TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("COLUMN_SIZE", TypeOid.INT4, 4); f[5] = new ResultSetField("BUFFER_LENGTH", TypeOid.INT4, 4); f[6] = new ResultSetField("DECIMAL_DIGITS", TypeOid.INT2, 2); f[7] = new ResultSetField("PSEUDO_COLUMN", TypeOid.INT2, 2); Object[] tuple = new Object[8]; /* * Postgresql does not have any column types that are automatically * updated like some databases' timestamp type. We can't tell what rules * or triggers might be doing, so we are left with the system columns * that change on an update. An update may change all of the following * system columns: ctid, xmax, xmin, cmax, and cmin. Depending on if we * are in a transaction and wether we roll it back or not the only * guaranteed change is to ctid. -KJ */ tuple[0] = null; tuple[1] = "ctid"; tuple[2] = new Short((short)m_connection.getSQLType("tid")); tuple[3] = "tid"; tuple[4] = null; tuple[5] = null; tuple[6] = null; tuple[7] = new Short((short)java.sql.DatabaseMetaData.versionColumnPseudo); v.add(tuple); /* * Perhaps we should check that the given catalog.schema.table actually * exists. -KJ */ return createSyntheticResultSet(f, v); } /* * Get a description of a table's primary key columns. They are ordered by * COLUMN_NAME.

Each column description has the following columns:

    *
  1. TABLE_CAT String => table catalog (may be null)
  2. TABLE_SCHEM * String => table schema (may be null)
  3. TABLE_NAME String => * table name
  4. COLUMN_NAME String => column name
  5. KEY_SEQ * short => sequence number within primary key
  6. PK_NAME String => * primary key name (may be null)
@param catalog a catalog name; "" * retrieves those without a catalog @param schema a schema name pattern; "" * retrieves those without a schema @param table a table name @return * ResultSet each row is a primary key column description */ public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { String from; String where = ""; String select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "; from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i "; where = " AND ct.relnamespace = n.oid AND " + resolveSchemaCondition("n.nspname", schema); String sql = select + " ct.relname AS TABLE_NAME, " + " a.attname AS COLUMN_NAME, " + " a.attnum::int2 AS KEY_SEQ, " + " ci.relname AS PK_NAME " + from + " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid " + " AND a.attrelid=ci.oid AND i.indisprimary "; if(table != null && !"".equals(table)) { sql += " AND ct.relname = '" + escapeQuotes(table) + "' "; } sql += where + " ORDER BY table_name, pk_name, key_seq"; return createMetaDataStatement().executeQuery(sql); } /** * @param primaryCatalog * @param primarySchema * @param primaryTable if provided will get the keys exported by this table * @param foreignTable if provided will get the keys imported by this table * @return ResultSet * @throws SQLException */ protected java.sql.ResultSet getImportedExportedKeys(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { ResultSetField f[] = new ResultSetField[14]; f[0] = new ResultSetField("PKTABLE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("PKTABLE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[2] = new ResultSetField("PKTABLE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[3] = new ResultSetField("PKCOLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("FKTABLE_CAT", TypeOid.VARCHAR, getMaxNameLength()); f[5] = new ResultSetField("FKTABLE_SCHEM", TypeOid.VARCHAR, getMaxNameLength()); f[6] = new ResultSetField("FKTABLE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[7] = new ResultSetField("FKCOLUMN_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[8] = new ResultSetField("KEY_SEQ", TypeOid.INT2, 2); f[9] = new ResultSetField("UPDATE_RULE", TypeOid.INT2, 2); f[10] = new ResultSetField("DELETE_RULE", TypeOid.INT2, 2); f[11] = new ResultSetField("FK_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[12] = new ResultSetField("PK_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[13] = new ResultSetField("DEFERRABILITY", TypeOid.INT2, 2); /* * The addition of the pg_constraint in 7.3 table should have really * helped us out here, but it comes up just a bit short. - The conkey, * confkey columns aren't really useful without contrib/array unless we * want to issues separate queries. - Unique indexes that can support * foreign keys are not necessarily added to pg_constraint. Also * multiple unique indexes covering the same keys can be created which * make it difficult to determine the PK_NAME field. */ String sql = "SELECT NULL::text AS PKTABLE_CAT, pkn.nspname AS PKTABLE_SCHEM, pkc.relname AS PKTABLE_NAME, pka.attname AS PKCOLUMN_NAME, " + "NULL::text AS FKTABLE_CAT, fkn.nspname AS FKTABLE_SCHEM, fkc.relname AS FKTABLE_NAME, fka.attname AS FKCOLUMN_NAME, " + "pos.n::int2 AS KEY_SEQ, " + "CASE con.confupdtype " + " WHEN 'c' THEN " + DatabaseMetaData.importedKeyCascade + " WHEN 'n' THEN " + DatabaseMetaData.importedKeySetNull + " WHEN 'd' THEN " + DatabaseMetaData.importedKeySetDefault + " WHEN 'r' THEN " + DatabaseMetaData.importedKeyRestrict + " WHEN 'a' THEN " + DatabaseMetaData.importedKeyNoAction + " ELSE NULL END::int2 AS UPDATE_RULE, " + "CASE con.confdeltype " + " WHEN 'c' THEN " + DatabaseMetaData.importedKeyCascade + " WHEN 'n' THEN " + DatabaseMetaData.importedKeySetNull + " WHEN 'd' THEN " + DatabaseMetaData.importedKeySetDefault + " WHEN 'r' THEN " + DatabaseMetaData.importedKeyRestrict + " WHEN 'a' THEN " + DatabaseMetaData.importedKeyNoAction + " ELSE NULL END::int2 AS DELETE_RULE, " + "con.conname AS FK_NAME, pkic.relname AS PK_NAME, " + "CASE " + " WHEN con.condeferrable AND con.condeferred THEN " + DatabaseMetaData.importedKeyInitiallyDeferred + " WHEN con.condeferrable THEN " + DatabaseMetaData.importedKeyInitiallyImmediate + " ELSE " + DatabaseMetaData.importedKeyNotDeferrable + " END::int2 AS DEFERRABILITY " + " FROM " + " pg_catalog.pg_namespace pkn, pg_catalog.pg_class pkc, pg_catalog.pg_attribute pka, " + " pg_catalog.pg_namespace fkn, pg_catalog.pg_class fkc, pg_catalog.pg_attribute fka, " + " pg_catalog.pg_constraint con, " + " pg_catalog.generate_series(1, " + getMaxIndexKeys() + ") pos(n), " + " pg_catalog.pg_depend dep, pg_catalog.pg_class pkic " + " WHERE pkn.oid = pkc.relnamespace AND pkc.oid = pka.attrelid AND pka.attnum = con.confkey[pos.n] AND con.confrelid = pkc.oid " + " AND fkn.oid = fkc.relnamespace AND fkc.oid = fka.attrelid AND fka.attnum = con.conkey[pos.n] AND con.conrelid = fkc.oid " + " AND con.contype = 'f' AND con.oid = dep.objid AND pkic.oid = dep.refobjid AND pkic.relkind = 'i' AND dep.classid = 'pg_constraint'::regclass::oid AND dep.refclassid = 'pg_class'::regclass::oid " + " AND " + resolveSchemaCondition("pkn.nspname", primarySchema) + " AND " + resolveSchemaCondition("fkn.nspname", foreignSchema); if(primaryTable != null && !"".equals(primaryTable)) { sql += " AND pkc.relname = '" + escapeQuotes(primaryTable) + "' "; } if(foreignTable != null && !"".equals(foreignTable)) { sql += " AND fkc.relname = '" + escapeQuotes(foreignTable) + "' "; } if(primaryTable != null) { sql += " ORDER BY fkn.nspname,fkc.relname,pos.n"; } else { sql += " ORDER BY pkn.nspname,pkc.relname,pos.n"; } return createMetaDataStatement().executeQuery(sql); } /* * Get a description of the primary key columns that are referenced by a * table's foreign key columns (the primary keys imported by a table). They * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.

Each * primary key column description has the following columns:

  1. PKTABLE_CAT * String => primary key table catalog being imported (may be null)
  2. PKTABLE_SCHEM * String => primary key table schema being imported (may be null)
  3. PKTABLE_NAME * String => primary key table name being imported
  4. PKCOLUMN_NAME * String => primary key column name being imported
  5. FKTABLE_CAT * String => foreign key table catalog (may be null)
  6. FKTABLE_SCHEM * String => foreign key table schema (may be null)
  7. FKTABLE_NAME * String => foreign key table name
  8. FKCOLUMN_NAME String => * foreign key column name
  9. KEY_SEQ short => sequence number * within foreign key
  10. UPDATE_RULE short => What happens to * foreign key when primary is updated:
    • importedKeyCascade - * change imported key to agree with primary key update
    • * importedKeyRestrict - do not allow update of primary key if it has been * imported
    • importedKeySetNull - change imported key to NULL if its * primary key has been updated
  11. DELETE_RULE short => What * happens to the foreign key when primary is deleted.
    • * importedKeyCascade - delete rows that import a deleted key
    • * importedKeyRestrict - do not allow delete of primary key if it has been * imported
    • importedKeySetNull - change imported key to NULL if its * primary key has been deleted
  12. FK_NAME String => foreign * key name (may be null)
  13. PK_NAME String => primary key name * (may be null)
@param catalog a catalog name; "" retrieves those * without a catalog @param schema a schema name pattern; "" retrieves those * without a schema @param table a table name @return ResultSet each row is * a primary key column description * * @see #getExportedKeys */ public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { return getImportedExportedKeys(null, null, null, catalog, schema, table); } /* * Get a description of a foreign key columns that reference a table's * primary key columns (the foreign keys exported by a table). They are * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ. This * method is currently unimplemented.

Each foreign key column * description has the following columns:

  1. PKTABLE_CAT * String => primary key table catalog (may be null)
  2. PKTABLE_SCHEM * String => primary key table schema (may be null)
  3. PKTABLE_NAME * String => primary key table name
  4. PKCOLUMN_NAME String => * primary key column name
  5. FKTABLE_CAT String => foreign key * table catalog (may be null) being exported (may be null)
  6. FKTABLE_SCHEM * String => foreign key table schema (may be null) being exported (may be * null)
  7. FKTABLE_NAME String => foreign key table name being * exported
  8. FKCOLUMN_NAME String => foreign key column name * being exported
  9. KEY_SEQ short => sequence number within * foreign key
  10. UPDATE_RULE short => What happens to foreign key * when primary is updated:
    • importedKeyCascade - change imported * key to agree with primary key update
    • importedKeyRestrict - do not * allow update of primary key if it has been imported
    • * importedKeySetNull - change imported key to NULL if its primary key has * been updated
  11. DELETE_RULE short => What happens to the * foreign key when primary is deleted.
    • importedKeyCascade - * delete rows that import a deleted key
    • importedKeyRestrict - do not * allow delete of primary key if it has been imported
    • * importedKeySetNull - change imported key to NULL if its primary key has * been deleted
  12. FK_NAME String => foreign key identifier * (may be null)
  13. PK_NAME String => primary key identifier (may * be null)
@param catalog a catalog name; "" retrieves those without * a catalog @param schema a schema name pattern; "" retrieves those without * a schema @param table a table name @return ResultSet each row is a * foreign key column description * * @see #getImportedKeys */ public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { return getImportedExportedKeys(catalog, schema, table, null, null, null); } /* * Get a description of the foreign key columns in the foreign key table * that reference the primary key columns of the primary key table (describe * how one table imports another's key.) This should normally return a * single foreign key/primary key pair (most tables only import a foreign * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, * FKTABLE_NAME, and KEY_SEQ. This method is currently unimplemented.

Each * foreign key column description has the following columns:

  1. PKTABLE_CAT * String => primary key table catalog (may be null)
  2. PKTABLE_SCHEM * String => primary key table schema (may be null)
  3. PKTABLE_NAME * String => primary key table name
  4. PKCOLUMN_NAME String => * primary key column name
  5. FKTABLE_CAT String => foreign key * table catalog (may be null) being exported (may be null)
  6. FKTABLE_SCHEM * String => foreign key table schema (may be null) being exported (may be * null)
  7. FKTABLE_NAME String => foreign key table name being * exported
  8. FKCOLUMN_NAME String => foreign key column name * being exported
  9. KEY_SEQ short => sequence number within * foreign key
  10. UPDATE_RULE short => What happens to foreign key * when primary is updated:
    • importedKeyCascade - change imported * key to agree with primary key update
    • importedKeyRestrict - do not * allow update of primary key if it has been imported
    • * importedKeySetNull - change imported key to NULL if its primary key has * been updated
  11. DELETE_RULE short => What happens to the * foreign key when primary is deleted.
    • importedKeyCascade - * delete rows that import a deleted key
    • importedKeyRestrict - do not * allow delete of primary key if it has been imported
    • * importedKeySetNull - change imported key to NULL if its primary key has * been deleted
  12. FK_NAME String => foreign key identifier * (may be null)
  13. PK_NAME String => primary key identifier (may * be null)
@param catalog a catalog name; "" retrieves those without * a catalog @param schema a schema name pattern; "" retrieves those without * a schema @param table a table name @return ResultSet each row is a * foreign key column description * * @see #getImportedKeys */ public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { return getImportedExportedKeys(primaryCatalog, primarySchema, primaryTable, foreignCatalog, foreignSchema, foreignTable); } /* * Get a description of all the standard SQL types supported by this * database. They are ordered by DATA_TYPE and then by how closely the data * type maps to the corresponding JDBC SQL type.

Each type description * has the following columns:

  1. TYPE_NAME String => Type * name
  2. DATA_TYPE short => SQL data type from java.sql.Types *
  3. PRECISION int => maximum precision
  4. LITERAL_PREFIX * String => prefix used to quote a literal (may be null)
  5. LITERAL_SUFFIX * String => suffix used to quote a literal (may be null)
  6. CREATE_PARAMS * String => parameters used in creating the type (may be null)
  7. NULLABLE * short => can you use NULL for this type?
    • typeNoNulls - does not * allow NULL values
    • typeNullable - allows NULL values
    • * typeNullableUnknown - nullability unknown
  8. CASE_SENSITIVE * boolean=> is it case sensitive?
  9. SEARCHABLE short => can you * use "WHERE" based on this type:
    • typePredNone - No support
    • * typePredChar - Only supported with WHERE .. LIKE
    • typePredBasic - * Supported except for WHERE .. LIKE
    • typeSearchable - Supported for * all WHERE ..
  10. UNSIGNED_ATTRIBUTE boolean => is it * unsigned?
  11. FIXED_PREC_SCALE boolean => can it be a money * value?
  12. AUTO_INCREMENT boolean => can it be used for an * auto-increment value?
  13. LOCAL_TYPE_NAME String => localized * version of type name (may be null)
  14. MINIMUM_SCALE short => * minimum scale supported
  15. MAXIMUM_SCALE short => maximum scale * supported
  16. SQL_DATA_TYPE int => unused
  17. SQL_DATETIME_SUB * int => unused
  18. NUM_PREC_RADIX int => usually 2 or 10
* @return ResultSet each row is a SQL type description */ public java.sql.ResultSet getTypeInfo() throws SQLException { ResultSetField f[] = new ResultSetField[18]; ArrayList v = new ArrayList(); // The new ResultSet tuple stuff f[0] = new ResultSetField("TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[1] = new ResultSetField("DATA_TYPE", TypeOid.INT2, 2); f[2] = new ResultSetField("PRECISION", TypeOid.INT4, 4); f[3] = new ResultSetField("LITERAL_PREFIX", TypeOid.VARCHAR, getMaxNameLength()); f[4] = new ResultSetField("LITERAL_SUFFIX", TypeOid.VARCHAR, getMaxNameLength()); f[5] = new ResultSetField("CREATE_PARAMS", TypeOid.VARCHAR, getMaxNameLength()); f[6] = new ResultSetField("NULLABLE", TypeOid.INT2, 2); f[7] = new ResultSetField("CASE_SENSITIVE", TypeOid.BOOL, 1); f[8] = new ResultSetField("SEARCHABLE", TypeOid.INT2, 2); f[9] = new ResultSetField("UNSIGNED_ATTRIBUTE", TypeOid.BOOL, 1); f[10] = new ResultSetField("FIXED_PREC_SCALE", TypeOid.BOOL, 1); f[11] = new ResultSetField("AUTO_INCREMENT", TypeOid.BOOL, 1); f[12] = new ResultSetField("LOCAL_TYPE_NAME", TypeOid.VARCHAR, getMaxNameLength()); f[13] = new ResultSetField("MINIMUM_SCALE", TypeOid.INT2, 2); f[14] = new ResultSetField("MAXIMUM_SCALE", TypeOid.INT2, 2); f[15] = new ResultSetField("SQL_DATA_TYPE", TypeOid.INT4, 4); f[16] = new ResultSetField("SQL_DATETIME_SUB", TypeOid.INT4, 4); f[17] = new ResultSetField("NUM_PREC_RADIX", TypeOid.INT4, 4); String sql = "SELECT typname FROM pg_catalog.pg_type where typrelid = 0"; ResultSet rs = m_connection.createStatement().executeQuery(sql); // cache some results, this will keep memory useage down, and speed // things up a little. Integer i9 = new Integer(9); Integer i10 = new Integer(10); Short nn = new Short((short)java.sql.DatabaseMetaData.typeNoNulls); Short ts = new Short((short)java.sql.DatabaseMetaData.typeSearchable); String typname = null; while(rs.next()) { Object[] tuple = new Object[18]; typname = rs.getString(1); tuple[0] = typname; tuple[1] = new Short((short)m_connection.getSQLType(typname)); tuple[2] = i9; // for now tuple[6] = nn; // for now tuple[7] = Boolean.FALSE; // false for now - not case sensitive tuple[8] = ts; tuple[9] = Boolean.FALSE; // false for now - it's signed tuple[10] = Boolean.FALSE; // false for now - must handle money tuple[11] = Boolean.FALSE; // false - it isn't autoincrement // 12 - LOCAL_TYPE_NAME is null // 13 & 14 ? // 15 & 16 are unused so we return null tuple[17] = i10; // everything is base 10 v.add(tuple); // add pseudo-type serial, bigserial if(typname.equals("int4")) { Object[] tuple1 = (Object[])tuple.clone(); tuple1[0] = "serial"; tuple1[11] = Boolean.TRUE; v.add(tuple1); } else if(typname.equals("int8")) { Object[] tuple1 = (Object[])tuple.clone(); tuple1[0] = "bigserial"; tuple1[11] = Boolean.TRUE; v.add(tuple1); } } rs.close(); return createSyntheticResultSet(f, v); } /* * Get a description of a table's indices and statistics. They are ordered * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.

Each index * column description has the following columns:

  1. TABLE_CAT * String => table catalog (may be null)
  2. TABLE_SCHEM String => * table schema (may be null)
  3. TABLE_NAME String => table name *
  4. NON_UNIQUE boolean => Can index values be non-unique? false * when TYPE is tableIndexStatistic
  5. INDEX_QUALIFIER String => * index catalog (may be null); null when TYPE is tableIndexStatistic
  6. INDEX_NAME * String => index name; null when TYPE is tableIndexStatistic
  7. TYPE * short => index type:
    • tableIndexStatistic - this identifies * table statistics that are returned in conjuction with a table's index * descriptions
    • tableIndexClustered - this is a clustered index
    • * tableIndexHashed - this is a hashed index
    • tableIndexOther - this is * some other style of index
  8. ORDINAL_POSITION short => * column sequence number within index; zero when TYPE is * tableIndexStatistic
  9. COLUMN_NAME String => column name; null * when TYPE is tableIndexStatistic
  10. ASC_OR_DESC String => * column sort sequence, "A" => ascending "D" => descending, may be null if * sort sequence is not supported; null when TYPE is tableIndexStatistic *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then this * is the number of rows in the table; otherwise it is the number of unique * values in the index.
  12. PAGES int => When TYPE is * tableIndexStatisic then this is the number of pages used for the table, * otherwise it is the number of pages used for the current index.
  13. FILTER_CONDITION * String => Filter condition, if any. (may be null)
@param catalog a * catalog name; "" retrieves those without a catalog @param schema a schema * name pattern; "" retrieves those without a schema @param table a table * name @param unique when true, return only indices for unique values; when * false, return indices regardless of whether unique or not @param * approximate when true, result is allowed to reflect approximate or out of * data values; when false, results are requested to be accurate @return * ResultSet each row is an index column description */ // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getIndexInfo(String catalog, String schema, String tableName, boolean unique, boolean approximate) throws SQLException { String select = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "; String from = " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_index i, pg_catalog.pg_attribute a, pg_catalog.pg_am am "; String where = " AND n.oid = ct.relnamespace " + " AND " + resolveSchemaCondition("n.nspname", schema); String sql = select + " ct.relname AS TABLE_NAME, NOT i.indisunique AS NON_UNIQUE, NULL AS INDEX_QUALIFIER, ci.relname AS INDEX_NAME, " + " CASE i.indisclustered " + " WHEN true THEN " + java.sql.DatabaseMetaData.tableIndexClustered + " ELSE CASE am.amname " + " WHEN 'hash' THEN " + java.sql.DatabaseMetaData.tableIndexHashed + " ELSE " + java.sql.DatabaseMetaData.tableIndexOther + " END " + " END::int2 AS TYPE, " + " a.attnum::int2 AS ORDINAL_POSITION, " + " a.attname AS COLUMN_NAME, " + " NULL AS ASC_OR_DESC, " + " ci.reltuples AS CARDINALITY, " + " ci.relpages AS PAGES, " + " NULL AS FILTER_CONDITION " + from + " WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid AND a.attrelid=ci.oid AND ci.relam=am.oid " + where + " AND ct.relname = '" + escapeQuotes(tableName) + "' "; if(unique) { sql += " AND i.indisunique "; } sql += " ORDER BY NON_UNIQUE, TYPE, INDEX_NAME, ORDINAL_POSITION "; return createMetaDataStatement().executeQuery(sql); } // ** JDBC 2 Extensions ** /* * Does the database support the given result set type? @param type - * defined in java.sql.ResultSet @return true if so; false otherwise * @exception SQLException - if a database access error occurs */ public boolean supportsResultSetType(int type) throws SQLException { // The only type we support return type == java.sql.ResultSet.TYPE_FORWARD_ONLY; } /* * Does the database support the concurrency type in combination with the * given result set type? @param type - defined in java.sql.ResultSet @param * concurrency - type defined in java.sql.ResultSet @return true if so; * false otherwise @exception SQLException - if a database access error * occurs */ public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { // These combinations are not supported! if(type != java.sql.ResultSet.TYPE_FORWARD_ONLY) return false; // We support only Concur Read Only if(concurrency != java.sql.ResultSet.CONCUR_READ_ONLY) return false; // Everything else we do return true; } /* lots of unsupported stuff... */ public boolean ownUpdatesAreVisible(int type) throws SQLException { return true; } public boolean ownDeletesAreVisible(int type) throws SQLException { return true; } public boolean ownInsertsAreVisible(int type) throws SQLException { // indicates that return true; } public boolean othersUpdatesAreVisible(int type) throws SQLException { return false; } public boolean othersDeletesAreVisible(int i) throws SQLException { return false; } public boolean othersInsertsAreVisible(int type) throws SQLException { return false; } public boolean updatesAreDetected(int type) throws SQLException { return false; } public boolean deletesAreDetected(int i) throws SQLException { return false; } public boolean insertsAreDetected(int type) throws SQLException { return false; } /* * Indicates whether the driver supports batch updates. */ public boolean supportsBatchUpdates() throws SQLException { return true; } /** * @param catalog String * @param schemaPattern String * @param typeNamePattern String * @param types int[] * @throws SQLException * @return ResultSet */ public java.sql.ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { String sql = "select " + "null as type_cat, n.nspname as type_schem, t.typname as type_name, null as class_name, " + "CASE WHEN t.typtype='c' then " + java.sql.Types.STRUCT + " else " + java.sql.Types.DISTINCT + " end as data_type, pg_catalog.obj_description(t.oid, 'pg_type') " + "as remarks, CASE WHEN t.typtype = 'd' then (select CASE"; for(int i = 0; i < SPIConnection.JDBC3_TYPE_NAMES.length; i++) { sql += " when typname = '" + SPIConnection.JDBC_TYPE_NUMBERS[i] + "' then " + SPIConnection.JDBC_TYPE_NUMBERS[i]; } sql += " else " + java.sql.Types.OTHER + " end from pg_type where oid=t.typbasetype) " + "else null end as base_type " + "from pg_catalog.pg_type t, pg_catalog.pg_namespace n where t.typnamespace = n.oid and n.nspname != 'pg_catalog' and n.nspname != 'pg_toast'"; String toAdd = ""; if(types != null) { toAdd += " and (false "; for(int i = 0; i < types.length; i++) { switch(types[i]) { case java.sql.Types.STRUCT: toAdd += " or t.typtype = 'c'"; break; case java.sql.Types.DISTINCT: toAdd += " or t.typtype = 'd'"; break; } } toAdd += " ) "; } else { toAdd += " and t.typtype IN ('c','d') "; } // spec says that if typeNamePattern is a fully qualified name // then the schema and catalog are ignored if(typeNamePattern != null) { // search for qualifier int firstQualifier = typeNamePattern.indexOf('.'); int secondQualifier = typeNamePattern.lastIndexOf('.'); if(firstQualifier != -1) // if one of them is -1 they both will // be { if(firstQualifier != secondQualifier) { // we have a catalog.schema.typename, ignore catalog schemaPattern = typeNamePattern.substring( firstQualifier + 1, secondQualifier); } else { // we just have a schema.typename schemaPattern = typeNamePattern .substring(0, firstQualifier); } // strip out just the typeName typeNamePattern = typeNamePattern .substring(secondQualifier + 1); } toAdd += " and t.typname like '" + escapeQuotes(typeNamePattern) + "'"; } // schemaPattern may have been modified above if(schemaPattern != null) { toAdd += " and n.nspname like '" + escapeQuotes(schemaPattern) + "'"; } sql += toAdd; sql += " order by data_type, type_schem, type_name"; java.sql.ResultSet rs = createMetaDataStatement().executeQuery(sql); return rs; } /* * Retrieves the connection that produced this metadata object. @return the * connection that produced this metadata object */ public Connection getConnection() throws SQLException { return m_connection; } /* I don't find these in the spec!?! */ public boolean rowChangesAreDetected(int type) throws SQLException { return false; } public boolean rowChangesAreVisible(int type) throws SQLException { return false; } private Statement createMetaDataStatement() throws SQLException { return m_connection.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); } /** * Retrieves whether this database supports savepoints. * * @return true if savepoints are supported; * false otherwise * @exception SQLException if a database access error occurs * @since 1.4 */ public boolean supportsSavepoints() throws SQLException { return this.getDatabaseMajorVersion() >= 8; } /** * Retrieves whether this database supports named parameters to callable * statements. * * @return true if named parameters are supported; * false otherwise * @exception SQLException if a database access error occurs * @since 1.4 */ public boolean supportsNamedParameters() throws SQLException { return false; } /** * Retrieves whether it is possible to have multiple ResultSet * objects returned from a CallableStatement object * simultaneously. * * @return true if a CallableStatement object * can return multiple ResultSet objects * simultaneously; false otherwise * @exception SQLException if a datanase access error occurs * @since 1.4 */ public boolean supportsMultipleOpenResults() throws SQLException { return false; } /** * Retrieves whether auto-generated keys can be retrieved after a statement * has been executed. * * @return true if auto-generated keys can be retrieved after * a statement has executed; false otherwise * @exception SQLException if a database access error occurs * @since 1.4 */ public boolean supportsGetGeneratedKeys() throws SQLException { return false; } /** * Retrieves a description of the user-defined type (UDT) hierarchies * defined in a particular schema in this database. Only the immediate super * type/ sub type relationship is modeled. *

* Only supertype information for UDTs matching the catalog, schema, and * type name is returned. The type name parameter may be a fully-qualified * name. When the UDT name supplied is a fully-qualified name, the catalog * and schemaPattern parameters are ignored. *

* If a UDT does not have a direct super type, it is not listed here. A row * of the ResultSet object returned by this method describes * the designated UDT and a direct supertype. A row has the following * columns: *

    *
  1. TYPE_CAT String => the UDT's catalog (may be * null) *
  2. TYPE_SCHEM String => UDT's schema (may be null) *
  3. TYPE_NAME String => type name of the UDT *
  4. SUPERTYPE_CAT String => the direct super type's catalog (may * be null) *
  5. SUPERTYPE_SCHEM String => the direct super type's schema * (may be null) *
  6. SUPERTYPE_NAME String => the direct super type's name *
*

* Note: If the driver does not support type hierarchies, an empty * result set is returned. * * @param catalog a catalog name; "" retrieves those without a catalog; * null means drop catalog name from the selection * criteria * @param schemaPattern a schema name pattern; "" retrieves those without a * schema * @param typeNamePattern a UDT name pattern; may be a fully-qualified name * @return a ResultSet object in which a row gives * information about the designated UDT * @throws SQLException if a database access error occurs * @since 1.4 */ public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { throw new UnsupportedFeatureException("DatabaseMetaData.getSuperTypes"); } /** * Retrieves a description of the table hierarchies defined in a particular * schema in this database. *

* Only supertable information for tables matching the catalog, schema and * table name are returned. The table name parameter may be a fully- * qualified name, in which case, the catalog and schemaPattern parameters * are ignored. If a table does not have a super table, it is not listed * here. Supertables have to be defined in the same catalog and schema as * the sub tables. Therefore, the type description does not need to include * this information for the supertable. *

* Each type description has the following columns: *

    *
  1. TABLE_CAT String => the type's catalog (may be * null) *
  2. TABLE_SCHEM String => type's schema (may be * null) *
  3. TABLE_NAME String => type name *
  4. SUPERTABLE_NAME String => the direct super type's name *
*

* Note: If the driver does not support type hierarchies, an empty * result set is returned. * * @param catalog a catalog name; "" retrieves those without a catalog; * null means drop catalog name from the selection * criteria * @param schemaPattern a schema name pattern; "" retrieves those without a * schema * @param tableNamePattern a table name pattern; may be a fully-qualified * name * @return a ResultSet object in which each row is a type * description * @throws SQLException if a database access error occurs * @since 1.4 */ public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { throw new UnsupportedFeatureException("DatabaseMetaData.getSuperTables"); } /** * Retrieves a description of the given attribute of the given type for a * user-defined type (UDT) that is available in the given schema and * catalog. *

* Descriptions are returned only for attributes of UDTs matching the * catalog, schema, type, and attribute name criteria. They are ordered by * TYPE_SCHEM, TYPE_NAME and ORDINAL_POSITION. This description does not * contain inherited attributes. *

* The ResultSet object that is returned has the following * columns: *

    *
  1. TYPE_CAT String => type catalog (may be null) *
  2. TYPE_SCHEM String => type schema (may be null) *
  3. TYPE_NAME String => type name *
  4. ATTR_NAME String => attribute name *
  5. DATA_TYPE short => attribute type SQL type from * java.sql.Types *
  6. ATTR_TYPE_NAME String => Data source dependent type name. * For a UDT, the type name is fully qualified. For a REF, the type name is * fully qualified and represents the target type of the reference type. *
  7. ATTR_SIZE int => column size. For char or date types this is * the maximum number of characters; for numeric or decimal types this is * precision. *
  8. DECIMAL_DIGITS int => the number of fractional digits *
  9. NUM_PREC_RADIX int => Radix (typically either 10 or 2) *
  10. NULLABLE int => whether NULL is allowed *
      *
    • attributeNoNulls - might not allow NULL values *
    • attributeNullable - definitely allows NULL values *
    • attributeNullableUnknown - nullability unknown *
    *
  11. REMARKS String => comment describing column (may be * null) *
  12. ATTR_DEF String => default value (may be null) *
  13. SQL_DATA_TYPE int => unused *
  14. SQL_DATETIME_SUB int => unused *
  15. CHAR_OCTET_LENGTH int => for char types the maximum number * of bytes in the column *
  16. ORDINAL_POSITION int => index of column in table (starting * at 1) *
  17. IS_NULLABLE String => "NO" means column definitely does not * allow NULL values; "YES" means the column might allow NULL values. An * empty string means unknown. *
  18. SCOPE_CATALOG String => catalog of table that is the scope * of a reference attribute (null if DATA_TYPE isn't REF) *
  19. SCOPE_SCHEMA String => schema of table that is the scope of * a reference attribute (null if DATA_TYPE isn't REF) *
  20. SCOPE_TABLE String => table name that is the scope of a * reference attribute (null if the DATA_TYPE isn't REF) *
  21. SOURCE_DATA_TYPE short => source type of a distinct type or * user-generated Ref type,SQL type from java.sql.Types (null * if DATA_TYPE isn't DISTINCT or user-generated REF) *
* * @param catalog a catalog name; must match the catalog name as it is * stored in the database; "" retrieves those without a catalog; * null means that the catalog name should not be * used to narrow the search * @param schemaPattern a schema name pattern; must match the schema name as * it is stored in the database; "" retrieves those without a * schema; null means that the schema name should * not be used to narrow the search * @param typeNamePattern a type name pattern; must match the type name as * it is stored in the database * @param attributeNamePattern an attribute name pattern; must match the * attribute name as it is declared in the database * @return a ResultSet object in which each row is an * attribute description * @exception SQLException if a database access error occurs * @since 1.4 */ public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException { throw new UnsupportedFeatureException("getAttributes"); } /** * Retrieves whether this database supports the given result set * holdability. * * @param holdability one of the following constants: * ResultSet.HOLD_CURSORS_OVER_COMMIT or * ResultSet.CLOSE_CURSORS_AT_COMMIT * @return true if so; false otherwise * @exception SQLException if a database access error occurs * @see Connection * @since 1.4 */ public boolean supportsResultSetHoldability(int holdability) throws SQLException { return true; } /** * Retrieves the default holdability of this ResultSet * object. * * @return the default holdability; either * ResultSet.HOLD_CURSORS_OVER_COMMIT or * ResultSet.CLOSE_CURSORS_AT_COMMIT * @exception SQLException if a database access error occurs * @since 1.4 */ public int getResultSetHoldability() throws SQLException { return ResultSet.HOLD_CURSORS_OVER_COMMIT; } /** * Retrieves the major version number of the underlying database. * * @return the underlying database's major version * @exception SQLException if a database access error occurs * @since 1.4 */ public int getDatabaseMajorVersion() throws SQLException { return m_connection.getVersionNumber()[0]; } /** * Retrieves the minor version number of the underlying database. * * @return underlying database's minor version * @exception SQLException if a database access error occurs * @since 1.4 */ public int getDatabaseMinorVersion() throws SQLException { return m_connection.getVersionNumber()[1]; } /** * Retrieves the major JDBC version number for this driver. * * @return JDBC version major number * @exception SQLException if a database access error occurs * @since 1.4 */ public int getJDBCMajorVersion() throws SQLException { return 3; // This class implements JDBC 3.0 } /** * Retrieves the minor JDBC version number for this driver. * * @return JDBC version minor number * @exception SQLException if a database access error occurs * @since 1.4 */ public int getJDBCMinorVersion() throws SQLException { return 0; // This class implements JDBC 3.0 } /** * Indicates whether the SQLSTATEs returned by * SQLException.getSQLState is X/Open (now known as Open * Group) SQL CLI or SQL99. * * @return the type of SQLSTATEs, one of: sqlStateXOpen or sqlStateSQL99 * @throws SQLException if a database access error occurs * @since 1.4 */ public int getSQLStateType() throws SQLException { return DatabaseMetaData.sqlStateSQL99; } /** * Indicates whether updates made to a LOB are made on a copy or directly to * the LOB. * * @return true if updates are made to a copy of the LOB; * false if updates are made directly to the LOB * @throws SQLException if a database access error occurs * @since 1.4 */ public boolean locatorsUpdateCopy() throws SQLException { /* * Currently LOB's aren't updateable at all, so it doesn't matter what * we return. We don't throw the notImplemented Exception because the * 1.5 JDK's CachedRowSet calls this method regardless of wether large * objects are used. */ return true; } /** * Retrieves weather this database supports statement pooling. * * @return true is so; false otherwise * @throws SQLExcpetion if a database access error occurs * @since 1.4 */ public boolean supportsStatementPooling() throws SQLException { return false; } /** * This method creates a ResultSet which is not associated with any * statement. */ private ResultSet createSyntheticResultSet(ResultSetField[] f, ArrayList tuples) throws SQLException { return new SyntheticResultSet(f, tuples); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SyntheticResultSetMetaData.java0000644000014500000120000000735511634451404030364 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import org.postgresql.pljava.internal.Oid; /** * Implementation of ResultSetMetaData for SyntheticResultSet * * @author Filip Hrbek */ public class SyntheticResultSetMetaData extends AbstractResultSetMetaData { private final ResultSetField[] m_fields; /** * Constructor. * @param fields Array of ResultSetField */ public SyntheticResultSetMetaData(ResultSetField[] fields) { super(); m_fields = fields; } /** * Returns the number of columns in this ResultSet object. * * @return the number of columns * @exception SQLException if a database access error occurs */ public final int getColumnCount() throws SQLException { return m_fields.length; } /** * Indicates whether the designated column is automatically numbered, thus read-only. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isAutoIncrement(int column) throws SQLException { checkColumnIndex(column); //SyntheticResultSet has no autoincrement columns return false; } /** * Gets the designated column's suggested title for use in printouts and * displays. * * @param column the first column is 1, the second is 2, ... * @return the suggested column title * @exception SQLException if a database access error occurs */ public final String getColumnLabel(int column) throws SQLException { checkColumnIndex(column); return m_fields[column-1].getColumnLabel(); } //--------------------------JDBC 2.0----------------------------------- /** *

Returns the fully-qualified name of the Java class whose instances * are manufactured if the method ResultSet.getObject * is called to retrieve a value * from the column. ResultSet.getObject may return a subclass of the * class returned by this method. * * @param column the first column is 1, the second is 2, ... * @return the fully-qualified name of the class in the Java programming * language that would be used by the method * ResultSet.getObject to retrieve the value in the specified * column. This is the class name used for custom mapping. * @exception SQLException if a database access error occurs * @since 1.2 */ public final String getColumnClassName(int column) throws SQLException { checkColumnIndex(column); return m_fields[column-1].getJavaClass().getName(); } /** * Checks if the column index is valid. * * @param column the first column is 1, the second is 2, ... * @exception SQLException if the column is out of index bounds */ protected final void checkColumnIndex(int column) throws SQLException { if (column < 1 || column > m_fields.length) { throw new SQLException("Invalid column index: " + column); } } /** * Gets column OID * @param column Column index * @return column OID * @throws SQLException if an error occurs */ protected final Oid getOid(int column) throws SQLException { return m_fields[column-1].getOID(); } /** * Gets column length * @param column Column index * @return column length * @throws SQLException if an error occurs */ protected final int getFieldLength(int column) throws SQLException { return m_fields[column-1].getLength(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SingleRowResultSet.java0000644000014500000120000001327311634451404026716 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSetMetaData; import java.sql.SQLException; import org.postgresql.pljava.internal.TupleDesc; /** * A single row ResultSet * * @author Thomas Hallgren */ public abstract class SingleRowResultSet extends ObjectResultSet { public int getConcurrency() throws SQLException { return CONCUR_UPDATABLE; } public int findColumn(String columnName) throws SQLException { return this.getTupleDesc().getColumnIndex(columnName); } public int getFetchDirection() throws SQLException { return FETCH_FORWARD; } public int getFetchSize() throws SQLException { return 1; } /** * Returns the metadata for this result set. */ public ResultSetMetaData getMetaData() throws SQLException { return new SPIResultSetMetaData(this.getTupleDesc()); } public int getRow() throws SQLException { return 1; } public int getType() throws SQLException { return TYPE_FORWARD_ONLY; } /** * Cursor positoning is not implemented. * @throws SQLException indicating that this feature is not supported. */ public void afterLast() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Cursor positoning is not implemented. * @throws SQLException indicating that this feature is not supported. */ public void beforeFirst() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Cursor positioning is not implemented. * @throws SQLException indicating that this feature is not supported. */ public boolean first() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Returns false. */ public boolean isAfterLast() throws SQLException { return false; } /** * Will always return false since a SingleRowWriter * starts on the one and only row. */ public boolean isBeforeFirst() throws SQLException { return false; } /** * Returns true. */ public boolean isFirst() throws SQLException { return true; } /** * Returns true. */ public boolean isLast() throws SQLException { return true; } /** * Cursor positioning is not implemented. * @throws SQLException indicating that this feature is not supported. */ public boolean last() throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * This method will always return false but it will not change * the state of the ResultSet. */ public boolean next() throws SQLException { return false; } /** * This method will always return false but it will not change * the state of the ResultSet. */ public boolean previous() throws SQLException { return false; } /** * Only {@link java.sql.ResultSet#FETCH_FORWARD} is supported. * @throws SQLException indicating that this feature is not supported * for other values on direction. */ public void setFetchDirection(int direction) throws SQLException { if(direction != FETCH_FORWARD) throw new UnsupportedFeatureException("Non forward fetch direction"); } /** * Only permitted value for fetchSize is 1. */ public void setFetchSize(int fetchSize) throws SQLException { if(fetchSize != 1) throw new IllegalArgumentException("Illegal fetch size for single row set"); } /** * Cursor positioning is not supported. * @throws SQLException indicating that this feature is not supported. */ public boolean absolute(int row) throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * Cursor positioning is not supported. * @throws SQLException indicating that this feature is not supported. */ public boolean relative(int rows) throws SQLException { throw new UnsupportedFeatureException("Cursor positioning"); } /** * This feature is not supported. * @throws SQLException indicating that this feature is not supported. */ public void deleteRow() throws SQLException { throw new UnsupportedFeatureException("Deletes not supported on single row set"); } /** * This feature is not supported. * @throws SQLException indicating that this feature is not supported. */ public void insertRow() throws SQLException { throw new UnsupportedFeatureException("Inserts not supported on single row set"); } /** * This is a no-op since the moveToInsertRow() method is * unsupported. */ public void moveToCurrentRow() throws SQLException { } /** * This feature is not supported on a SingleRowWriter. * @throws SQLException indicating that this feature is not supported. */ public void moveToInsertRow() throws SQLException { throw new UnsupportedFeatureException("Inserts not supported on single row set"); } /** * This is a noop. */ public void updateRow() throws SQLException { } /** * Will always return false. */ public boolean rowDeleted() throws SQLException { return false; } /** * Will always return false. */ public boolean rowInserted() throws SQLException { return false; } /** * The scale is not really supported. This method just strips it off and * calls {@link #updateObject(int, Object)} */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { // Simply drop the scale. // this.updateObject(columnIndex, x); } protected abstract TupleDesc getTupleDesc() throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/.#TypeOid.java.1.30000644000014500000120000000237511634451403025130 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import org.postgresql.pljava.internal.Oid; /** * Provides constants for well-known backend OIDs for the types we commonly * use. */ public class TypeOid { public static final Oid INVALID = new Oid(0); public static final Oid INT2 = new Oid(21); public static final Oid INT4 = new Oid(23); public static final Oid INT8 = new Oid(20); public static final Oid TEXT = new Oid(25); public static final Oid NUMERIC = new Oid(1700); public static final Oid FLOAT4 = new Oid(700); public static final Oid FLOAT8 = new Oid(701); public static final Oid BOOL = new Oid(16); public static final Oid DATE = new Oid(1082); public static final Oid TIME = new Oid(1083); public static final Oid TIMESTAMP = new Oid(1114); public static final Oid TIMESTAMPTZ = new Oid(1184); public static final Oid BYTEA = new Oid(17); public static final Oid VARCHAR = new Oid(1043); public static final Oid OID = new Oid(26); public static final Oid BPCHAR = new Oid(1042); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/.#SingleRowReader.java.1.50000644000014500000120000000673211634451404026613 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, read-only ResultSet, specially made for functions and * procedures that takes complex types as arguments (PostgreSQL 7.5 * and later). * * @author Thomas Hallgren */ public class SingleRowReader extends SingleRowResultSet { private TupleDesc m_tupleDesc; private final long m_pointer; public SingleRowReader(long pointer, TupleDesc tupleDesc) throws SQLException { m_pointer = pointer; m_tupleDesc = tupleDesc; } public void close() { } public void finalize() { synchronized(Backend.THREADLOCK) { _free(m_pointer); m_tupleDesc = null; } } protected Object getObjectValue(int columnIndex) throws SQLException { synchronized(Backend.THREADLOCK) { return _getObject(m_pointer, m_tupleDesc.getNativePointer(), columnIndex); } } /** * Returns {@link ResultSet#CONCUR_READ_ONLY}. */ public int getConcurrency() throws SQLException { return ResultSet.CONCUR_READ_ONLY; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void cancelRowUpdates() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void deleteRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void insertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void moveToInsertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateRow() throws SQLException { throw readOnlyException(); } /** * Always returns false. */ public boolean rowUpdated() throws SQLException { return false; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x) throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw readOnlyException(); } private static SQLException readOnlyException() { return new UnsupportedFeatureException("ResultSet is read-only"); } protected final TupleDesc getTupleDesc() { return m_tupleDesc; } protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/ObjectResultSet.java0000644000014500000120000002277111634451404026216 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Time; import java.sql.Timestamp; import java.math.BigDecimal; import java.net.URL; import java.util.Calendar; import java.util.Map; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; /** * @author Thomas Hallgren */ public abstract class ObjectResultSet extends AbstractResultSet { private boolean m_wasNull = false; /** * This is a noop since warnings are not supported. */ public void clearWarnings() throws SQLException { } public Array getArray(int columnIndex) throws SQLException { return (Array)this.getValue(columnIndex, Array.class); } public InputStream getAsciiStream(int columnIndex) throws SQLException { Clob c = this.getClob(columnIndex); return (c == null) ? null : c.getAsciiStream(); } public BigDecimal getBigDecimal(int columnIndex) throws SQLException { return (BigDecimal)this.getValue(columnIndex, BigDecimal.class); } /** * @deprecated */ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { throw new UnsupportedFeatureException("getBigDecimal(int, int)"); } public InputStream getBinaryStream(int columnIndex) throws SQLException { Blob b = this.getBlob(columnIndex); return (b == null) ? null : b.getBinaryStream(); } public Blob getBlob(int columnIndex) throws SQLException { byte[] bytes = this.getBytes(columnIndex); return (bytes == null) ? null : new BlobValue(bytes); } public boolean getBoolean(int columnIndex) throws SQLException { Boolean b = (Boolean)this.getValue(columnIndex, Boolean.class); return (b == null) ? false : b.booleanValue(); } public byte getByte(int columnIndex) throws SQLException { Number b = this.getNumber(columnIndex, byte.class); return (b == null) ? 0 : b.byteValue(); } public byte[] getBytes(int columnIndex) throws SQLException { return (byte[])this.getValue(columnIndex, byte[].class); } public Reader getCharacterStream(int columnIndex) throws SQLException { Clob c = this.getClob(columnIndex); return (c == null) ? null : c.getCharacterStream(); } public Clob getClob(int columnIndex) throws SQLException { String str = this.getString(columnIndex); return (str == null) ? null : new ClobValue(str); } public Date getDate(int columnIndex) throws SQLException { return (Date)this.getValue(columnIndex, Date.class); } public Date getDate(int columnIndex, Calendar cal) throws SQLException { return (Date)this.getValue(columnIndex, Date.class, cal); } public double getDouble(int columnIndex) throws SQLException { Number d = this.getNumber(columnIndex, double.class); return (d == null) ? 0 : d.doubleValue(); } public float getFloat(int columnIndex) throws SQLException { Number f = this.getNumber(columnIndex, float.class); return (f == null) ? 0 : f.floatValue(); } public int getInt(int columnIndex) throws SQLException { Number i = this.getNumber(columnIndex, int.class); return (i == null) ? 0 : i.intValue(); } public long getLong(int columnIndex) throws SQLException { Number l = this.getNumber(columnIndex, long.class); return (l == null) ? 0 : l.longValue(); } /** * ResultSetMetaData is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public ResultSetMetaData getMetaData() throws SQLException { throw new UnsupportedFeatureException("ResultSet meta data is not yet implemented"); } public final Object getObject(int columnIndex) throws SQLException { Object value = this.getObjectValue(columnIndex); m_wasNull = (value == null); return value; } public final Object getObject(int columnIndex, Map map) throws SQLException { Object value = this.getObjectValue(columnIndex, map); m_wasNull = (value == null); return value; } public Ref getRef(int columnIndex) throws SQLException { return (Ref)this.getValue(columnIndex, Ref.class); } public short getShort(int columnIndex) throws SQLException { Number s = this.getNumber(columnIndex, short.class); return (s == null) ? 0 : s.shortValue(); } public String getString(int columnIndex) throws SQLException { return (String)this.getValue(columnIndex, String.class); } public Time getTime(int columnIndex) throws SQLException { return (Time)this.getValue(columnIndex, Time.class); } public Time getTime(int columnIndex, Calendar cal) throws SQLException { return (Time)this.getValue(columnIndex, Time.class, cal); } public Timestamp getTimestamp(int columnIndex) throws SQLException { return (Timestamp)this.getValue(columnIndex, Timestamp.class); } public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { return (Timestamp)this.getValue(columnIndex, Timestamp.class, cal); } /** * @deprecated */ public InputStream getUnicodeStream(int columnIndex) throws SQLException { throw new UnsupportedFeatureException("ResultSet.getUnicodeStream"); } public URL getURL(int columnIndex) throws SQLException { return (URL)this.getValue(columnIndex, URL.class); } public SQLWarning getWarnings() throws SQLException { return null; } /** * Refresh row is not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public void refreshRow() throws SQLException { throw new UnsupportedFeatureException("Refresh row"); } public void updateArray(int columnIndex, Array x) throws SQLException { this.updateObject(columnIndex, x); } public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { try { this.updateObject(columnIndex, new ClobValue(new InputStreamReader(x, "US-ASCII"), length)); } catch(UnsupportedEncodingException e) { throw new SQLException("US-ASCII encoding is not supported by this JVM"); } } public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { this.updateObject(columnIndex, x); } public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { this.updateBlob(columnIndex, (Blob) new BlobValue(x, length)); } public void updateBlob(int columnIndex, Blob x) throws SQLException { this.updateObject(columnIndex, x); } public void updateBoolean(int columnIndex, boolean x) throws SQLException { this.updateObject(columnIndex, x ? Boolean.TRUE : Boolean.FALSE); } public void updateByte(int columnIndex, byte x) throws SQLException { this.updateObject(columnIndex, new Byte(x)); } public void updateBytes(int columnIndex, byte[] x) throws SQLException { this.updateObject(columnIndex, x); } public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { this.updateClob(columnIndex, (Clob) new ClobValue(x, length)); } public void updateClob(int columnIndex, Clob x) throws SQLException { this.updateObject(columnIndex, x); } public void updateDate(int columnIndex, Date x) throws SQLException { this.updateObject(columnIndex, x); } public void updateDouble(int columnIndex, double x) throws SQLException { this.updateObject(columnIndex, new Double(x)); } public void updateFloat(int columnIndex, float x) throws SQLException { this.updateObject(columnIndex, new Float(x)); } public void updateInt(int columnIndex, int x) throws SQLException { this.updateObject(columnIndex, new Integer(x)); } public void updateLong(int columnIndex, long x) throws SQLException { this.updateObject(columnIndex, new Long(x)); } public void updateNull(int columnIndex) throws SQLException { this.updateObject(columnIndex, null); } public void updateRef(int columnIndex, Ref x) throws SQLException { this.updateObject(columnIndex, x); } public void updateShort(int columnIndex, short x) throws SQLException { this.updateObject(columnIndex, new Short(x)); } public void updateString(int columnIndex, String x) throws SQLException { this.updateObject(columnIndex, x); } public void updateTime(int columnIndex, Time x) throws SQLException { this.updateObject(columnIndex, x); } public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { this.updateObject(columnIndex, x); } public boolean wasNull() { return m_wasNull; } protected final Number getNumber(int columnIndex, Class cls) throws SQLException { Object value = this.getObjectValue(columnIndex); m_wasNull = (value == null); return SPIConnection.basicNumericCoersion(cls, value); } protected final Object getValue(int columnIndex, Class cls) throws SQLException { return SPIConnection.basicCoersion(cls, this.getObject(columnIndex)); } protected Object getValue(int columnIndex, Class cls, Calendar cal) throws SQLException { return SPIConnection.basicCalendricalCoersion(cls, this.getObject(columnIndex), cal); } protected Object getObjectValue(int columnIndex, Map typeMap) throws SQLException { if(typeMap == null) return this.getObjectValue(columnIndex); throw new UnsupportedFeatureException("Obtaining values using explicit Map"); } protected abstract Object getObjectValue(int columnIndex) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIDriver.java0000644000014500000120000000341511634451404024736 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.util.Properties; /** * * @author Thomas Hallgren */ public class SPIDriver implements Driver { private static final String s_defaultURL = "jdbc:default:connection"; private static final int s_defaultURLLen = s_defaultURL.length(); private static final Connection s_defaultConn = new SPIConnection(); private static final DriverPropertyInfo[] s_noInfo = new DriverPropertyInfo[0]; static { try { DriverManager.registerDriver(new SPIDriver()); } catch(SQLException e) { throw new ExceptionInInitializerError(e); } } public Connection connect(String url, Properties info) throws SQLException { return this.acceptsURL(url) ? s_defaultConn : null; } public boolean acceptsURL(String url) throws SQLException { if(url.startsWith(s_defaultURL)) { // Accept extra info at end provided the defaultURL is // delimited by a ':' // if(url.length() == s_defaultURLLen || url.charAt(s_defaultURLLen) == ':') return true; } return false; } public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { return s_noInfo; } public int getMajorVersion() { return 1; } public int getMinorVersion() { return 0; } public boolean jdbcCompliant() { return false; // Not all functionality is supported at present. } static Connection getDefault() { return s_defaultConn; } }pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSet.java0000644000014500000120000001767511634451404026562 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.InputStream; import java.io.Reader; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.math.BigDecimal; import java.net.URL; import java.util.Calendar; import java.util.Map; /** * The AbstractResultSet serves as a base class for implementations * of the{@link java.sql.ResultSet} interface. All calls using columnNames are * translated into the corresponding call with index position computed using * a call to {@link java.sql.ResultSet#findColumn(String) findColumn}. * * @author Thomas Hallgren */ public abstract class AbstractResultSet implements ResultSet { public Array getArray(String columnName) throws SQLException { return this.getArray(this.findColumn(columnName)); } public InputStream getAsciiStream(String columnName) throws SQLException { return this.getAsciiStream(this.findColumn(columnName)); } public BigDecimal getBigDecimal(String columnName) throws SQLException { return this.getBigDecimal(this.findColumn(columnName)); } /** * @deprecated */ public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { return this.getBigDecimal(this.findColumn(columnName), scale); } public InputStream getBinaryStream(String columnName) throws SQLException { return this.getBinaryStream(this.findColumn(columnName)); } public Blob getBlob(String columnName) throws SQLException { return this.getBlob(this.findColumn(columnName)); } public boolean getBoolean(String columnName) throws SQLException { return this.getBoolean(this.findColumn(columnName)); } public byte getByte(String columnName) throws SQLException { return this.getByte(this.findColumn(columnName)); } public byte[] getBytes(String columnName) throws SQLException { return this.getBytes(this.findColumn(columnName)); } public Reader getCharacterStream(String columnName) throws SQLException { return this.getCharacterStream(this.findColumn(columnName)); } public Clob getClob(String columnName) throws SQLException { return this.getClob(this.findColumn(columnName)); } public String getCursorName() throws SQLException { return null; } public Date getDate(String columnName) throws SQLException { return this.getDate(this.findColumn(columnName)); } public Date getDate(String columnName, Calendar cal) throws SQLException { return this.getDate(this.findColumn(columnName), cal); } public double getDouble(String columnName) throws SQLException { return this.getDouble(this.findColumn(columnName)); } public float getFloat(String columnName) throws SQLException { return this.getFloat(this.findColumn(columnName)); } public int getInt(String columnName) throws SQLException { return this.getInt(this.findColumn(columnName)); } public long getLong(String columnName) throws SQLException { return this.getLong(this.findColumn(columnName)); } public Object getObject(String columnName) throws SQLException { return this.getObject(this.findColumn(columnName)); } public Object getObject(String columnName, Map map) throws SQLException { return this.getObject(this.findColumn(columnName), map); } public Ref getRef(String columnName) throws SQLException { return this.getRef(this.findColumn(columnName)); } public short getShort(String columnName) throws SQLException { return this.getShort(this.findColumn(columnName)); } public Statement getStatement() throws SQLException { return null; } public String getString(String columnName) throws SQLException { return this.getString(this.findColumn(columnName)); } public Time getTime(String columnName) throws SQLException { return this.getTime(this.findColumn(columnName)); } public Time getTime(String columnName, Calendar cal) throws SQLException { return this.getTime(this.findColumn(columnName), cal); } public Timestamp getTimestamp(String columnName) throws SQLException { return this.getTimestamp(this.findColumn(columnName)); } public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { return this.getTimestamp(this.findColumn(columnName), cal); } /** * @deprecated */ public InputStream getUnicodeStream(String columnName) throws SQLException { return this.getUnicodeStream(this.findColumn(columnName)); } public URL getURL(String columnName) throws SQLException { return this.getURL(this.findColumn(columnName)); } public void updateArray(String columnName, Array x) throws SQLException { this.updateArray(this.findColumn(columnName), x); } public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException { this.updateAsciiStream(this.findColumn(columnName), x, length); } public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { this.updateBigDecimal(this.findColumn(columnName), x); } public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException { this.updateBinaryStream(this.findColumn(columnName), x, length); } public void updateBlob(String columnName, Blob x) throws SQLException { this.updateBlob(this.findColumn(columnName), x); } public void updateBoolean(String columnName, boolean x) throws SQLException { this.updateBoolean(this.findColumn(columnName), x); } public void updateByte(String columnName, byte x) throws SQLException { this.updateByte(this.findColumn(columnName), x); } public void updateBytes(String columnName, byte x[]) throws SQLException { this.updateBytes(this.findColumn(columnName), x); } public void updateCharacterStream(String columnName, Reader x, int length) throws SQLException { this.updateCharacterStream(this.findColumn(columnName), x, length); } public void updateClob(String columnName, Clob x) throws SQLException { this.updateClob(this.findColumn(columnName), x); } public void updateDate(String columnName, Date x) throws SQLException { this.updateDate(this.findColumn(columnName), x); } public void updateDouble(String columnName, double x) throws SQLException { this.updateDouble(this.findColumn(columnName), x); } public void updateFloat(String columnName, float x) throws SQLException { this.updateFloat(this.findColumn(columnName), x); } public void updateInt(String columnName, int x) throws SQLException { this.updateInt(this.findColumn(columnName), x); } public void updateLong(String columnName, long x) throws SQLException { this.updateLong(this.findColumn(columnName), x); } public void updateNull(String columnName) throws SQLException { this.updateNull(this.findColumn(columnName)); } public void updateObject(String columnName, Object x) throws SQLException { this.updateObject(this.findColumn(columnName), x); } public void updateObject(String columnName, Object x, int scale) throws SQLException { this.updateObject(this.findColumn(columnName), x, scale); } public void updateRef(String columnName, Ref x) throws SQLException { this.updateRef(this.findColumn(columnName), x); } public void updateShort(String columnName, short x) throws SQLException { this.updateShort(this.findColumn(columnName), x); } public void updateString(String columnName, String x) throws SQLException { this.updateString(this.findColumn(columnName), x); } public void updateTime(String columnName, Time x) throws SQLException { this.updateTime(this.findColumn(columnName), x); } public void updateTimestamp(String columnName, Timestamp x) throws SQLException { this.updateTimestamp(this.findColumn(columnName), x); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java~0000644000014500000120000005733311634451404026010 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.BitSet; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.postgresql.pljava.internal.Oid; import org.postgresql.pljava.internal.PgSavepoint; /** * @author Thomas Hallgren */ public class SPIConnection implements Connection { private static final HashMap s_sqlType2Class = new HashMap(30); private int[] VERSION_NUMBER = null; //version number static { addType(String.class, Types.VARCHAR); addType(Byte.class, Types.TINYINT); addType(Short.class, Types.SMALLINT); addType(Integer.class, Types.INTEGER); addType(Long.class, Types.BIGINT); addType(Float.class, Types.FLOAT); addType(Double.class, Types.DOUBLE); addType(BigDecimal.class, Types.DECIMAL); addType(BigInteger.class, Types.NUMERIC); addType(Boolean.class, Types.BOOLEAN); addType(Blob.class, Types.BLOB); addType(Clob.class, Types.CLOB); addType(Date.class, Types.DATE); addType(Time.class, Types.TIME); addType(Timestamp.class, Types.TIMESTAMP); addType(java.util.Date.class, Types.TIMESTAMP); addType(byte[].class, Types.VARBINARY); addType(BitSet.class, Types.BIT); addType(URL.class, Types.DATALINK); } private static final void addType(Class clazz, int sqlType) { s_sqlType2Class.put(clazz, new Integer(sqlType)); } /** * Returns a default connection instance. It is the callers responsability * to close this instance. */ public static Connection getDefault() throws SQLException { return new SPIConnection(); } /** * Returns {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. Cursors are actually * closed when a function returns to SQL. */ public int getHoldability() throws SQLException { return ResultSet.CLOSE_CURSORS_AT_COMMIT; } /** * Returns {@link Connection#TRANSACTION_READ_COMMITTED}. */ public int getTransactionIsolation() throws SQLException { return TRANSACTION_READ_COMMITTED; } /** * Warnings are not yet supported. * @throws SQLException indicating that this feature is not supported. */ public void clearWarnings() throws SQLException { throw new UnsupportedFeatureException("Connection.clearWarnings"); } /** * This is a no-op. The default connection never closes. */ public void close() throws SQLException { } /** * It's not legal to do a commit within a call from SQL. * @throws SQLException indicating that this feature is not supported. */ public void commit() throws SQLException { throw new UnsupportedFeatureException("Connection.commit"); } /** * It's not legal to do a rollback within a call from SQL. * @throws SQLException indicating that this feature is not supported. */ public void rollback() throws SQLException { throw new UnsupportedFeatureException("Connection.rollback"); } /** * It is assumed that an SPI call is under transaction control. This method * will always return false. */ public boolean getAutoCommit() throws SQLException { return false; } /** * Will always return false. */ public boolean isClosed() throws SQLException { return false; } /** * Returns false. The SPIConnection is not real-only. */ public boolean isReadOnly() throws SQLException { return false; } /** * Change of holdability is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setHoldability(int holdability) throws SQLException { throw new UnsupportedFeatureException("Connection.setHoldability"); } /** * Change of transaction isolation level is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setTransactionIsolation(int level) throws SQLException { throw new UnsupportedFeatureException("Connection.setTransactionIsolation"); } /** * It is assumed that an SPI call is under transaction control. Changing * that is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setAutoCommit(boolean autoCommit) throws SQLException { throw new UnsupportedFeatureException("Connection.setAutoCommit"); } /** * It is assumed that an inserts and updates can be performed using and * SPIConnection. Changing that is not supported. * @throws SQLException indicating that this feature is not supported. */ public void setReadOnly(boolean readOnly) throws SQLException { throw new UnsupportedFeatureException("Connection.setReadOnly"); } /** * Returns the database in which we are running. */ public String getCatalog() throws SQLException { ResultSet rs = createStatement().executeQuery("SELECT pg_catalog.current_database()"); try { rs.next(); return rs.getString(1); } finally { rs.close(); } } /** * The catalog name cannot be set. * @throws SQLException indicating that this feature is not supported. */ public void setCatalog(String catalog) throws SQLException { throw new UnsupportedFeatureException("Connection.setCatalog"); } /** * DatabaseMetaData is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public DatabaseMetaData getMetaData() throws SQLException { return new SPIDatabaseMetaData(this); } /** * Warnings are not yet supported. * @throws SQLException indicating that this feature is not supported. */ public SQLWarning getWarnings() throws SQLException { throw new UnsupportedFeatureException("Connection.getWarnings"); } public void releaseSavepoint(Savepoint savepoint) throws SQLException { if(!(savepoint instanceof PgSavepoint)) throw new IllegalArgumentException("Not a PL/Java Savepoint"); PgSavepoint sp = (PgSavepoint)savepoint; sp.release(); forgetSavepoint(sp); } public void rollback(Savepoint savepoint) throws SQLException { if(!(savepoint instanceof PgSavepoint)) throw new IllegalArgumentException("Not a PL/Java Savepoint"); PgSavepoint sp = (PgSavepoint)savepoint; Invocation.clearErrorCondition(); sp.rollback(); forgetSavepoint(sp); } /** * Creates a new instance of SPIStatement. */ public Statement createStatement() throws SQLException { if(this.isClosed()) throw new SQLException("Connection is closed"); return new SPIStatement(this); } /** * Creates a new instance of SPIStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY} or if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}. */ public Statement createStatement( int resultSetType, int resultSetConcurrency) throws SQLException { if(resultSetType != ResultSet.TYPE_FORWARD_ONLY) throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type"); if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency"); return this.createStatement(); } /** * Creates a new instance of SPIStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY}, if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the * resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ public Statement createStatement( int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) throw new UnsupportedOperationException( "CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability"); return this.createStatement(resultSetType, resultSetConcurrency); } /** * Returns null. Type map is not yet imlemented. */ public Map getTypeMap() throws SQLException { return null; } /** * Type map is not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public void setTypeMap(Map map) throws SQLException { throw new UnsupportedOperationException("Type map is not yet implemented"); } /** * Parse the JDBC SQL into PostgreSQL. */ public String nativeSQL(String sql) throws SQLException { return this.nativeSQL(sql, null); } public String nativeSQL(String sql, int[] paramCountRet) { StringBuffer buf = new StringBuffer(); int len = sql.length(); char inQuote = 0; int paramIndex = 1; for(int idx = 0; idx < len; ++idx) { char c = sql.charAt(idx); switch(c) { case '\\': // Next character is escaped. Keep both // escape and the character. // buf.append(c); if(++idx == len) break; c = sql.charAt(idx); break; case '\'': case '"': // Strings within quotes should not be subject // to '?' -> '$n' substitution. // if(inQuote == c) inQuote = 0; else inQuote = c; break; case '?': if(inQuote == 0) { buf.append('$'); buf.append(paramIndex++); continue; } break; default: if(inQuote == 0 && Character.isWhitespace(c)) { // Strip of multiple whitespace outside of // strings. // ++idx; while(idx < len && Character.isWhitespace(sql.charAt(idx))) ++idx; --idx; c = ' '; } } buf.append(c); } if(paramCountRet != null) paramCountRet[0] = paramIndex - 1; return buf.toString(); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall(String sql) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Procedure calls are not yet implemented. * @throws SQLException indicating that this feature is not supported. */ public CallableStatement prepareCall( String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { throw new UnsupportedOperationException("Procedure calls are not yet implemented"); } /** * Creates a new instance of SPIPreparedStatement. */ public PreparedStatement prepareStatement(String sql) throws SQLException { if(this.isClosed()) throw new SQLException("Connection is closed"); int[] pcount = new int[] { 0 }; sql = this.nativeSQL(sql, pcount); PreparedStatement stmt = new SPIPreparedStatement(this, sql, pcount[0]); Invocation.current().manageStatement(stmt); return stmt; } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Creates a new instance of SPIPreparedStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY} or if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}. */ public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency) throws SQLException { if(resultSetType != ResultSet.TYPE_FORWARD_ONLY) throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type"); if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency"); return prepareStatement(sql); } /** * Creates a new instance of SPIPreparedStatement. * * @throws SQLException * if the resultSetType differs from {@link * ResultSet#TYPE_FORWARD_ONLY}, if the resultSetConcurrencty * differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the * resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ public PreparedStatement prepareStatement( String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) throw new UnsupportedOperationException( "CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability"); return this.prepareStatement(sql, resultSetType, resultSetConcurrency); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } /** * Return of auto generated keys is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { throw new UnsupportedFeatureException("Auto generated key support not yet implemented"); } public Savepoint setSavepoint() throws SQLException { return this.rememberSavepoint(PgSavepoint.set("anonymous_savepoint")); } public Savepoint setSavepoint(String name) throws SQLException { return this.rememberSavepoint(PgSavepoint.set(name)); } static int getTypeForClass(Class c) { if(c.isArray() && !c.equals(byte[].class)) return Types.ARRAY; Integer sqt = (Integer)s_sqlType2Class.get(c); if(sqt != null) return sqt.intValue(); /* * This is not a well known JDBC type. */ return Types.OTHER; } private Savepoint rememberSavepoint(PgSavepoint sp) throws SQLException { // Remember the first savepoint for each call-level so // that it can be released when the function call ends. Releasing // the first savepoint will release all subsequent savepoints. // Invocation invocation = Invocation.current(); Savepoint old = invocation.getSavepoint(); if(old == null) invocation.setSavepoint(sp); return sp; } private static void forgetSavepoint(PgSavepoint sp) throws SQLException { Invocation invocation = Invocation.current(); if(invocation.getSavepoint() == sp) invocation.setSavepoint(null); } public int[] getVersionNumber() throws SQLException { if (VERSION_NUMBER != null) return VERSION_NUMBER; ResultSet rs = createStatement().executeQuery( "SELECT version()"); try { if (!rs.next()) throw new SQLException( "Cannot retrieve product version number"); String ver = rs.getString(1); Pattern p = Pattern.compile( "^PostgreSQL\\s+(\\d+)\\.(\\d+)(.\\d+)?.*"); Matcher m = p.matcher(ver); if(m.matches()) { VERSION_NUMBER = new int[3]; VERSION_NUMBER[0] = Integer.parseInt(m.group(1)); VERSION_NUMBER[1] = Integer.parseInt(m.group(2)); String bugfix = m.group(3); if(bugfix != null && bugfix.length() > 1) VERSION_NUMBER[2] = Integer.parseInt(bugfix.substring(1)); return VERSION_NUMBER; } throw new SQLException( "Unexpected product version string format: " + ver); } catch (PatternSyntaxException e) { throw new SQLException( "Error in product version string parsing: " + e.getMessage()); } finally { rs.close(); } } /* * This implemetation uses the jdbc3Types array to support the jdbc3 * datatypes. Basically jdbc2 and jdbc3 are the same, except that * jdbc3 adds some */ public int getSQLType(String pgTypeName) { if (pgTypeName == null) return Types.OTHER; for (int i = 0;i < JDBC3_TYPE_NAMES.length;i++) if (pgTypeName.equals(JDBC3_TYPE_NAMES[i])) return JDBC_TYPE_NUMBERS[i]; return Types.OTHER; } /* * This returns the java.sql.Types type for a PG type oid * * @param oid PostgreSQL type oid * @return the java.sql.Types type * @exception SQLException if a database access error occurs */ public int getSQLType(Oid oid) throws SQLException { return getSQLType(getPGType(oid)); } public String getPGType(Oid oid) throws SQLException { String typeName = null; PreparedStatement query = null; ResultSet rs = null; try { query = prepareStatement("SELECT typname FROM pg_catalog.pg_type WHERE oid=?"); query.setObject(1, oid); rs = query.executeQuery(); if (rs.next()) { typeName = rs.getString(1); } else { throw new SQLException("Cannot find PG type with oid=" + oid); } } finally { if (query != null) { query.close(); } } return typeName; } static Object basicCoersion(Class cls, Object value) throws SQLException { if(value == null || cls.isInstance(value)) return value; if(cls == String.class) { if(value instanceof Number || value instanceof Boolean || value instanceof Timestamp || value instanceof Date || value instanceof Time) return value.toString(); } else if(cls == URL.class && value instanceof String) { try { return new URL((String)value); } catch(MalformedURLException e) { throw new SQLException(e.toString()); } } throw new SQLException("Cannot derive a value of class " + cls.getName() + " from an object of class " + value.getClass().getName()); } static Number basicNumericCoersion(Class cls, Object value) throws SQLException { if(value == null || value instanceof Number) return (Number)value; if(cls == int.class || cls == long.class || cls == short.class || cls == byte.class) { if(value instanceof String) return Long.valueOf((String)value); if(value instanceof Boolean) return new Long(((Boolean)value).booleanValue() ? 1 : 0); } else if(cls == BigDecimal.class) { if(value instanceof String) return new BigDecimal((String)value); if(value instanceof Boolean) return new BigDecimal(((Boolean)value).booleanValue() ? 1 : 0); } if(cls == double.class || cls == float.class) { if(value instanceof String) return Double.valueOf((String)value); if(value instanceof Boolean) return new Double(((Boolean)value).booleanValue() ? 1 : 0); } throw new SQLException("Cannot derive a Number from an object of class " + value.getClass().getName()); } static Object basicCalendricalCoersion(Class cls, Object value, Calendar cal) throws SQLException { if(value == null) return value; if(cls.isInstance(value)) return value; if(cls == Timestamp.class) { if(value instanceof Date) { cal.setTime((Date)value); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return new Timestamp(cal.getTimeInMillis()); } else if(value instanceof Time) { cal.setTime((Date)value); cal.set(1970, 0, 1); return new Timestamp(cal.getTimeInMillis()); } else if(value instanceof String) { return Timestamp.valueOf((String)value); } } else if(cls == Date.class) { if(value instanceof Timestamp) { Timestamp ts = (Timestamp)value; cal.setTime(ts); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return new Date(cal.getTimeInMillis()); } else if(value instanceof String) { return Date.valueOf((String)value); } } else if(cls == Time.class) { if(value instanceof Timestamp) { Timestamp ts = (Timestamp)value; cal.setTime(ts); cal.set(1970, 0, 1); return new Time(cal.getTimeInMillis()); } else if(value instanceof String) { return Time.valueOf((String)value); } } throw new SQLException("Cannot derive a value of class " + cls.getName() + " from an object of class " + value.getClass().getName()); } /* * This table holds the org.postgresql names for the types supported. * Any types that map to Types.OTHER (eg POINT) don't go into this table. * They default automatically to Types.OTHER * * Note: This must be in the same order as below. * * Tip: keep these grouped together by the Types. value */ public static final String JDBC3_TYPE_NAMES[] = { "int2", "int4", "oid", "int8", "cash", "money", "numeric", "float4", "float8", "bpchar", "char", "char2", "char4", "char8", "char16", "varchar", "text", "name", "filename", "bytea", "bool", "bit", "date", "time", "timetz", "abstime", "timestamp", "timestamptz", "_bool", "_char", "_int2", "_int4", "_text", "_oid", "_varchar", "_int8", "_float4", "_float8", "_abstime", "_date", "_time", "_timestamp", "_numeric", "_bytea" }; /* * This table holds the JDBC type for each entry above. * * Note: This must be in the same order as above * * Tip: keep these grouped together by the Types. value */ public static final int JDBC_TYPE_NUMBERS[] = { Types.SMALLINT, Types.INTEGER, Types.INTEGER, Types.BIGINT, Types.DOUBLE, Types.DOUBLE, Types.NUMERIC, Types.REAL, Types.DOUBLE, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BINARY, Types.BIT, Types.BIT, Types.DATE, Types.TIME, Types.TIME, Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY }; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/BuiltinFunctions.java0000644000014500000120000000713611634451404026432 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; /** * PostgreSQL builtin functions * @author Filip Hrbek */ public class BuiltinFunctions { // numeric functions names public final static String ABS="abs"; public final static String ACOS="acos"; public final static String ASIN="asin"; public final static String ATAN="atan"; public final static String ATAN2="atan2"; public final static String CEILING="ceiling"; public final static String COS="cos"; public final static String COT="cot"; public final static String DEGREES="degrees"; public final static String EXP="exp"; public final static String FLOOR="floor"; public final static String LOG="log"; public final static String LOG10="log10"; public final static String MOD="mod"; public final static String PI="pi"; public final static String POWER="power"; public final static String RADIANS="radians"; public final static String RAND="rand"; public final static String ROUND="round"; public final static String SIGN="sign"; public final static String SIN="sin"; public final static String SQRT="sqrt"; public final static String TAN="tan"; public final static String TRUNCATE="truncate"; // string function names public final static String ASCII="ascii"; public final static String CHAR="char"; public final static String CONCAT="concat"; public final static String INSERT="insert"; // change arguments order public final static String LCASE="lcase"; public final static String LEFT="left"; public final static String LENGTH="length"; public final static String LOCATE="locate"; // the 3 args version duplicate args public final static String LTRIM="ltrim"; public final static String REPEAT="repeat"; public final static String REPLACE="replace"; public final static String RIGHT="right"; // duplicate args public final static String RTRIM="rtrim"; public final static String SPACE="space"; public final static String SUBSTRING="substring"; public final static String UCASE="ucase"; // soundex is implemented on the server side by // the contrib/fuzzystrmatch module. We provide a translation // for this in the driver, but since we don't want to bother with run // time detection of this module's installation we don't report this // method as supported in DatabaseMetaData. // difference is currently unsupported entirely. // date time function names public final static String CURDATE="curdate"; public final static String CURTIME="curtime"; public final static String DAYNAME="dayname"; public final static String DAYOFMONTH="dayofmonth"; public final static String DAYOFWEEK="dayofweek"; public final static String DAYOFYEAR="dayofyear"; public final static String HOUR="hour"; public final static String MINUTE="minute"; public final static String MONTH="month"; public final static String MONTHNAME="monthname"; public final static String NOW="now"; public final static String QUARTER="quarter"; public final static String SECOND="second"; public final static String WEEK="week"; public final static String YEAR="year"; // TODO : timestampadd and timestampdiff // system functions public final static String DATABASE="database"; public final static String IFNULL="ifnull"; public final static String USER="user"; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetField.java0000644000014500000120000000427711634451404026034 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import org.postgresql.pljava.internal.Oid; /** * * @author Filip Hrbek */ public class ResultSetField { private final String m_name; private final Oid m_oid; private final int m_len; private final int m_mod; /* * Construct a field based on the information fed to it. * * @param name the name (column name and label) of the field * @param oid the OID of the field * @param len the length of the field */ public ResultSetField(String name, Oid oid, int len, int mod) throws SQLException { m_name = name.toUpperCase(); m_oid = oid; m_len = len; m_mod = mod; } /* * Constructor without mod parameter. * * @param name the name (column name and label) of the field * @param oid the OID of the field * @param len the length of the field */ public ResultSetField(String name, Oid oid, int len) throws SQLException { this(name, oid, len, 0); } /* * @return the oid of this Field's data type */ public final Oid getOID() { return m_oid; } /* * @return the Java class for oid of this Field's data type */ public final Class getJavaClass() throws SQLException { return m_oid.getJavaClass(); } /* * @return true if the field can contain a value of specified class */ public final boolean canContain(Class cls) throws SQLException { return this.getJavaClass().isAssignableFrom(cls); } /* * @return the mod of this Field's data type */ public final int getMod() { return m_mod; } /* * @return the column label of this Field's data type */ public final String getColumnLabel() { return m_name; } /* * @return the length of this Field's data type */ public final int getLength() { return m_len; } }pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIResultSetMetaData.java0000644000014500000120000000770711634451404027046 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; import org.postgresql.pljava.internal.Oid; import org.postgresql.pljava.internal.TupleDesc; /** * Implementation of ResultSetMetaData for SPIResultSet * * @author Filip Hrbek */ public class SPIResultSetMetaData extends AbstractResultSetMetaData { private final TupleDesc m_tupleDesc; /** * Constructor. * @param tupleDesc The descriptor for the ResultSet tuples */ public SPIResultSetMetaData(TupleDesc tupleDesc) { super(); m_tupleDesc = tupleDesc; } /** * Returns the number of columns in this ResultSet object. * * @return the number of columns * @exception SQLException if a database access error occurs */ public final int getColumnCount() throws SQLException { return m_tupleDesc.size(); } /** * Indicates whether the designated column is automatically numbered, thus read-only. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise * @exception SQLException if a database access error occurs */ public final boolean isAutoIncrement(int column) throws SQLException { checkColumnIndex(column); //Could SPIResultSet detect autoincrement columns??? return false; } /** * Gets the designated column's suggested title for use in printouts and * displays. * * @param column the first column is 1, the second is 2, ... * @return the suggested column title * @exception SQLException if a database access error occurs */ public final String getColumnLabel(int column) throws SQLException { checkColumnIndex(column); return m_tupleDesc.getColumnName(column).toUpperCase(); } //--------------------------JDBC 2.0----------------------------------- /** *

Returns the fully-qualified name of the Java class whose instances * are manufactured if the method ResultSet.getObject * is called to retrieve a value * from the column. ResultSet.getObject may return a subclass of the * class returned by this method. * * @param column the first column is 1, the second is 2, ... * @return the fully-qualified name of the class in the Java programming * language that would be used by the method * ResultSet.getObject to retrieve the value in the specified * column. This is the class name used for custom mapping. * @exception SQLException if a database access error occurs * @since 1.2 */ public final String getColumnClassName(int column) throws SQLException { checkColumnIndex(column); return this.getOid(column).getJavaClass().getName(); } /** * Checks if the column index is valid. * * @param column the first column is 1, the second is 2, ... * @exception SQLException if the column is out of index bounds */ protected final void checkColumnIndex(int column) throws SQLException { if (column < 1 || column > m_tupleDesc.size()) { throw new SQLException("Invalid column index: " + column); } } /** * Gets column OID * @param column Column index * @return column OID * @throws SQLException if an error occurs */ protected final Oid getOid(int column) throws SQLException { return m_tupleDesc.getOid(column); } /** * Gets column length. This method is called if the AbstractResultSet does not * know how to get column length according to type OID. * We retutn 0 because we don't know the proper length either. * @param column Column index * @return column length * @throws SQLException if an error occurs */ protected final int getFieldLength(int column) throws SQLException { return 0; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SingleRowWriter.java0000644000014500000120000000703611634451404026240 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.util.Arrays; import java.util.Calendar; import org.postgresql.pljava.internal.Tuple; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, updateable ResultSet, specially made for functions and * procedures that returns complex types or sets. * * @author Thomas Hallgren */ public class SingleRowWriter extends SingleRowResultSet { private final TupleDesc m_tupleDesc; private final Object[] m_values; private Tuple m_tuple; public SingleRowWriter(TupleDesc tupleDesc) throws SQLException { m_tupleDesc = tupleDesc; m_values = new Object[tupleDesc.size()]; } protected Object getObjectValue(int columnIndex) throws SQLException { if(columnIndex < 1) throw new SQLException("System columns cannot be obtained from this type of ResultSet"); return m_values[columnIndex - 1]; } /** * Returns true if the row contains any non null * values since all values of the row are null initially. */ public boolean rowUpdated() throws SQLException { int top = m_values.length; while(--top >= 0) if(m_values[top] != null) return true; return false; } public void updateObject(int columnIndex, Object x) throws SQLException { if(columnIndex < 1) throw new SQLException("System columns cannot be updated"); if(x == null) m_values[columnIndex-1] = x; Class c = m_tupleDesc.getColumnClass(columnIndex); if(!c.isInstance(x) && !(c == byte[].class && (x instanceof BlobValue))) { if(Number.class.isAssignableFrom(c)) x = SPIConnection.basicNumericCoersion(c, x); else if(Time.class.isAssignableFrom(c) || Date.class.isAssignableFrom(c) || Timestamp.class.isAssignableFrom(c)) x = SPIConnection.basicCalendricalCoersion(c, x, Calendar.getInstance()); else x = SPIConnection.basicCoersion(c, x); } m_values[columnIndex-1] = x; } public void cancelRowUpdates() throws SQLException { Arrays.fill(m_values, null); } /** * Cancels all changes but doesn't really close the set. */ public void close() throws SQLException { Arrays.fill(m_values, null); m_tuple = null; // Feel free to garbage collect... } public void copyRowFrom(ResultSet rs) throws SQLException { int top = m_values.length; for(int idx = 0; idx < top; ++idx) m_values[idx] = rs.getObject(idx+1); } /** * Creates a tuple from the current row values and then cancel all row * updates to prepare for a new row. This method is called automatically by * the trigger handler and should not be called in any other way. * * @return The native pointer of the Tuple reflecting the current row * values. * @throws SQLException */ public long getTupleAndClear() throws SQLException { // We hold on to the tuple as an instance variable so that it doesn't // get garbage collected until this result set is closed or we create // another tuple. This behavior is connected to the internal behavior // of Set Returning Functions (SRF) in the backend. // m_tuple = this.getTupleDesc().formTuple(m_values); Arrays.fill(m_values, null); return m_tuple.getNativePointer(); } protected final TupleDesc getTupleDesc() { return m_tupleDesc; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SingleRowReader.java~0000644000014500000120000000671111634451404026363 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.TupleDesc; /** * A single row, read-only ResultSet, specially made for functions and * procedures that takes complex types as arguments (PostgreSQL 7.5 * and later). * * @author Thomas Hallgren */ public class SingleRowReader extends SingleRowResultSet { private final TupleDesc m_tupleDesc; private final long m_pointer; public SingleRowReader(long pointer, TupleDesc tupleDesc) throws SQLException { m_pointer = pointer; m_tupleDesc = tupleDesc; } public void close() { } public void finalize() { synchronized(Backend.THREADLOCK) { _free(m_pointer); } } protected Object getObjectValue(int columnIndex) throws SQLException { synchronized(Backend.THREADLOCK) { return _getObject(m_pointer, m_tupleDesc.getNativePointer(), columnIndex); } } /** * Returns {@link ResultSet#CONCUR_READ_ONLY}. */ public int getConcurrency() throws SQLException { return ResultSet.CONCUR_READ_ONLY; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void cancelRowUpdates() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void deleteRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void insertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void moveToInsertRow() throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateRow() throws SQLException { throw readOnlyException(); } /** * Always returns false. */ public boolean rowUpdated() throws SQLException { return false; } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x) throws SQLException { throw readOnlyException(); } /** * This feature is not supported on a ReadOnlyResultSet. * @throws SQLException indicating that this feature is not supported. */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException { throw readOnlyException(); } private static SQLException readOnlyException() { return new UnsupportedFeatureException("ResultSet is read-only"); } protected final TupleDesc getTupleDesc() { return m_tupleDesc; } protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/StatementClosedException.java0000644000014500000120000000124011634451404030076 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class StatementClosedException extends SQLException { private static final long serialVersionUID = 9108917755099200271L; public static final String INVALID_SQL_STATEMENT_NAME = "26000"; public StatementClosedException() { super("Statement is closed", INVALID_SQL_STATEMENT_NAME); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/Invocation.java0000644000014500000120000000735511634451404025247 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.logging.Logger; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.PgSavepoint; /** * @author Thomas Hallgren */ public class Invocation { /** * The current "stack" of invocations. */ private static Invocation[] s_levels = new Invocation[10]; /** * Nesting level for this invocation */ private final int m_nestingLevel; /** * Top level savepoint relative to this invocation. */ private PgSavepoint m_savepoint; private Invocation(int level) { m_nestingLevel = level; } /** * @return The nesting level of this invocation */ public int getNestingLevel() { return m_nestingLevel; } /** * @return Returns the savePoint. */ final PgSavepoint getSavepoint() { return m_savepoint; } private ArrayList m_preparedStatements; final void manageStatement(PreparedStatement statement) { if(m_preparedStatements == null) m_preparedStatements = new ArrayList(); m_preparedStatements.add(statement); } final void forgetStatement(PreparedStatement statement) { if(m_preparedStatements == null) return; int idx = m_preparedStatements.size(); while(--idx >= 0) if(m_preparedStatements.get(idx) == statement) { m_preparedStatements.remove(idx); return; } } /** * @param savepoint The savepoint to set. */ final void setSavepoint(PgSavepoint savepoint) { m_savepoint = savepoint; } /** * Called from the backend when the invokation exits. Should * not be invoked any other way. */ public void onExit() throws SQLException { try { if(m_savepoint != null) m_savepoint.onInvocationExit(SPIDriver.getDefault()); if(m_preparedStatements != null) { int idx = m_preparedStatements.size(); if(idx > 0) { Logger w = Logger.getAnonymousLogger(); w.warning( "Closing " + idx + " \"forgotten\" statement" + ((idx > 1) ? "s" : "")); while(--idx >= 0) { PreparedStatement stmt = (PreparedStatement)m_preparedStatements.get(idx); w.fine("Closed: " + stmt); stmt.close(); } } } } finally { s_levels[m_nestingLevel] = null; } } /** * @return The current invocation */ public static Invocation current() { synchronized(Backend.THREADLOCK) { Invocation curr = _getCurrent(); if(curr != null) return curr; int level = _getNestingLevel(); int top = s_levels.length; if(level < top) { curr = s_levels[level]; if(curr != null) { curr._register(); return curr; } } else { int newSize = top; do { newSize <<= 2; } while(newSize <= level); Invocation[] levels = new Invocation[newSize]; System.arraycopy(s_levels, 0, levels, 0, top); s_levels = levels; } curr = new Invocation(level); s_levels[level] = curr; curr._register(); return curr; } } static void clearErrorCondition() { synchronized(Backend.THREADLOCK) { _clearErrorCondition(); } } /** * Register this Invocation so that it receives the onExit callback */ private native void _register(); /** * Returns the current invocation or null if no invocation has been * registered yet. */ private native static Invocation _getCurrent(); /** * Returns the current nesting level */ private native static int _getNestingLevel(); /** * Clears the error condition set by elog(ERROR) */ private native static void _clearErrorCondition(); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SPIPreparedStatement.java0000644000014500000120000002604211634451404027133 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Arrays; import java.util.Calendar; import org.postgresql.pljava.internal.ExecutionPlan; import org.postgresql.pljava.internal.Oid; /** * * @author Thomas Hallgren */ public class SPIPreparedStatement extends SPIStatement implements PreparedStatement { private final Oid[] m_typeIds; private final Object[] m_values; private final int[] m_sqlTypes; private final String m_statement; private ExecutionPlan m_plan; public SPIPreparedStatement(SPIConnection conn, String statement, int paramCount) { super(conn); m_statement = statement; m_typeIds = new Oid[paramCount]; m_values = new Object[paramCount]; m_sqlTypes = new int[paramCount]; Arrays.fill(m_sqlTypes, Types.NULL); } public void close() throws SQLException { if(m_plan != null) { m_plan.close(); m_plan = null; } this.clearParameters(); super.close(); Invocation.current().forgetStatement(this); } public ResultSet executeQuery() throws SQLException { this.execute(); return this.getResultSet(); } public int executeUpdate() throws SQLException { this.execute(); return this.getUpdateCount(); } public void setNull(int columnIndex, int sqlType) throws SQLException { this.setObject(columnIndex, null, sqlType); } public void setBoolean(int columnIndex, boolean value) throws SQLException { this.setObject(columnIndex, value ? Boolean.TRUE : Boolean.FALSE, Types.BOOLEAN); } public void setByte(int columnIndex, byte value) throws SQLException { this.setObject(columnIndex, new Byte(value), Types.TINYINT); } public void setShort(int columnIndex, short value) throws SQLException { this.setObject(columnIndex, new Short(value), Types.SMALLINT); } public void setInt(int columnIndex, int value) throws SQLException { this.setObject(columnIndex, new Integer(value), Types.INTEGER); } public void setLong(int columnIndex, long value) throws SQLException { this.setObject(columnIndex, new Long(value), Types.BIGINT); } public void setFloat(int columnIndex, float value) throws SQLException { this.setObject(columnIndex, new Float(value), Types.FLOAT); } public void setDouble(int columnIndex, double value) throws SQLException { this.setObject(columnIndex, new Double(value), Types.DOUBLE); } public void setBigDecimal(int columnIndex, BigDecimal value) throws SQLException { this.setObject(columnIndex, value, Types.DECIMAL); } public void setString(int columnIndex, String value) throws SQLException { this.setObject(columnIndex, value, Types.VARCHAR); } public void setBytes(int columnIndex, byte[] value) throws SQLException { this.setObject(columnIndex, value, Types.VARBINARY); } public void setDate(int columnIndex, Date value) throws SQLException { this.setObject(columnIndex, value, Types.DATE); } public void setTime(int columnIndex, Time value) throws SQLException { this.setObject(columnIndex, value, Types.TIME); } public void setTimestamp(int columnIndex, Timestamp value) throws SQLException { this.setObject(columnIndex, value, Types.TIMESTAMP); } public void setAsciiStream(int columnIndex, InputStream value, int length) throws SQLException { try { this.setObject(columnIndex, new ClobValue(new InputStreamReader(value, "US-ASCII"), length), Types.CLOB); } catch(UnsupportedEncodingException e) { throw new SQLException("US-ASCII encoding is not supported by this JVM"); } } /** * @deprecated */ public void setUnicodeStream(int columnIndex, InputStream value, int arg2) throws SQLException { throw new UnsupportedFeatureException("PreparedStatement.setUnicodeStream"); } public void setBinaryStream(int columnIndex, InputStream value, int length) throws SQLException { this.setObject(columnIndex, new BlobValue(value, length), Types.BLOB); } public void clearParameters() throws SQLException { Arrays.fill(m_values, null); Arrays.fill(m_sqlTypes, Types.NULL); } public void setObject(int columnIndex, Object value, int sqlType, int scale) throws SQLException { this.setObject(columnIndex, value, sqlType); } public void setObject(int columnIndex, Object value, int sqlType) throws SQLException { if(columnIndex < 1 || columnIndex > m_sqlTypes.length) throw new SQLException("Illegal parameter index"); Oid id = (sqlType == Types.OTHER) ? Oid.forJavaClass(value.getClass()) : Oid.forSqlType(sqlType); // Default to String. // if(id == null) id = Oid.forSqlType(Types.VARCHAR); Oid op = m_typeIds[--columnIndex]; if(op == null) m_typeIds[columnIndex] = id; else if(!op.equals(id)) { m_typeIds[columnIndex] = id; // We must re-prepare // if(m_plan != null) m_plan.close(); m_plan = null; } m_sqlTypes[columnIndex] = sqlType; m_values[columnIndex] = value; } public void setObject(int columnIndex, Object value) throws SQLException { if(value == null) throw new SQLException("Can't assign null unless the SQL type is known"); this.setObject(columnIndex, value, SPIConnection.getTypeForClass(value.getClass())); } /** * Obtains the XOPEN SQL types for the parameters. * @return The array of types. */ private int[] getSqlTypes() { int idx = m_sqlTypes.length; int[] types = (int[])m_sqlTypes.clone(); while(--idx >= 0) { if(types[idx] == Types.NULL) types[idx] = Types.VARCHAR; // Default. } return types; } public boolean execute() throws SQLException { int[] sqlTypes = m_sqlTypes; int idx = sqlTypes.length; while(--idx >= 0) if(sqlTypes[idx] == Types.NULL) throw new SQLException("Not all parameters have been set"); if(m_plan == null) m_plan = ExecutionPlan.prepare(m_statement, m_typeIds); boolean result = this.executePlan(m_plan, m_values); this.clearParameters(); // Parameters are cleared upon successful completion. return result; } /** * The prepared statement cannot be used for executing oter statements. * @throws SQLException indicating that this feature is not supported. */ public boolean execute(String statement) throws SQLException { throw new UnsupportedFeatureException("Can't execute other statements using a prepared statement"); } public void addBatch() throws SQLException { this.internalAddBatch(new Object[]{m_values.clone(), m_sqlTypes.clone(), m_typeIds.clone()}); this.clearParameters(); // Parameters are cleared upon successful completion. } /** * The prepared statement cannot have other statements added too it. * @throws SQLException indicating that this feature is not supported. */ public void addBatch(String statement) throws SQLException { throw new UnsupportedFeatureException("Can't add batch statements to a prepared statement"); } public void setCharacterStream(int columnIndex, Reader value, int length) throws SQLException { this.setObject(columnIndex, new ClobValue(value, length), Types.CLOB); } public void setRef(int columnIndex, Ref value) throws SQLException { this.setObject(columnIndex, value, Types.REF); } public void setBlob(int columnIndex, Blob value) throws SQLException { this.setObject(columnIndex, value, Types.BLOB); } public void setClob(int columnIndex, Clob value) throws SQLException { this.setObject(columnIndex, value, Types.CLOB); } public void setArray(int columnIndex, Array value) throws SQLException { this.setObject(columnIndex, value, Types.ARRAY); } /** * ResultSetMetaData is not yet supported. * @throws SQLException indicating that this feature is not supported. */ public ResultSetMetaData getMetaData() throws SQLException { throw new UnsupportedFeatureException("ResultSet meta data is not yet implemented"); } public void setDate(int columnIndex, Date value, Calendar cal) throws SQLException { if(cal == null || cal == Calendar.getInstance()) this.setObject(columnIndex, value, Types.DATE); throw new UnsupportedFeatureException("Setting date using explicit Calendar"); } public void setTime(int columnIndex, Time value, Calendar cal) throws SQLException { if(cal == null || cal == Calendar.getInstance()) this.setObject(columnIndex, value, Types.TIME); throw new UnsupportedFeatureException("Setting time using explicit Calendar"); } public void setTimestamp(int columnIndex, Timestamp value, Calendar cal) throws SQLException { if(cal == null || cal == Calendar.getInstance()) this.setObject(columnIndex, value, Types.TIMESTAMP); throw new UnsupportedFeatureException("Setting time using explicit Calendar"); } public void setNull(int columnIndex, int sqlType, String typeName) throws SQLException { this.setNull(columnIndex, sqlType); } public void setURL(int columnIndex, URL value) throws SQLException { this.setObject(columnIndex, value, Types.DATALINK); } public String toString() { return m_statement; } /** * Due to the design of the SPI_prepare, it is currently impossible to * obtain the correct parameter meta data before all the parameters have been * set, hence a ParameterMetaData obtained prior to setting the paramteres * will have all parameters set to the default type {@link Types#VARCHAR}. * Once the parameters have been set, a fair attempt is made to generate this * object based on the supplied values. * @return The meta data for parameter values. */ public ParameterMetaData getParameterMetaData() throws SQLException { return new SPIParameterMetaData(this.getSqlTypes()); } protected int executeBatchEntry(Object batchEntry) throws SQLException { int ret = SUCCESS_NO_INFO; Object batchParams[] = (Object[])batchEntry; Object batchValues = batchParams[0]; Object batchSqlTypes = batchParams[1]; Object batchTypeIds[] = (Object[])batchParams[2]; System.arraycopy(batchValues, 0, m_values, 0, m_values.length); System.arraycopy(batchSqlTypes, 0, m_sqlTypes, 0, m_sqlTypes.length); // Determine if we need to replan the query because the // types have changed from the last execution. // for (int i=0; i= 0) ret = updCount; } return ret; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/jdbc/SQLUtils.java0000644000014500000120000000200411634451404024600 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * Helpful utility commands when dealing with JDBC * @author Thomas Hallgren */ public abstract class SQLUtils { public static Connection getDefaultConnection() { return SPIDriver.getDefault(); } public static void close(Connection conn) { if(conn != null) try { conn.close(); } catch(SQLException e) { /* ignore */ } } public static void close(Statement stmt) { if(stmt != null) try { stmt.close(); } catch(SQLException e) { /* ignore */ } } public static void close(ResultSet rs) { if(rs != null) try { rs.close(); } catch(SQLException e) { /* ignore */ } } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/SavepointListener.java0000644000014500000120000000122411634451403025676 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.SQLException; import java.sql.Savepoint; /** * @author Thomas Hallgren */ public interface SavepointListener { void onAbort(Session session, Savepoint savepoint, Savepoint parent) throws SQLException; void onCommit(Session session, Savepoint savepoint, Savepoint parent) throws SQLException; void onStart(Session session, Savepoint savepoint, Savepoint parent) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/TriggerException.java0000644000014500000120000000407411634451403025510 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.SQLException; /** * An exception specially suited to be thrown from within a method * designated to be a trigger function. The message generated by * this exception will contain information on what trigger and * what relation it was that caused the exception * * @author Thomas Hallgren */ public class TriggerException extends SQLException { private static final long serialVersionUID = 5543711707414329116L; private static boolean s_recursionLock = false; public static final String TRIGGER_ACTION_EXCEPTION = "09000"; private static final String makeMessage(TriggerData td, String message) { StringBuffer bld = new StringBuffer(); bld.append("In Trigger "); if(!s_recursionLock) { s_recursionLock = true; try { bld.append(td.getName()); bld.append(" on relation "); bld.append(td.getTableName()); } catch(SQLException e) { bld.append("(exception while generating exception message)"); } finally { s_recursionLock = false; } } if(message != null) { bld.append(": "); bld.append(message); } return bld.toString(); } /** * Create an exception based on the TriggerData that was * passed to the trigger method. * @param td The TriggerData that was passed to the trigger * method. */ public TriggerException(TriggerData td) { super(makeMessage(td, null), TRIGGER_ACTION_EXCEPTION); } /** * Create an exception based on the TriggerData that was * passed to the trigger method and an additional message. * @param td The TriggerData that was passed to the trigger * method. * @param reason An additional message with info about the exception. */ public TriggerException(TriggerData td, String reason) { super(makeMessage(td, reason), TRIGGER_ACTION_EXCEPTION); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/PooledObject.java0000644000014500000120000000226611634451403024600 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.SQLException; /** * Interface for objects that are pooled and reused. * @author Thomas Hallgren */ public interface PooledObject { /** * The activate method is called when the instance is activated * from its "passive" state. * @throws SQLException if something goes wrong with the activation. * When this happens, a call will be issued to {@link #remove()} and * the object will no longer be part of the pool. */ void activate() throws SQLException; /** * The passivate method is called before the instance enters * the "passive" state. * @throws SQLException if something goes wrong with the passivation. * When this happens, a call will be issued to {@link #remove()} and * the object will no longer be part of the pool. */ void passivate() throws SQLException; /** * PLJava invokes this method before it ends the life of the * pooled object. */ void remove(); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/TransactionListener.java0000644000014500000120000000101011634451403026204 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.SQLException; /** * @author Thomas Hallgren */ public interface TransactionListener { void onAbort(Session session) throws SQLException; void onCommit(Session session) throws SQLException; void onPrepare(Session session) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/0000755000014500000120000000000011634451403023172 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ELogFormatter.java0000644000014500000120000000330211634451403026545 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.io.PrintWriter; import java.io.StringWriter; import java.text.MessageFormat; import java.util.Date; import java.util.logging.Formatter; import java.util.logging.LogRecord; /** * A default formatter for the ELogHandler. * * @author Thomas Hallgren */ public class ELogFormatter extends Formatter { private final static MessageFormat s_tsFormatter = new MessageFormat( "{0,date,dd MMM yy} {0,time,HH:mm:ss} {1} {2}"); private final static String s_lineSeparator = System.getProperty("line.separator"); private final Date m_timestamp = new Date(); private final Object m_args[] = new Object[] { m_timestamp, null, null }; private final StringBuffer m_buffer = new StringBuffer(); /** * Format the given LogRecord. * @param record the log record to be formatted. * @return a formatted log record */ public synchronized String format(LogRecord record) { StringBuffer sb = m_buffer; sb.setLength(0); m_timestamp.setTime(record.getMillis()); String tmp = record.getSourceClassName(); m_args[1] = (tmp == null) ? record.getLoggerName() : tmp; m_args[2] = this.formatMessage(record); s_tsFormatter.format(m_args, sb, null); Throwable thrown = record.getThrown(); if(thrown != null) { sb.append(s_lineSeparator); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); record.getThrown().printStackTrace(pw); pw.close(); sb.append(sw.toString()); } return sb.toString(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ExecutionPlan.java0000644000014500000120000001327611634451403026624 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; import java.util.Collections; import java.util.Map; import java.util.LinkedHashMap; /** * The ExecutionPlan correspons to the execution plan obtained * using an internal PostgreSQL SPI_prepare call. * * @author Thomas Hallgren */ public class ExecutionPlan { static final int INITIAL_CACHE_CAPACITY = 29; static final float CACHE_LOAD_FACTOR = 0.75f; private long m_pointer; /** * MRU cache for prepared plans. */ static final class PlanCache extends LinkedHashMap { private final int m_cacheSize; public PlanCache(int cacheSize) { super(INITIAL_CACHE_CAPACITY, CACHE_LOAD_FACTOR, true); m_cacheSize = cacheSize; } protected boolean removeEldestEntry(Map.Entry eldest) { if(this.size() <= m_cacheSize) return false; ExecutionPlan evicted = (ExecutionPlan)eldest.getValue(); synchronized(Backend.THREADLOCK) { if(evicted.m_pointer != 0) { _invalidate(evicted.m_pointer); evicted.m_pointer = 0; } } return true; } }; static final class PlanKey { private final int m_hashCode; private final String m_stmt; private final Oid[] m_argTypes; PlanKey(String stmt, Oid[] argTypes) { m_stmt = stmt; m_hashCode = stmt.hashCode() + 1; m_argTypes = argTypes; } public boolean equals(Object o) { if(!(o instanceof PlanKey)) return false; PlanKey pk = (PlanKey)o; if(!pk.m_stmt.equals(m_stmt)) return false; Oid[] pat = pk.m_argTypes; Oid[] mat = m_argTypes; int idx = pat.length; if(mat.length != idx) return false; while(--idx >= 0) if(!pat[idx].equals(mat[idx])) return false; return true; } public int hashCode() { return m_hashCode; } } private static final Map s_planCache; private final Object m_key; static { int cacheSize = Backend.getStatementCacheSize(); s_planCache = Collections.synchronizedMap(new PlanCache(cacheSize < 11 ? 11 : cacheSize)); } private ExecutionPlan(Object key, long pointer) { m_key = key; m_pointer = pointer; } /** * Close the plan. */ public void close() { ExecutionPlan old = (ExecutionPlan)s_planCache.put(m_key, this); if(old != null && old.m_pointer != 0) { synchronized(Backend.THREADLOCK) { _invalidate(old.m_pointer); old.m_pointer = 0; } } } /** * Set up a cursor that will execute the plan using the internal * SPI_cursor_open function * * @param cursorName Name of the cursor or null for a system * generated name. * @param parameters Values for the parameters. * @return The Portal that represents the opened cursor. * @throws SQLException If the underlying native structure has gone stale. */ public Portal cursorOpen(String cursorName, Object[] parameters) throws SQLException { synchronized(Backend.THREADLOCK) { return _cursorOpen(m_pointer, System.identityHashCode(Thread .currentThread()), cursorName, parameters); } } /** * Checks if this ExecutionPlan can create a Portal * * using {@link #cursorOpen}. This is true if the plan contains only one * regular SELECT query. * * @return true if the plan can create a Portal * @throws SQLException If the underlying native structure has gone stale. */ public boolean isCursorPlan() throws SQLException { synchronized(Backend.THREADLOCK) { return _isCursorPlan(m_pointer); } } /** * Execute the plan using the internal SPI_execp function. * * @param parameters Values for the parameters. * @param rowCount The maximum number of tuples to create. A value of * rowCount of zero is interpreted as no limit, * i.e., run to completion. * @return One of the status codes declared in class {@link SPI}. * @throws SQLException If the underlying native structure has gone stale. */ public int execute(Object[] parameters, int rowCount) throws SQLException { synchronized(Backend.THREADLOCK) { return _execute(m_pointer, System.identityHashCode(Thread .currentThread()), parameters, rowCount); } } /** * Create an execution plan for a statement to be executed later using the * internal SPI_prepare function. * * @param statement The command string. * @param argTypes SQL types of argument types. * @return An execution plan for the prepared statement. * @throws SQLException * @see java.sql.Types */ public static ExecutionPlan prepare(String statement, Oid[] argTypes) throws SQLException { Object key = (argTypes == null) ? (Object)statement : (Object)new PlanKey(statement, argTypes); ExecutionPlan plan = (ExecutionPlan)s_planCache.remove(key); if(plan == null) { synchronized(Backend.THREADLOCK) { plan = new ExecutionPlan(key, _prepare( System.identityHashCode(Thread.currentThread()), statement, argTypes)); } } return plan; } private static native Portal _cursorOpen(long pointer, long threadId, String cursorName, Object[] parameters) throws SQLException; private static native boolean _isCursorPlan(long pointer) throws SQLException; private static native int _execute(long pointer, long threadId, Object[] parameters, int rowCount) throws SQLException; private static native long _prepare(long threadId, String statement, Oid[] argTypes) throws SQLException; private static native void _invalidate(long pointer); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ResultSetPicker.java0000644000014500000120000000215211634451403027125 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.ResultSetHandle; import org.postgresql.pljava.ResultSetProvider; import org.postgresql.pljava.jdbc.SingleRowWriter; public class ResultSetPicker implements ResultSetProvider { private final ResultSetHandle m_resultSetHandle; private final ResultSet m_resultSet; public ResultSetPicker(ResultSetHandle resultSetHandle) throws SQLException { m_resultSetHandle = resultSetHandle; m_resultSet = resultSetHandle.getResultSet(); } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(m_resultSet == null || !m_resultSet.next()) return false; ((SingleRowWriter)receiver).copyRowFrom(m_resultSet); return true; } public void close() throws SQLException { m_resultSet.close(); m_resultSetHandle.close(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/TransactionalMap.java0000644000014500000120000001605711634451403027306 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Collection; import java.util.Iterator; import java.util.AbstractSet; import java.util.AbstractCollection; import java.util.NoSuchElementException; /** * A TransactionalMap acts as a modifiable front for a backing map. All * modifications can be reverted by a call to abort or propagated to * the backing map by a call to commit. * * The map is not synchronized so care should be taken if multiple threads * will access the map. * * @author Thomas Hallgren */ public class TransactionalMap extends HashMap { private static final long serialVersionUID = 5337569423915578121L; // The object representing no object (i.e. a shadowed entry) // private static final Object s_noObject = new Object(); // Cache of backed collections. // private Set m_entrySet; private Set m_keySet; private Collection m_valueColl; // Commited data // private final Map m_base; protected TransactionalMap(Map base) { m_base = base; } /** * Undo all changes made since the map was created or since * last commit or abort. */ public void abort() { super.clear(); } /** * Clear this map (an anti-object is inserted for each entry * present in the backed map). */ public void clear() { super.clear(); // Add an anti-entry for each key represented in the // parent scope. // Iterator itor = m_base.keySet().iterator(); while(itor.hasNext()) super.put(itor.next(), s_noObject); } /** * Commit all changes made since the map was created or since * last commit or abort. All changes are propagated to the backing * map. */ public void commit() { Iterator itor = super.entrySet().iterator(); while(itor.hasNext()) { Map.Entry e = (Map.Entry)itor.next(); Object key = e.getKey(); Object val = e.getValue(); if(val == s_noObject) m_base.remove(key); else m_base.put(key, val); } super.clear(); } public boolean containsKey(Object key) { Object v = super.get(key); if(v != null) return (v != s_noObject); return super.containsKey(key) || m_base.containsKey(key); } public Object get(Object key) { Object val = super.get(key); if(val == s_noObject) val = null; else if(val == null && !super.containsKey(key)) val = m_base.get(key); return val; } public Object remove(Object key) { Object val = super.get(key); if(val == s_noObject) // // Already removed // return null; Object bval = m_base.get(key); if(bval == null && !m_base.containsKey(key)) { // Not present in base // if(val != null || super.containsKey(key)) super.remove(key); return val; } if(val == null && !super.containsKey(key)) val = bval; super.put(key, s_noObject); return val; } public int size() { int sz = m_base.size(); int psz = super.size(); if(sz == 0) return psz; if(psz == 0) return sz; Iterator itor = super.entrySet().iterator(); // Decrease counter for entries present in both maps. // while(itor.hasNext()) { Map.Entry me = (Map.Entry)itor.next(); Object val = me.getValue(); if(val == s_noObject) --sz; else if(!m_base.containsKey(me.getKey())) ++sz; } return sz; } public boolean containsValue(Object val) { Iterator itor = this.getValueIterator(); while(itor.hasNext()) { Object v = itor.next(); if(v == val || (v != null && v.equals(val))) return true; } return false; } public Set entrySet() { if(m_entrySet == null) { m_entrySet = new AbstractSet() { public Iterator iterator() { return TransactionalMap.this.getEntryIterator(); } public int size() { return TransactionalMap.this.size(); } public boolean contains(Object k) { return TransactionalMap.this.containsKey(k); } }; } return m_entrySet; } public boolean isEmpty() { return (this.size() == 0); } public Set keySet() { if(m_keySet == null) { m_keySet = new AbstractSet() { public Iterator iterator() { return TransactionalMap.this.getKeyIterator(); } public int size() { return TransactionalMap.this.size(); } public boolean contains(Object k) { return TransactionalMap.this.containsKey(k); } }; } return m_keySet; } public Object put(Object key, Object value) { Object old = this.get(key); super.put(key, value); return old; } public void putAll(Map t) { super.putAll(t); } public Collection values() { if(m_valueColl == null) { m_valueColl = new AbstractCollection() { public Iterator iterator() { return TransactionalMap.this.getValueIterator(); } public int size() { return TransactionalMap.this.size(); } public boolean contains(Object v) { return TransactionalMap.this.containsValue(v); } }; } return m_valueColl; } private Set superKeySet() { return super.keySet(); } protected Iterator getEntryIterator() { return new EntryIterator(); } protected Iterator getKeyIterator() { return new KeyIterator(); } protected Iterator getValueIterator() { return new ValueIterator(); } protected class BackedEntry implements Map.Entry { private Object m_key; public BackedEntry(Object key) { m_key = key; } public Object getKey() { return m_key; } public Object getValue() { return TransactionalMap.this.get(m_key); } public Object setValue(Object value) { return TransactionalMap.this.put(m_key, value); } } protected class KeyIterator implements Iterator { private boolean m_phaseA = true; private Iterator m_currentItor = TransactionalMap.this.superKeySet().iterator(); private Object m_currentKey = null; public boolean hasNext() { m_currentKey = this.getValidKey(m_currentKey); return (m_currentKey != null); } public Object next() { Object key = this.getValidKey(m_currentKey); if(key == null) throw new NoSuchElementException(); m_currentKey = null; // Force retrieval of next key return key; } public void remove() { throw new UnsupportedOperationException(); } protected Object getValidKey(Object key) { if(key != null && TransactionalMap.this.containsKey(key)) return key; // Entry is not valid. Get next entry. // for(;;) { while(m_currentItor.hasNext()) { key = m_currentItor.next(); if(TransactionalMap.this.containsKey(key)) return key; } if(!m_phaseA) break; m_currentItor = m_base.keySet().iterator(); m_phaseA = false; } return null; } } protected class EntryIterator extends KeyIterator { public Object next() { return new BackedEntry(super.next()); } } protected class ValueIterator extends KeyIterator { public Object next() { return TransactionalMap.this.get(super.next()); } } }pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/LargeObject.java0000644000014500000120000000767011634451403026230 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The LargeObject correspons to the internal PostgreSQL * LargeObjectDesc. * * @author Thomas Hallgren */ public class LargeObject extends JavaWrapper { /** * Write mode flag to be passed to {@link #create} and {@link #open} */ public static final int INV_WRITE = 0x00020000; /** * Read mode flag to be passed to {@link #create} and {@link #open} */ public static final int INV_READ = 0x00040000; /** * Flag returned by {@link #create} and {@link #open} */ public static final int IFS_RDLOCK = (1 << 0); /** * Flag returned by {@link #create} and {@link #open} */ public static final int IFS_WRLOCK = (1 << 1); /** * Flag to be passed to {@link #seek} denoting that the * offset parameter should be treated as an absolute address. */ public static final int SEEK_SET = 0; /** * Flag to be passed to {@link #seek} denoting that the * offset parameter should be treated relative to the current * address. */ public static final int SEEK_CUR = 1; /** * Flag to be passed to {@link #seek} denoting that the * offset parameter should be treated relative to the end * of the data. */ public static final int SEEK_END = 2; LargeObject(long nativePointer) { super(nativePointer); } /** * Creates a LargeObject handle and returns the {@link Oid} of * that handle. * @param flags Flags to use for creation. * @return A Oid that can be used in a call to {@link #open(Oid, int)} * or {@link #drop(Oid)}. * @throws SQLException */ public static Oid create(int flags) throws SQLException { synchronized(Backend.THREADLOCK) { return _create(flags); } } public static LargeObject open(Oid lobjId, int flags) throws SQLException { synchronized(Backend.THREADLOCK) { return _open(lobjId, flags); } } public void close() throws SQLException { synchronized(Backend.THREADLOCK) { _close(this.getNativePointer()); } } public static int drop(Oid lobjId) throws SQLException { synchronized(Backend.THREADLOCK) { return _drop(lobjId); } } public Oid getId() throws SQLException { synchronized(Backend.THREADLOCK) { return _getId(this.getNativePointer()); } } public long length() throws SQLException { synchronized(Backend.THREADLOCK) { return _length(this.getNativePointer()); } } public long seek(long offset, int whence) throws SQLException { synchronized(Backend.THREADLOCK) { return _seek(this.getNativePointer(), offset, whence); } } public long tell() throws SQLException { synchronized(Backend.THREADLOCK) { return _tell(this.getNativePointer()); } } public int read(byte[] buf) throws SQLException { synchronized(Backend.THREADLOCK) { return _read(this.getNativePointer(), buf); } } public int write(byte[] buf) throws SQLException { synchronized(Backend.THREADLOCK) { return _write(this.getNativePointer(), buf); } } private static native Oid _create(int flags) throws SQLException; private static native int _drop(Oid lobjId) throws SQLException; private static native LargeObject _open(Oid lobjId, int flags) throws SQLException; private static native void _close(long pointer) throws SQLException; private static native Oid _getId(long pointer) throws SQLException; private static native long _length(long pointer) throws SQLException; private static native long _seek(long pointer, long offset, int whence) throws SQLException; private static native long _tell(long pointer) throws SQLException; private static native int _read(long pointer, byte[] buf) throws SQLException; private static native int _write(long pointer, byte[] buf) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Backend.java0000644000014500000120000001425711634451403025375 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.io.File; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.Permission; import java.sql.SQLException; import java.util.PropertyPermission; import java.util.logging.Level; import java.util.logging.Logger; import org.postgresql.pljava.management.Commands; /** * Provides access to some useful routines in the PostgreSQL server. * @author Thomas Hallgren */ public class Backend { /** * All native calls synchronize on this object. */ public static final Object THREADLOCK = new Object(); private static Session s_session; public static synchronized Session getSession() { if(s_session == null) s_session = new Session(); return s_session; } /** * Returns the configuration option as read from the Global * Unified Config package (GUC). * @param key The name of the option. * @return The value of the option. */ public static String getConfigOption(String key) { synchronized(THREADLOCK) { return _getConfigOption(key); } } /** * Returns the size of the statement cache. * @return the size of the statement cache. */ public static int getStatementCacheSize() { synchronized(THREADLOCK) { return _getStatementCacheSize(); } } /** * Log a message using the internal elog command. * @param logLevel The log level as defined in * {@link ELogHandler}. * @param str The message */ static void log(int logLevel, String str) { synchronized(THREADLOCK) { _log(logLevel, str); } } private static class PLJavaSecurityManager extends SecurityManager { private boolean m_recursion = false; public void checkPermission(Permission perm) { this.nonRecursiveCheck(perm); } public void checkPermission(Permission perm, Object context) { this.nonRecursiveCheck(perm); } private synchronized void nonRecursiveCheck(Permission perm) { if(m_recursion) // // Something, probably a ClassLoader loading one of // the referenced classes, caused a recursion. Well // everything done within this method is permitted // so we just return here. // return; m_recursion = true; try { this.assertPermission(perm); } finally { m_recursion = false; } } void assertPermission(Permission perm) { if(perm instanceof RuntimePermission) { String name = perm.getName(); if("*".equals(name) || "exitVM".equals(name)) throw new SecurityException(); else if("setSecurityManager".equals(name) && !s_inSetTrusted) // // Attempt to set another security manager while not // in the setTrusted method // throw new SecurityException(); } else if(perm instanceof PropertyPermission) { if(perm.getActions().indexOf("write") >= 0) { // We never allow this to be changed. // String propName = perm.getName(); if(propName.equals("java.home")) throw new SecurityException(); } } } } private static boolean s_inSetTrusted = false; private static final SecurityManager s_untrustedSecurityManager = new PLJavaSecurityManager(); /** * This security manager will block all attempts to access the file system */ private static final SecurityManager s_trustedSecurityManager = new PLJavaSecurityManager() { void assertPermission(Permission perm) { if(perm instanceof FilePermission) { String actions = perm.getActions(); if("read".equals(actions)) { // Must be able to read timezone info etc. in the java // installation directory. // File javaHome = new File(System.getProperty("java.home")); File accessedFile = new File(perm.getName()); File fileDir = accessedFile.getParentFile(); while(fileDir != null) { if(fileDir.equals(javaHome)) return; fileDir = fileDir.getParentFile(); } } throw new SecurityException(perm.getActions() + " on " + perm.getName()); } super.assertPermission(perm); } }; public static void addClassImages(int jarId, String urlString) throws SQLException { InputStream urlStream = null; boolean wasTrusted = (System.getSecurityManager() == s_trustedSecurityManager); if(wasTrusted) setTrusted(false); try { URL url = new URL(urlString); urlStream = url.openStream(); Commands.addClassImages(jarId, urlStream); } catch(IOException e) { throw new SQLException("I/O exception reading jar file: " + e.getMessage()); } finally { if(urlStream != null) try { urlStream.close(); } catch(IOException e) {} if(wasTrusted) setTrusted(true); } } public static void clearFunctionCache() { synchronized(THREADLOCK) { _clearFunctionCache(); } } /** * Called when the JVM is first booted and then everytime a switch * is made between calling a trusted function versus an untrusted * function. */ private static void setTrusted(boolean trusted) { s_inSetTrusted = true; try { Logger log = Logger.getAnonymousLogger(); if(log.isLoggable(Level.FINE)) log.fine("Using SecurityManager for " + (trusted ? "trusted" : "untrusted") + " language"); System.setSecurityManager(trusted ? s_trustedSecurityManager : s_untrustedSecurityManager); } finally { s_inSetTrusted = false; } } /** * Returns true if the backend is awaiting a return from a * call into the JVM. This method will only return false * when called from a thread other then the main thread and the main * thread has returned from the call into the JVM. */ public native static boolean isCallingJava(); /** * Returns the value of the GUC custom variable * pljava.release_lingering_savepoints. */ public native static boolean isReleaseLingeringSavepoints(); private native static String _getConfigOption(String key); private native static int _getStatementCacheSize(); private native static void _log(int logLevel, String str); private native static void _clearFunctionCache(); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/TupleDesc.java0000644000014500000120000000633611634451403025735 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The TupleDesc correspons to the internal PostgreSQL * TupleDesc. * * @author Thomas Hallgren */ public class TupleDesc extends JavaWrapper { private final int m_size; private Class[] m_columnClasses; TupleDesc(long pointer, int size) throws SQLException { super(pointer); m_size = size; } /** * Returns the name of the column at index. * @param index The one based index of the column. * @return The name of the column. * @throws SQLException If the index is out of range for this * tuple descriptor. */ public String getColumnName(int index) throws SQLException { synchronized(Backend.THREADLOCK) { return _getColumnName(this.getNativePointer(), index); } } /** * Returns the index of the column named colName. * @param colName The name of the column. * @return The index for column colName. * @throws SQLException If no column with the given name can * be found in this tuple descriptor. */ public int getColumnIndex(String colName) throws SQLException { synchronized(Backend.THREADLOCK) { return _getColumnIndex(this.getNativePointer(), colName.toLowerCase()); } } /** * Creates a Tuple that is described by this descriptor and * initialized with the supplied values. * @return The created Tuple. * @throws SQLException If the length of the values array does not * match the size of the descriptor or if the handle of this descriptor * has gone stale. */ public Tuple formTuple(Object[] values) throws SQLException { synchronized(Backend.THREADLOCK) { return _formTuple(this.getNativePointer(), values); } } /** * Returns the number of columns in this tuple descriptor. */ public int size() { return m_size; } /** * Returns the Java class of the column at index */ public Class getColumnClass(int index) throws SQLException { if(m_columnClasses == null) { m_columnClasses = new Class[m_size]; synchronized(Backend.THREADLOCK) { long _this = this.getNativePointer(); for(int idx = 0; idx < m_size; ++idx) m_columnClasses[idx] = _getOid(_this, idx+1).getJavaClass(); } } return m_columnClasses[index-1]; } /** * Returns OID of the column type. */ public Oid getOid(int index) throws SQLException { synchronized(Backend.THREADLOCK) { return _getOid(this.getNativePointer(), index); } } /** * Calls the backend function FreeTupleDesc(TupleDesc desc) * @param pointer The native pointer to the source TupleDesc */ protected native void _free(long pointer); private static native String _getColumnName(long _this, int index) throws SQLException; private static native int _getColumnIndex(long _this, String colName) throws SQLException; private static native Tuple _formTuple(long _this, Object[] values) throws SQLException; private static native Oid _getOid(long _this, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/TupleTable.java0000644000014500000120000000202411634451403026074 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; /** * The SPITupleTable correspons to the internal PostgreSQL * SPITupleTable type. * * @author Thomas Hallgren */ public class TupleTable { private final TupleDesc m_tupleDesc; private final Tuple[] m_tuples; TupleTable(TupleDesc tupleDesc, Tuple[] tuples) { m_tupleDesc = tupleDesc; m_tuples = tuples; } public final TupleDesc getTupleDesc() { return m_tupleDesc; } /** * Returns the number of Tuple instances contained in this table. */ public final int getCount() { return m_tuples.length; } /** * Returns the Tuple at the given index. * @param position Index of desired slot. First slot has index zero. */ public final Tuple getSlot(int position) { return m_tuples[position]; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ServerException.java0000644000014500000120000000127711634451403027171 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class ServerException extends SQLException { private static final long serialVersionUID = 8812755938793744633L; private transient final ErrorData m_errorData; public ServerException(ErrorData errorData) { super(errorData.getMessage(), errorData.getSqlState()); m_errorData = errorData; } public final ErrorData getErrorData() { return m_errorData; } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/JavaWrapper.java0000644000014500000120000000211711634451403026260 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; public abstract class JavaWrapper { private final long m_pointer; /** * Creates an instance of this class that will be attached to a native * structure represented by pointer. This constructor must only be called * from native code. * * @param pointer The wapped pointer. */ protected JavaWrapper(long pointer) { m_pointer = pointer; } public void finalize() { synchronized(Backend.THREADLOCK) { _free(m_pointer); } } /** * Returns the native pointer */ public final long getNativePointer() { return m_pointer; } /** * Calls the C function pfree() with the given pointer as an argument. * Subclasses may override this method if special handling is needed when * freeing up the object. * * @param pointer The pointer to free. */ protected native void _free(long pointer); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ObjectPoolImpl.java0000644000014500000120000000764411634451403026732 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; import java.util.IdentityHashMap; import org.postgresql.pljava.ObjectPool; import org.postgresql.pljava.PooledObject; class ObjectPoolImpl implements ObjectPool { /** * An InstanceHandle is a link in a single linked list that * holds on to a ResultSetProvider. */ private static class PooledObjectHandle { private PooledObject m_instance; private PooledObjectHandle m_next; } private static Class[] s_ctorSignature = { ObjectPool.class }; private static PooledObjectHandle s_handlePool; private static final IdentityHashMap s_poolCache = new IdentityHashMap(); private final Constructor m_ctor; private PooledObjectHandle m_providerPool; private ObjectPoolImpl(Class c) { if(!PooledObject.class.isAssignableFrom(c)) throw new IllegalArgumentException("Class " + c + " does not implement the " + PooledObject.class + " interface"); try { m_ctor = c.getConstructor(s_ctorSignature); } catch(SecurityException e) { throw new RuntimeException(e); } catch(NoSuchMethodException e) { throw new IllegalArgumentException("Unable to locate constructor " + c + "(ObjectPool)"); } } /** * Obtain a pool for the given class. * @param cls * @return * @throws SQLException */ public static ObjectPoolImpl getObjectPool(Class cls) { ObjectPoolImpl pool = (ObjectPoolImpl)s_poolCache.get(cls); if(pool == null) { pool = new ObjectPoolImpl(cls); s_poolCache.put(cls, pool); } return pool; } public PooledObject activateInstance() throws SQLException { PooledObject instance; PooledObjectHandle handle = m_providerPool; if(handle != null) { m_providerPool = handle.m_next; instance = handle.m_instance; // Return the handle to the unused handle pool. // handle.m_instance = null; handle.m_next = s_handlePool; s_handlePool = handle; } else { try { instance = (PooledObject)m_ctor.newInstance(new Object[] { this }); } catch(InvocationTargetException e) { Throwable t = e.getTargetException(); if(t instanceof SQLException) throw (SQLException)t; if(t instanceof RuntimeException) throw (RuntimeException)t; if(t instanceof Error) throw (Error)t; throw new SQLException(e.getMessage()); } catch(RuntimeException e) { throw e; } catch(Exception e) { throw new SQLException("Failed to create an instance of: " + m_ctor.getDeclaringClass() + " :" + e.getMessage()); } } try { instance.activate(); } catch(SQLException e) { instance.remove(); throw e; } return instance; } public void passivateInstance(PooledObject instance) throws SQLException { try { instance.passivate(); } catch(SQLException e) { instance.remove(); throw e; } // Obtain a handle from the pool of handles so that // we have something to wrap the instance in. // PooledObjectHandle handle = s_handlePool; if(handle != null) s_handlePool = handle.m_next; else handle = new PooledObjectHandle(); handle.m_instance = instance; handle.m_next = m_providerPool; m_providerPool = handle; } public void removeInstance(PooledObject instance) throws SQLException { PooledObjectHandle prev = null; for(PooledObjectHandle handle = m_providerPool; handle != null; handle = handle.m_next) { if(handle.m_instance == instance) { if(prev == null) m_providerPool = handle.m_next; else prev.m_next = handle.m_next; handle.m_instance = null; handle.m_next = s_handlePool; s_handlePool = handle; break; } } instance.remove(); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Portal.java0000644000014500000120000001060011634451403025273 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The Portal correspons to the internal PostgreSQL * Portal type. * * @author Thomas Hallgren */ public class Portal { private long m_pointer; Portal(long pointer) { m_pointer = pointer; } /** * Invalidates this structure and frees up memory using the * internal function SPI_cursor_close */ public void close() { synchronized(Backend.THREADLOCK) { _close(m_pointer); m_pointer = 0; } } /** * Returns the name of this Portal. * @throws SQLException if the handle to the native structur is stale. */ public String getName() throws SQLException { synchronized(Backend.THREADLOCK) { return _getName(m_pointer); } } /** * Returns the value of the portalPos attribute. * @throws SQLException if the handle to the native structur is stale. */ public int getPortalPos() throws SQLException { synchronized(Backend.THREADLOCK) { return _getPortalPos(m_pointer); } } /** * Returns the TupleDesc that describes the row Tuples for this * Portal. * @throws SQLException if the handle to the native structur is stale. */ public TupleDesc getTupleDesc() throws SQLException { synchronized(Backend.THREADLOCK) { return _getTupleDesc(m_pointer); } } /** * Performs an SPI_cursor_fetch. * @param forward Set to true for forward, false for backward. * @param count Maximum number of rows to fetch. * @return The actual number of fetched rows. * @throws SQLException if the handle to the native structur is stale. */ public int fetch(boolean forward, int count) throws SQLException { synchronized(Backend.THREADLOCK) { return _fetch(m_pointer, System.identityHashCode(Thread.currentThread()), forward, count); } } /** * Returns the value of the atEnd attribute. * @throws SQLException if the handle to the native structur is stale. */ public boolean isAtEnd() throws SQLException { synchronized(Backend.THREADLOCK) { return _isAtEnd(m_pointer); } } /** * Returns the value of the atStart attribute. * @throws SQLException if the handle to the native structur is stale. */ public boolean isAtStart() throws SQLException { synchronized(Backend.THREADLOCK) { return _isAtStart(m_pointer); } } /** * Returns the value of the posOverflow attribute. * @throws SQLException if the handle to the native structur is stale. */ public boolean isPosOverflow() throws SQLException { synchronized(Backend.THREADLOCK) { return _isPosOverflow(m_pointer); } } /** * Checks if the portal is still active. I can be closed either explicitly * using the {@link #close()} mehtod or implicitly due to a pop of invocation * context. */ public boolean isValid() { return m_pointer != 0; } /** * Performs an SPI_cursor_move. * @param forward Set to true for forward, false for backward. * @param count Maximum number of rows to fetch. * @return The value of the global variable SPI_result. * @throws SQLException if the handle to the native structur is stale. */ public int move(boolean forward, int count) throws SQLException { synchronized(Backend.THREADLOCK) { return _move(m_pointer, System.identityHashCode(Thread.currentThread()), forward, count); } } private static native String _getName(long pointer) throws SQLException; private static native int _getPortalPos(long pointer) throws SQLException; private static native TupleDesc _getTupleDesc(long pointer) throws SQLException; private static native int _fetch(long pointer, long threadId, boolean forward, int count) throws SQLException; private static native void _close(long pointer); private static native boolean _isAtEnd(long pointer) throws SQLException; private static native boolean _isAtStart(long pointer) throws SQLException; private static native boolean _isPosOverflow(long pointer) throws SQLException; private static native int _move(long pointer, long threadId, boolean forward, int count) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ELogHandler.java0000644000014500000120000001241211634451403026161 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Properties; import java.util.logging.Filter; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; /** * Provides access to the loggin mechanism of the PostgreSQL server. * * @author Thomas Hallgren */ public class ELogHandler extends Handler { /** * Debugging messages, in categories of * decreasing detail. */ public static final int LOG_DEBUG5 = 10; public static final int LOG_DEBUG4 = 11; public static final int LOG_DEBUG3 = 12; public static final int LOG_DEBUG2 = 13; public static final int LOG_DEBUG1 = 14; /** * Server operational messages; sent only * to server log by default. */ public static final int LOG_LOG = 15; /** * Informative messages that are always * sent to client; is not affected by * client_min_messages */ public static final int LOG_INFO = 17; /** * Helpful messages to users about query * operation; sent to client and server * log by default. */ public static final int LOG_NOTICE = 18; /** * Warnings */ public static final int LOG_WARNING = 19; /** * user error - abort transaction; return * to known state */ public static final int LOG_ERROR = 20; /** * fatal error - abort process */ public static final int LOG_FATAL = 21; /** * take down the other backends with me */ public static final int LOG_PANIC = 22; /* (non-Javadoc) * @see java.util.logging.Handler#publish(java.util.logging.LogRecord) */ public void publish(LogRecord record) { Level level = record.getLevel(); int pgLevel; if(level == null) pgLevel = LOG_LOG; else if(level.equals(Level.SEVERE)) pgLevel = LOG_ERROR; else if(level.equals(Level.WARNING)) pgLevel = LOG_WARNING; else if(level.equals(Level.INFO)) pgLevel = LOG_INFO; else if(level.equals(Level.FINE)) pgLevel = LOG_DEBUG1; else if(level.equals(Level.FINER)) pgLevel = LOG_DEBUG2; else if(level.equals(Level.FINEST)) pgLevel = LOG_DEBUG3; else pgLevel = LOG_LOG; Backend.log(pgLevel, this.getFormatter().format(record)); } public ELogHandler() { this.configure(); } /** * This is a no-op. */ public void flush() { } /** * This is a no-op. */ public void close() throws SecurityException { } public static void init() { Properties props = new Properties(); props.setProperty("handlers", ELogHandler.class.getName()); props.setProperty(".level", getPgLevel().getName()); ByteArrayOutputStream po = new ByteArrayOutputStream(); try { props.store(po, null); LogManager.getLogManager().readConfiguration( new ByteArrayInputStream(po.toByteArray())); } catch(IOException e) { } } /** * Obtains the "log_min_messages" configuration variable and * translates it into a {@link Level} object. * @return The Level that corresponds to the configuration variable. */ public static Level getPgLevel() { // We use this little trick to provide the correct config option // without having to call back into the JNI code the first time // around since that call will be a bit premature (it will come // during JVM initialization and before the native method is // registered). // String pgLevel = Backend.getConfigOption("log_min_messages"); Level level = Level.ALL; if(pgLevel != null) { pgLevel = pgLevel.toLowerCase().trim(); if(pgLevel.equals("panic") || pgLevel.equals("fatal")) level = Level.OFF; else if(pgLevel.equals("error")) level = Level.SEVERE; else if(pgLevel.equals("warning")) level = Level.WARNING; else if(pgLevel.equals("notice")) level = Level.CONFIG; else if(pgLevel.equals("info")) level = Level.INFO; else if(pgLevel.equals("debug1")) level = Level.FINE; else if(pgLevel.equals("debug2")) level = Level.FINER; else if(pgLevel.equals("debug3") || pgLevel.equals("debug4") || pgLevel.equals("debug5")) level = Level.FINEST; } return level; } // Private method to configure an ELogHandler // private void configure() { LogManager mgr = LogManager.getLogManager(); String cname = ELogHandler.class.getName(); String val = mgr.getProperty(cname + ".filter"); if(val != null) { try { this.setFilter((Filter)Class.forName(val.trim()).newInstance()); } catch (Exception e) { val = null; } } if(val == null) this.setFilter(null); val = mgr.getProperty(cname + ".formatter"); if(val != null) { try { this.setFormatter((Formatter)Class.forName(val.trim()).newInstance()); } catch (Exception e) { val = null; } } if(val == null) this.setFormatter(new ELogFormatter()); val = mgr.getProperty(cname + ".encoding"); if(val != null) { try { setEncoding(val.trim()); } catch(Exception e) { val = null; } } if(val == null) try { setEncoding(null); } catch (Exception e) { /* ignore */ } } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/HeapTupleHeader.java0000644000014500000120000000270411634451403027040 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The HeapTupleHeader correspons to the internal PostgreSQL * HeapTupleHeader struct. * * @author Thomas Hallgren */ public class HeapTupleHeader extends JavaWrapper { private final TupleDesc m_tupleDesc; HeapTupleHeader(long pointer, TupleDesc tupleDesc) { super(pointer); m_tupleDesc = tupleDesc; } /** * Obtains a value from the underlying native HeapTupleHeader * structure. * @param index Index of value in the structure (one based). * @return The value or null. * @throws SQLException If the underlying native structure has gone stale. */ public final Object getObject(int index) throws SQLException { synchronized(Backend.THREADLOCK) { return _getObject(this.getNativePointer(), m_tupleDesc.getNativePointer(), index); } } /** * Obtains the TupleDesc that describes the tuple and returns it. * @return The TupleDesc that describes this tuple. */ public final TupleDesc getTupleDesc() { return m_tupleDesc; } protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/SPIException.java0000644000014500000120000000114111634451403026344 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * @author Thomas Hallgren */ public class SPIException extends SQLException { private static final long serialVersionUID = -834098440757881189L; public SPIException(int resultCode) { super("SPI exception. Result = " + SPI.getResultText(resultCode)); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/ErrorData.java0000644000014500000120000001057711634451403025732 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; /** * The ErrorData correspons to the ErrorData obtained * using an internal PostgreSQL CopyErrorData call. * * @author Thomas Hallgren */ public class ErrorData extends JavaWrapper { ErrorData(long pointer) { super(pointer); } /** * Returns The error level */ public int getErrorLevel() { synchronized(Backend.THREADLOCK) { return _getErrorLevel(this.getNativePointer()); } } /** * Returns true if the error will be reported to the server log */ public boolean isOutputToServer() { synchronized(Backend.THREADLOCK) { return _isOutputToServer(this.getNativePointer()); } } /** * Returns true if the error will be reported to the client */ public boolean isOutputToClient() { synchronized(Backend.THREADLOCK) { return _isOutputToClient(this.getNativePointer()); } } /** * Returns true if funcname inclusion is set */ public boolean isShowFuncname() { synchronized(Backend.THREADLOCK) { return _isShowFuncname(this.getNativePointer()); } } /** * Returns The file where the error occured */ public String getFilename() { synchronized(Backend.THREADLOCK) { return _getFilename(this.getNativePointer()); } } /** * Returns The line where the error occured */ public int getLineno() { synchronized(Backend.THREADLOCK) { return _getLineno(this.getNativePointer()); } } /** * Returns the name of the function where the error occured */ public String getFuncname() { synchronized(Backend.THREADLOCK) { return _getFuncname(this.getNativePointer()); } } /** * Returns the unencoded ERRSTATE */ public String getSqlState() { synchronized(Backend.THREADLOCK) { return _getSqlState(this.getNativePointer()); } } /** * Returns the primary error message */ public String getMessage() { synchronized(Backend.THREADLOCK) { return _getMessage(this.getNativePointer()); } } /** * Returns the detailed error message */ public String getDetail() { synchronized(Backend.THREADLOCK) { return _getDetail(this.getNativePointer()); } } /** * Returns the hint message */ public String getHint() { synchronized(Backend.THREADLOCK) { return _getHint(this.getNativePointer()); } } /** * Returns the context message */ public String getContextMessage() { synchronized(Backend.THREADLOCK) { return _getContextMessage(this.getNativePointer()); } } /** * Returns the cursor index into the query string */ public int getCursorPos() { synchronized(Backend.THREADLOCK) { return _getCursorPos(this.getNativePointer()); } } /** * Returns the cursor index into internal query */ public int getInternalPos() { synchronized(Backend.THREADLOCK) { return _getInternalPos(this.getNativePointer()); } } /** * Returns the internally-generated query */ public String getInternalQuery() { synchronized(Backend.THREADLOCK) { return _getInternalQuery(this.getNativePointer()); } } /** * Returns the errno at entry */ public int getSavedErrno() { synchronized(Backend.THREADLOCK) { return _getSavedErrno(this.getNativePointer()); } } private static native int _getErrorLevel(long pointer); private static native boolean _isOutputToServer(long pointer); private static native boolean _isOutputToClient(long pointer); private static native boolean _isShowFuncname(long pointer); private static native String _getFilename(long pointer); private static native int _getLineno(long pointer); private static native String _getFuncname(long pointer); private static native String _getSqlState(long pointer); private static native String _getMessage(long pointer); private static native String _getDetail(long pointer); private static native String _getHint(long pointer); private static native String _getContextMessage(long pointer); private static native int _getCursorPos(long pointer); private static native int _getInternalPos(long pointer); private static native String _getInternalQuery(long pointer); private static native int _getSavedErrno(long pointer); /* errno at entry */ protected native void _free(long pointer); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/SPI.java0000644000014500000120000001026111634451403024470 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; /** * The SPI class provides access to some global * variables used by SPI. * * @author Thomas Hallgren */ public class SPI { public static final int ERROR_CONNECT = -1; public static final int ERROR_COPY = -2; public static final int ERROR_OPUNKNOWN = -3; public static final int ERROR_UNCONNECTED = -4; public static final int ERROR_CURSOR = -5; public static final int ERROR_ARGUMENT = -6; public static final int ERROR_PARAM = -7; public static final int ERROR_TRANSACTION = -8; public static final int ERROR_NOATTRIBUTE = -9; public static final int ERROR_NOOUTFUNC = -10; public static final int ERROR_TYPUNKNOWN = -11; public static final int OK_CONNECT = 1; public static final int OK_FINISH = 2; public static final int OK_FETCH = 3; public static final int OK_UTILITY = 4; public static final int OK_SELECT = 5; public static final int OK_SELINTO = 6; public static final int OK_INSERT = 7; public static final int OK_DELETE = 8; public static final int OK_UPDATE = 9; public static final int OK_CURSOR = 10; /** * Execute a command using the internal SPI_exec function. * @param command The command to execute. * @param rowCount The maximum number of tuples to create. A value * of rowCount of zero is interpreted as no limit, i.e., * run to completion. * @return One of the declared status codes. */ public static int exec(String command, int rowCount) { synchronized(Backend.THREADLOCK) { return _exec(System.identityHashCode(Thread.currentThread()), command, rowCount); } } public static void freeTupTable() { synchronized(Backend.THREADLOCK) { _freeTupTable(); } } /** * Returns the value of the global variable SPI_processed. */ public static int getProcessed() { synchronized(Backend.THREADLOCK) { return _getProcessed(); } } /** * Returns the value of the global variable SPI_result. */ public static int getResult() { synchronized(Backend.THREADLOCK) { return _getResult(); } } /** * Returns the value of the global variable SPI_tuptable. */ public static TupleTable getTupTable(TupleDesc known) { synchronized(Backend.THREADLOCK) { return _getTupTable(known); } } /** * Returns a textual representatio of a result code */ public static String getResultText(int resultCode) { String s; switch(resultCode) { case ERROR_CONNECT: s = "ERROR_CONNECT"; break; case ERROR_COPY: s = "ERROR_COPY"; break; case ERROR_OPUNKNOWN: s = "ERROR_OPUNKNOWN"; break; case ERROR_UNCONNECTED: s = "ERROR_UNCONNECTED"; break; case ERROR_CURSOR: s = "ERROR_CURSOR"; break; case ERROR_ARGUMENT: s = "ERROR_ARGUMENT"; break; case ERROR_PARAM: s = "ERROR_PARAM"; break; case ERROR_TRANSACTION: s = "ERROR_TRANSACTION"; break; case ERROR_NOATTRIBUTE: s = "ERROR_NOATTRIBUTE"; break; case ERROR_NOOUTFUNC: s = "ERROR_NOOUTFUNC"; break; case ERROR_TYPUNKNOWN: s = "ERROR_TYPUNKNOWN"; break; case OK_CONNECT: s = "OK_CONNECT"; break; case OK_FINISH: s = "OK_FINISH"; break; case OK_FETCH: s = "OK_FETCH"; break; case OK_UTILITY: s = "OK_UTILITY"; break; case OK_SELECT: s = "OK_SELECT"; break; case OK_SELINTO: s = "OK_SELINTO"; break; case OK_INSERT: s = "OK_INSERT"; break; case OK_DELETE: s = "OK_DELETE"; break; case OK_UPDATE: s = "OK_UPDATE"; break; case OK_CURSOR: s = "OK_CURSOR"; break; default: s = "Unkown result code: " + resultCode; } return s; } private native static int _exec(long threadId, String command, int rowCount); private native static int _getProcessed(); private native static int _getResult(); private native static void _freeTupTable(); private native static TupleTable _getTupTable(TupleDesc known); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/PgSavepoint.java0000644000014500000120000000541411634451403026300 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.Connection; import java.sql.SQLException; import java.util.Iterator; import java.util.WeakHashMap; import java.util.logging.Logger; /** * @author Thomas Hallgren */ public class PgSavepoint implements java.sql.Savepoint { private static final WeakHashMap s_knownSavepoints = new WeakHashMap(); private long m_pointer; PgSavepoint(long pointer) { m_pointer = pointer; } public static PgSavepoint set(String name) throws SQLException { synchronized(Backend.THREADLOCK) { PgSavepoint sp = new PgSavepoint(_set(name)); s_knownSavepoints.put(sp, Boolean.TRUE); return sp; } } static PgSavepoint forId(int savepointId) { if(savepointId != 0) { synchronized(Backend.THREADLOCK) { Iterator itor = s_knownSavepoints.keySet().iterator(); while(itor.hasNext()) { PgSavepoint sp = (PgSavepoint)itor.next(); if(savepointId == _getId(sp.m_pointer)) return sp; } } } return null; } public int hashCode() { return this.getSavepointId(); } public boolean equals(Object o) { return (this == o); } public void release() throws SQLException { synchronized(Backend.THREADLOCK) { _release(m_pointer); s_knownSavepoints.remove(this); m_pointer = 0; } } public void rollback() throws SQLException { synchronized(Backend.THREADLOCK) { _rollback(m_pointer); s_knownSavepoints.remove(this); m_pointer = 0; } } public String getSavepointName() throws SQLException { synchronized(Backend.THREADLOCK) { return _getName(m_pointer); } } public int getSavepointId() { synchronized(Backend.THREADLOCK) { return _getId(m_pointer); } } public void onInvocationExit(Connection conn) throws SQLException { if(m_pointer == 0) return; Logger logger = Logger.getAnonymousLogger(); if(Backend.isReleaseLingeringSavepoints()) { logger.warning("Releasing savepoint '" + _getId(m_pointer) + "' since its lifespan exceeds that of the function where it was set"); conn.releaseSavepoint(this); } else { logger.warning("Rolling back to savepoint '" + _getId(m_pointer) + "' since its lifespan exceeds that of the function where it was set"); conn.rollback(this); } } private static native long _set(String name) throws SQLException; private static native void _release(long pointer) throws SQLException; private static native void _rollback(long pointer) throws SQLException; private static native String _getName(long pointer); private static native int _getId(long pointer); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/TriggerData.java0000644000014500000120000002336711634451403026245 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.jdbc.TriggerResultSet; /** * The TriggerData correspons to the internal PostgreSQL TriggerData. * * @author Thomas Hallgren */ public class TriggerData extends JavaWrapper implements org.postgresql.pljava.TriggerData { private Relation m_relation; private TriggerResultSet m_old = null; private TriggerResultSet m_new = null; private Tuple m_newTuple; private Tuple m_triggerTuple; TriggerData(long pointer) { super(pointer); } /** * Returns the ResultSet that represents the new row. This ResultSet will * be null for delete triggers and for triggers that was fired for * statement.
The returned set will be updateable and positioned on a * valid row. * * @return An updateable ResultSet containing one row or * null. * @throws SQLException * if the contained native buffer has gone stale. */ public ResultSet getNew() throws SQLException { if (m_new != null) return m_new; if (this.isFiredByDelete() || this.isFiredForStatement()) return null; // PostgreSQL uses the trigger tuple as the new tuple for inserts. // Tuple tuple = this.isFiredByInsert() ? this.getTriggerTuple() : this.getNewTuple(); // Triggers fired after will always have a read-only row // m_new = new TriggerResultSet(this.getRelation().getTupleDesc(), tuple, this.isFiredAfter()); return m_new; } /** * Returns the ResultSet that represents the old row. This ResultSet will * be null for insert triggers and for triggers that was fired for * statement.
The returned set will be read-only and positioned on a * valid row. * * @return A read-only ResultSet containing one row or * null. * @throws SQLException * if the contained native buffer has gone stale. */ public ResultSet getOld() throws SQLException { if (m_old != null) return m_old; if (this.isFiredByInsert() || this.isFiredForStatement()) return null; m_old = new TriggerResultSet(this.getRelation().getTupleDesc(), this.getTriggerTuple(), true); return m_old; } /** * Commits the changes made on the ResultSet representing * new and returns the native pointer of new tuple. This * method is called automatically by the trigger handler and should not * be called in any other way. * * @return The modified tuple, or if no modifications have been made, the * original tuple. */ public long getTriggerReturnTuple() throws SQLException { if(this.isFiredForStatement() || this.isFiredAfter()) // // Only triggers fired before each row can have a return // value. // return 0; if (m_new != null) { Object[] changes = m_new.getChangeIndexesAndValues(); if (changes != null) { Tuple original = (Tuple)changes[0]; int[] indexes = (int[])changes[1]; Object[] values = (Object[])changes[2]; return this.getRelation().modifyTuple(original, indexes, values).getNativePointer(); } } // Return the original tuple. // return (this.isFiredByUpdate() ? this.getNewTuple() : this.getTriggerTuple()).getNativePointer(); } public String getTableName() throws SQLException { return this.getRelation().getName(); } public String getSchemaName() throws SQLException { return this.getRelation().getSchema(); } /** * Returns a descriptor for the Tuples exposed by this trigger. * * @throws SQLException * if the contained native buffer has gone stale. */ public Relation getRelation() throws SQLException { if(m_relation == null) { synchronized(Backend.THREADLOCK) { m_relation = _getRelation(this.getNativePointer()); } } return m_relation; } /** * Returns a Tuple reflecting the row for which the trigger * was fired. This is the row being inserted, updated, or deleted. If this * trigger was fired for an * INSERT or DELETE * then this is what you should return to from the method if you don't want * to replace the row with a different one (in the case of INSERT * ) * or skip the operation. * * @throws SQLException * if the contained native buffer has gone stale. */ public Tuple getTriggerTuple() throws SQLException { if(m_triggerTuple == null) { synchronized(Backend.THREADLOCK) { m_triggerTuple = _getTriggerTuple(this.getNativePointer()); } } return m_triggerTuple; } /** * Returns a Tuple reflecting the new version of the row, if * the trigger was fired for an UPDATE, and null * if it is for an INSERT or a DELETE. This * is what you have to return from the function if the event is an UPDATE * and you don't want to replace this row by a different one or skip the * operation. * * @throws SQLException * if the contained native buffer has gone stale. */ public Tuple getNewTuple() throws SQLException { if(m_newTuple == null) { synchronized(Backend.THREADLOCK) { m_newTuple = _getNewTuple(this.getNativePointer()); } } return m_newTuple; } /** * Returns the arguments for this trigger (as declared in the CREATE TRIGGER * statement. If the trigger has no arguments, this method will return an * array with size 0. * * @throws SQLException * if the contained native buffer has gone stale. */ public String[] getArguments() throws SQLException { synchronized(Backend.THREADLOCK) { return _getArguments(this.getNativePointer()); } } /** * Returns the name of the trigger (as declared in the CREATE TRIGGER * statement). * * @throws SQLException * if the contained native buffer has gone stale. */ public String getName() throws SQLException { synchronized(Backend.THREADLOCK) { return _getName(this.getNativePointer()); } } /** * Returns true if the trigger was fired after the statement * or row action that it is associated with. * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredAfter() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredAfter(this.getNativePointer()); } } /** * Returns true if the trigger was fired before the * statement or row action that it is associated with. * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredBefore() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredBefore(this.getNativePointer()); } } /** * Returns true if this trigger is fired once for each row * (as opposed to once for the entire statement). * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredForEachRow() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredForEachRow(this.getNativePointer()); } } /** * Returns true if this trigger is fired once for the entire * statement (as opposed to once for each row). * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredForStatement() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredForStatement(this.getNativePointer()); } } /** * Returns true if this trigger was fired by a DELETE. * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredByDelete() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredByDelete(this.getNativePointer()); } } /** * Returns true if this trigger was fired by an INSERT. * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredByInsert() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredByInsert(this.getNativePointer()); } } /** * Returns true if this trigger was fired by an UPDATE. * * @throws SQLException * if the contained native buffer has gone stale. */ public boolean isFiredByUpdate() throws SQLException { synchronized(Backend.THREADLOCK) { return _isFiredByUpdate(this.getNativePointer()); } } protected native void _free(long pointer); private static native Relation _getRelation(long pointer) throws SQLException; private static native Tuple _getTriggerTuple(long pointer) throws SQLException; private static native Tuple _getNewTuple(long pointer) throws SQLException; private static native String[] _getArguments(long pointer) throws SQLException; private static native String _getName(long pointer) throws SQLException; private static native boolean _isFiredAfter(long pointer) throws SQLException; private static native boolean _isFiredBefore(long pointer) throws SQLException; private static native boolean _isFiredForEachRow(long pointer) throws SQLException; private static native boolean _isFiredForStatement(long pointer) throws SQLException; private static native boolean _isFiredByDelete(long pointer) throws SQLException; private static native boolean _isFiredByInsert(long pointer) throws SQLException; private static native boolean _isFiredByUpdate(long pointer) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Relation.java0000644000014500000120000000520111634451403025610 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The Relation correspons to the internal PostgreSQL * Relation. * * @author Thomas Hallgren */ public class Relation extends JavaWrapper { private TupleDesc m_tupleDesc; Relation(long pointer) { super(pointer); } /** * Returns the name of this Relation. * @throws SQLException */ public String getName() throws SQLException { synchronized(Backend.THREADLOCK) { return _getName(this.getNativePointer()); } } /** * Returns the schema name of this Relation. * @throws SQLException */ public String getSchema() throws SQLException { synchronized(Backend.THREADLOCK) { return _getSchema(this.getNativePointer()); } } /** * Returns a descriptor that describes tuples in this Relation. * @throws SQLException */ public TupleDesc getTupleDesc() throws SQLException { if(m_tupleDesc == null) { synchronized(Backend.THREADLOCK) { m_tupleDesc = _getTupleDesc(this.getNativePointer()); } } return m_tupleDesc; } /** * Creates a new Tuple by substituting new values for selected columns * copying the columns of the original Tuple at other positions. The * original Tuple is not modified.
* @param original The tuple that serves as the source. * @param fieldNumbers An array of one based indexes denoting the positions that * are to receive modified values. * @param values The array of new values. Each value in this array corresponds to * an index in the fieldNumbers array. * @return A copy of the original with modifications. * @throws SQLException if indexes are out of range or the values illegal. */ public Tuple modifyTuple(Tuple original, int[] fieldNumbers, Object[] values) throws SQLException { synchronized(Backend.THREADLOCK) { return _modifyTuple(this.getNativePointer(), original.getNativePointer(), fieldNumbers, values); } } protected native void _free(long pointer); private static native String _getName(long pointer) throws SQLException; private static native String _getSchema(long pointer) throws SQLException; private static native TupleDesc _getTupleDesc(long pointer) throws SQLException; private static native Tuple _modifyTuple(long pointer, long original, int[] fieldNumbers, Object[] values) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/XactListener.java0000644000014500000120000000350011634451403026440 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; import java.util.HashMap; import org.postgresql.pljava.TransactionListener; /** * Class that enables registrations using the PostgreSQL RegisterXactCallback * function. * * @author Thomas Hallgren */ class XactListener { private static final HashMap s_listeners = new HashMap(); static void onAbort(long listenerId) throws SQLException { TransactionListener listener = (TransactionListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onAbort(Backend.getSession()); } static void onCommit(long listenerId) throws SQLException { TransactionListener listener = (TransactionListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onCommit(Backend.getSession()); } static void onPrepare(long listenerId) throws SQLException { TransactionListener listener = (TransactionListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onPrepare(Backend.getSession()); } static void addListener(TransactionListener listener) { synchronized(Backend.THREADLOCK) { long key = System.identityHashCode(listener); if(s_listeners.put(new Long(key), listener) != listener) _register(key); } } static void removeListener(TransactionListener listener) { synchronized(Backend.THREADLOCK) { long key = System.identityHashCode(listener); if(s_listeners.remove(new Long(key)) == listener) _unregister(key); } } private static native void _register(long listenerId); private static native void _unregister(long listenerId); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Oid.java0000644000014500000120000001227311634451403024555 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; import java.util.HashMap; /** * The Oid correspons to the internal PostgreSQL Oid. * Should the size of that change from 32 bit, this class must change too. * In Java, the InvalidOid is represented as null. * * @author Thomas Hallgren */ public class Oid extends Number { private static final HashMap s_class2typeId = new HashMap(); private static final HashMap s_typeId2class = new HashMap(); static { try { // Ensure that the SPI JDBC driver is loaded and registered // with the java.sql.DriverManager. // Class.forName("org.postgresql.pljava.jdbc.SPIDriver"); } catch(ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } /** * Finds the PostgreSQL well known Oid for the given class. * @param clazz The class. * @return The well known Oid or null if no such Oid could be found. */ public static Oid forJavaClass(Class clazz) { return (Oid)s_class2typeId.get(clazz); } /** * Finds the PostgreSQL well known Oid for a type name. * @param typeString The name of the type, optionally qualified with a namespace. * @return The well known Oid. * @throws SQLException if the type could not be found */ public static Oid forTypeName(String typeString) { synchronized(Backend.THREADLOCK) { return new Oid(_forTypeName(typeString)); } } /** * Finds the PostgreSQL well known Oid for the XOPEN Sql type. * @param sqlType The XOPEN type code. * @throws SQLException if the type could not be found */ public static Oid forSqlType(int sqlType) { synchronized(Backend.THREADLOCK) { return new Oid(_forSqlType(sqlType)); } } /** * Returns the PostgreSQL type id for the Oid type. */ public static Oid getTypeId() { synchronized(Backend.THREADLOCK) { return _getTypeId(); } } /** * A Type well known to PostgreSQL but not known as a standard XOPEN * SQL type can be registered here. This includes types like the Oid * itself and all the geometry related types. * @param clazz The Java class that corresponds to the type id. * @param typeId The well known type id. */ public static void registerType(Class clazz, Oid typeId) { s_class2typeId.put(clazz, typeId); if(!s_typeId2class.containsKey(typeId)) s_typeId2class.put(typeId, clazz); } /* * The native Oid represented as a 32 bit quantity. * See definition in file "include/postgres_ext" of the * PostgreSQL distribution. */ private final int m_native; public Oid(int value) { m_native = value; } public double doubleValue() { return m_native; } /** * Checks to see if the other object is an Oid, and if so, * if the native value of that Oid equals the native value * of this Oid. * @return true if the objects are equal. */ public boolean equals(Object o) { return (o == this) || ((o instanceof Oid) && ((Oid)o).m_native == m_native); } public float floatValue() { return m_native; } public Class getJavaClass() throws SQLException { Class c = (Class)s_typeId2class.get(this); if(c == null) { String className; synchronized(Backend.THREADLOCK) { className = _getJavaClassName(m_native); } try { c = Class.forName(getCanonicalClassName(className, 0)); } catch(ClassNotFoundException e) { throw new SQLException(e.getMessage()); } s_typeId2class.put(this, c); s_class2typeId.put(c, this); } return c; } /** * The native value is used as the hash code. * @return The hashCode for this Oid. */ public int hashCode() { return m_native; } public int intValue() { return m_native; } public long longValue() { return m_native; } /** * Returns a string representation of this OID. */ public String toString() { return "OID(" + m_native + ')'; } private static String getCanonicalClassName(String name, int nDims) { if(name.endsWith("[]")) return getCanonicalClassName(name.substring(0, name.length() - 2), nDims + 1); boolean primitive = true; if(name.equals("boolean")) name = "Z"; else if(name.equals("byte")) name = "B"; else if(name.equals("char")) name = "C"; else if(name.equals("double")) name = "D"; else if(name.equals("float")) name = "F"; else if(name.equals("int")) name = "I"; else if(name.equals("long")) name = "J"; else if(name.equals("short")) name = "S"; else primitive = false; if(nDims > 0) { StringBuffer bld = new StringBuffer(); while(--nDims >= 0) bld.append('['); if(primitive) bld.append(name); else { bld.append('L'); bld.append(name); bld.append(';'); } name = bld.toString(); } return name; } private native static int _forTypeName(String typeString); private native static int _forSqlType(int sqlType); private native static Oid _getTypeId(); private native static String _getJavaClassName(int nativeOid) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/SubXactListener.java0000644000014500000120000000405511634451403027120 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; import java.util.HashMap; import org.postgresql.pljava.SavepointListener; /** * Class that enables registrations using the PostgreSQL RegisterSubXactCallback * function. * * @author Thomas Hallgren */ class SubXactListener { private static final HashMap s_listeners = new HashMap(); static void onAbort(long listenerId, int spId, int parentSpId) throws SQLException { SavepointListener listener = (SavepointListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onAbort(Backend.getSession(), PgSavepoint.forId(spId), PgSavepoint.forId(parentSpId)); } static void onCommit(long listenerId, int spId, int parentSpId) throws SQLException { SavepointListener listener = (SavepointListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onCommit(Backend.getSession(), PgSavepoint.forId(spId), PgSavepoint.forId(parentSpId)); } static void onStart(long listenerId, long spPointer, int parentSpId) throws SQLException { SavepointListener listener = (SavepointListener)s_listeners.get(new Long(listenerId)); if(listener != null) listener.onStart(Backend.getSession(), new PgSavepoint(spPointer), PgSavepoint.forId(parentSpId)); } static void addListener(SavepointListener listener) { synchronized(Backend.THREADLOCK) { long key = System.identityHashCode(listener); if(s_listeners.put(new Long(key), listener) != listener) _register(key); } } static void removeListener(SavepointListener listener) { synchronized(Backend.THREADLOCK) { long key = System.identityHashCode(listener); if(s_listeners.remove(new Long(key)) == listener) _unregister(key); } } private static native void _register(long listenerId); private static native void _unregister(long listenerId); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Session.java0000644000014500000120000000631611634451403025466 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import org.postgresql.pljava.ObjectPool; import org.postgresql.pljava.SavepointListener; import org.postgresql.pljava.TransactionListener; import org.postgresql.pljava.jdbc.SQLUtils; /** * An instance of this interface reflects the current session. The attribute * store is transactional. * * @author Thomas Hallgren */ public class Session implements org.postgresql.pljava.Session { private final TransactionalMap m_attributes = new TransactionalMap(new HashMap()); /** * Adds the specified listener to the list of listeners that will * receive transactional events. */ public void addTransactionListener(TransactionListener listener) { XactListener.addListener(listener); } /** * Adds the specified listener to the list of listeners that will * receive savepoint events. */ public void addSavepointListener(SavepointListener listener) { SubXactListener.addListener(listener); } public Object getAttribute(String attributeName) { return m_attributes.get(attributeName); } public ObjectPool getObjectPool(Class cls) { return ObjectPoolImpl.getObjectPool(cls); } /** * Return the current user. */ public String getUserName() { return AclId.getUser().getName(); } /** * Return the session user. */ public String getSessionUserName() { return AclId.getSessionUser().getName(); } public void removeAttribute(String attributeName) { m_attributes.remove(attributeName); } public void setAttribute(String attributeName, Object value) { m_attributes.put(attributeName, value); } /** * Removes the specified listener from the list of listeners that will * receive transactional events. */ public void removeTransactionListener(TransactionListener listener) { XactListener.removeListener(listener); } /** * Removes the specified listener from the list of listeners that will * receive savepoint events. */ public void removeSavepointListener(SavepointListener listener) { SubXactListener.removeListener(listener); } public void executeAsSessionUser(Connection conn, String statement) throws SQLException { Statement stmt = conn.createStatement(); synchronized(Backend.THREADLOCK) { ResultSet rs = null; AclId sessionUser = AclId.getSessionUser(); AclId effectiveUser = AclId.getUser(); try { _setUser(sessionUser); if(stmt.execute(statement)) { rs = stmt.getResultSet(); rs.next(); } } finally { SQLUtils.close(rs); SQLUtils.close(stmt); _setUser(effectiveUser); } } } /** * Called from native code when the JVM is instantiated. */ static long init() throws SQLException { ELogHandler.init(); // Should be replace with a Thread.getId() once we abandon // Java 1.4 // return System.identityHashCode(Thread.currentThread()); } private static native void _setUser(AclId userId); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/Tuple.java0000644000014500000120000000250711634451403025132 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The Tuple correspons to the internal PostgreSQL * HeapTuple. * * @author Thomas Hallgren */ public class Tuple extends JavaWrapper { Tuple(long pointer) { super(pointer); } /** * Obtains a value from the underlying native HeapTuple * structure. * @param tupleDesc The Tuple descriptor for this instance. * @param index Index of value in the structure (one based). * @return The value or null. * @throws SQLException If the underlying native structure has gone stale. */ public Object getObject(TupleDesc tupleDesc, int index) throws SQLException { synchronized(Backend.THREADLOCK) { return _getObject(this.getNativePointer(), tupleDesc.getNativePointer(), index); } } /** * Calls the backend function heap_freetuple(HeapTuple tuple) * @param pointer The native pointer to the source HeapTuple */ protected native void _free(long pointer); private static native Object _getObject(long pointer, long tupleDescPointer, int index) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/internal/AclId.java0000644000014500000120000000476011634451403025020 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.internal; import java.sql.SQLException; /** * The AclId correspons to the internal PostgreSQL AclId. * * @author Thomas Hallgren */ public final class AclId { private final int m_native; /** * Called from native code. */ public AclId(int nativeAclId) { m_native = nativeAclId; } /** * Returns equal if other is an AclId that is equal to this id. */ public boolean equals(Object other) { return this == other || ((other instanceof AclId) && ((AclId)other).m_native == m_native); } /** * Returns the integer value of this id. */ public int intValue() { return m_native; } /** * Returns the hashCode of this id. */ public int hashCode() { return m_native; } /** * Return the id of the current database user. */ public static AclId getUser() { synchronized(Backend.THREADLOCK) { return _getUser(); } } /** * Return the id of the session user. */ public static AclId getSessionUser() { synchronized(Backend.THREADLOCK) { return _getSessionUser(); } } /** * Return the id of the session user. * @throws SQLException if the user is unknown to the system. */ public static AclId fromName(String name) throws SQLException { synchronized(Backend.THREADLOCK) { return _fromName(name); } } /** * Return the name that corresponds to this id. */ public String getName() { synchronized(Backend.THREADLOCK) { return this._getName(); } } /** * Return true if this AclId has the right to create new objects * in the given schema. */ public boolean hasSchemaCreatePermission(Oid oid) { synchronized(Backend.THREADLOCK) { return this._hasSchemaCreatePermission(oid); } } /** * Returns true if this AclId represents a super user. */ public boolean isSuperuser() { synchronized(Backend.THREADLOCK) { return this._isSuperuser(); } } /** * Returns the result of calling #getName(). */ public String toString() { return this.getName(); } private static native AclId _getUser(); private static native AclId _getSessionUser(); private static native AclId _fromName(String name); private native String _getName(); private native boolean _hasSchemaCreatePermission(Oid oid); private native boolean _isSuperuser(); } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/sqlj/0000755000014500000120000000000011634451403022327 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/sqlj/Loader.java0000644000014500000120000002152511634451403024405 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlj; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLData; import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.Oid; import org.postgresql.pljava.jdbc.SQLUtils; /** * @author Thomas Hallgren */ public class Loader extends ClassLoader { private final static Logger s_logger = Logger.getLogger(Loader.class.getName()); static class EntryEnumeration implements Enumeration { private final int[] m_entryIds; private int m_top = 0; EntryEnumeration(int[] entryIds) { m_entryIds = entryIds; } public boolean hasMoreElements() { return (m_top < m_entryIds.length); } public Object nextElement() throws NoSuchElementException { if (m_top >= m_entryIds.length) throw new NoSuchElementException(); return entryURL(m_entryIds[m_top++]); } } private static final String PUBLIC_SCHEMA = "public"; private static final Map s_schemaLoaders = new HashMap(); private static final Map s_typeMap = new HashMap(); /** * Removes all cached schema loaders, functions, and type maps. This * method is called by the utility functions that manipulate the * data that has been cached. It is not intended to be called * from user code. */ public static void clearSchemaLoaders() { s_schemaLoaders.clear(); s_typeMap.clear(); Backend.clearFunctionCache(); } /** * Obtains the loader that is in effect for the current schema (i.e. the * schema that is first in the search path). * @return A loader * @throws SQLException */ public static ClassLoader getCurrentLoader() throws SQLException { String schema; Statement stmt = SQLUtils.getDefaultConnection().createStatement(); ResultSet rs = null; try { rs = stmt.executeQuery("SELECT current_schema()"); if(!rs.next()) throw new SQLException("Unable to determine current schema"); schema = rs.getString(1); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } return getSchemaLoader(schema); } /** * Obtain a loader that has been configured for the class path of the * schema named schemaName. Class paths are defined using the * SQL procedure sqlj.set_classpath. * @param schemaName The name of the schema. * @return A loader. */ public static ClassLoader getSchemaLoader(String schemaName) throws SQLException { if(schemaName == null || schemaName.length() == 0) schemaName = PUBLIC_SCHEMA; else schemaName = schemaName.toLowerCase(); ClassLoader loader = (ClassLoader)s_schemaLoaders.get(schemaName); if(loader != null) return loader; Map classImages = new HashMap(); Connection conn = SQLUtils.getDefaultConnection(); PreparedStatement outer = null; PreparedStatement inner = null; try { // Read the entries so that the one with highest prio is read last. // outer = conn.prepareStatement( "SELECT r.jarId" + " FROM sqlj.jar_repository r INNER JOIN sqlj.classpath_entry c ON r.jarId = c.jarId" + " WHERE c.schemaName = ? ORDER BY c.ordinal DESC"); inner = conn.prepareStatement( "SELECT entryId, entryName FROM sqlj.jar_entry WHERE jarId = ?"); outer.setString(1, schemaName); ResultSet rs = outer.executeQuery(); try { while(rs.next()) { inner.setInt(1, rs.getInt(1)); ResultSet rs2 = inner.executeQuery(); try { while(rs2.next()) { int entryId = rs2.getInt(1); String entryName = rs2.getString(2); int[] oldEntry = (int[])classImages.get(entryName); if(oldEntry == null) classImages.put(entryName, new int[] { entryId }); else { int last = oldEntry.length; int[] newEntry = new int[last + 1]; newEntry[0] = entryId; System.arraycopy(oldEntry, 0, newEntry, 1, last); classImages.put(entryName, newEntry); } } } finally { SQLUtils.close(rs2); } } } finally { SQLUtils.close(rs); } } finally { SQLUtils.close(outer); SQLUtils.close(inner); } ClassLoader parent = ClassLoader.getSystemClassLoader(); if(classImages.size() == 0) // // No classpath defined for the schema. Default to // classpath of public schema or to the system classloader if the // request already is for the public schema. // loader = schemaName.equals(PUBLIC_SCHEMA) ? parent : getSchemaLoader(PUBLIC_SCHEMA); else loader = new Loader(classImages, parent); s_schemaLoaders.put(schemaName, loader); return loader; } /** * Returns the SQL type {@link Oid} to Java {@link Class} map that contains the * Java UDT mappings for the given schema. * This method is called by the function mapping mechanisms. Application code * should never call this method. * * @param schema The schema * @return The Map, possibly empty but never null. */ public static Map getTypeMap(final String schema) throws SQLException { Map typesForSchema = (Map)s_typeMap.get(schema); if(typesForSchema != null) return typesForSchema; s_logger.fine("Creating typeMappings for schema " + schema); typesForSchema = new HashMap() { public Object get(Object key) { s_logger.fine("Obtaining type mapping for OID " + key + " for schema " + schema); return super.get(key); } }; ClassLoader loader = Loader.getSchemaLoader(schema); Statement stmt = SQLUtils.getDefaultConnection().createStatement(); ResultSet rs = null; try { rs = stmt.executeQuery("SELECT javaName, sqlName FROM sqlj.typemap_entry"); while(rs.next()) { try { String javaClassName = rs.getString(1); String sqlName = rs.getString(2); Class cls = loader.loadClass(javaClassName); if(!SQLData.class.isAssignableFrom(cls)) throw new SQLException("Class " + javaClassName + " does not implement java.sql.SQLData"); Oid typeOid = Oid.forTypeName(sqlName); typesForSchema.put(typeOid, cls); s_logger.fine("Adding type mapping for OID " + typeOid + " -> class " + cls.getName() + " for schema " + schema); } catch(ClassNotFoundException e) { // Ignore, type is not know to this schema and that is ok } } if(typesForSchema.isEmpty()) typesForSchema = Collections.EMPTY_MAP; s_typeMap.put(schema, typesForSchema); return typesForSchema; } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } private static URL entryURL(int entryId) { try { return new URL( "dbf", "localhost", -1, "/" + entryId, EntryStreamHandler.getInstance()); } catch(MalformedURLException e) { throw new RuntimeException(e); } } private final Map m_entries; /** * Create a new Loader. * @param entries * @param parent */ Loader(Map entries, ClassLoader parent) { super(parent); m_entries = entries; } protected Class findClass(final String name) throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); int[] entryId = (int[])m_entries.get(path); if(entryId != null) { PreparedStatement stmt = null; ResultSet rs = null; try { // This code rely heavily on the fact that the connection // is a singleton and that the prepared statement will live // for the duration of the loader. // stmt = SQLUtils.getDefaultConnection().prepareStatement( "SELECT entryImage FROM sqlj.jar_entry WHERE entryId = ?"); stmt.setInt(1, entryId[0]); rs = stmt.executeQuery(); if(rs.next()) { byte[] img = rs.getBytes(1); rs.close(); rs = null; return this.defineClass(name, img, 0, img.length); } } catch(SQLException e) { Logger.getAnonymousLogger().log(Level.INFO, "Failed to load class", e); throw new ClassNotFoundException(name + " due to: " + e.getMessage()); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } throw new ClassNotFoundException(name); } protected URL findResource(String name) { int[] entryIds = (int[])m_entries.get(name); if(entryIds == null) return null; return entryURL(entryIds[0]); } protected Enumeration findResources(String name) throws IOException { int[] entryIds = (int[])m_entries.get(name); if(entryIds == null) entryIds = new int[0]; return new EntryEnumeration(entryIds); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/sqlj/EntryStreamHandler.java0000644000014500000120000000575111634451403026755 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.sqlj; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.jdbc.SQLUtils; /** * @author Thomas Hallgren */ class EntryStreamHandler extends URLStreamHandler { private static EntryStreamHandler s_instance; protected URLConnection openConnection(URL u) throws IOException { return new EntryConnection(u); } /** * Creates an EntryStreamHandler on the first invocation. This handler is * then cached and returned on subsequent invocations. * @return the one and only handler. */ static URLStreamHandler getInstance() { if(s_instance == null) s_instance = new EntryStreamHandler(); return s_instance; } class EntryConnection extends URLConnection { private final int m_entryId; private String m_entryName; private byte[] m_image; protected EntryConnection(URL entryURL) { super(entryURL); m_entryId = Integer.parseInt(url.getPath().substring(1)); } /** * Executes the prepared statement that will fetch an image based on * the entryId denoted by the URL of this connection. * If connect has been called already, subsequent calls are ignored. */ public void connect() throws IOException { if(connected) return; PreparedStatement stmt = null; ResultSet rs = null; try { stmt = SQLUtils.getDefaultConnection().prepareStatement( "SELECT entryName, entryImage FROM sqlj.jar_entry WHERE entryId = ?"); stmt.setInt(1, m_entryId); rs = stmt.executeQuery(); if(rs.next()) { m_entryName = rs.getString(1); m_image = rs.getBytes(2); connected = true; } else throw new FileNotFoundException("jarId = " + m_entryId); } catch(SQLException e) { throw new IOException(e.getMessage()); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } /** * Creates a new {@link java.io.ByteArrayInputStream} for the image that * was obtained during {@link #connect}. * @return the created input stream. */ public InputStream getInputStream() throws IOException { this.connect(); return new ByteArrayInputStream(m_image); } /** * Consults the current {@link java.net.FileNameMap FileNameMap} to obtain * the MIME type for the file name extension for the entry handled by this * connection. * @return the content type based on the file name extension. */ public String getContentType() { return getFileNameMap().getContentTypeFor(m_entryName); } } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/management/0000755000014500000120000000000011634451403023472 5ustar johannstaffpljava-1.4.3/src/java/pljava/org/postgresql/pljava/management/SQLDeploymentDescriptor.java0000644000014500000120000002600611634451403031100 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.management; import java.sql.Connection; import java.sql.SQLException; import java.text.ParseException; import java.util.ArrayList; import java.util.logging.Logger; import org.postgresql.pljava.Session; import org.postgresql.pljava.SessionManager; /** * This class deals with parsing and executing the deployment descriptor as * defined in ISO/IEC 9075-13:2003. It has the following format:


 * <descriptor file> ::=
 * SQLActions <left bracket> <right bracket> <equal sign>
 * { [ <double quote> <action group> <double quote>
 *   [ <comma> <double quote> <action group> <double quote> ] ] }
 *
 * <action group> ::=
 *     <install actions>
 *   | <remove actions>
 * 
 * <install actions> ::=
 *   BEGIN INSTALL [ <command> <semicolon> ]... END INSTALL
 *
 * <remove actions> ::=
 *   BEGIN REMOVE [ <command> <semicolon> ]... END REMOVE
 *
 * <command> ::=
 *     <SQL statement>
 *   | <implementor block>
 *
 * <SQL statement> ::= <SQL token>...
 * 
 * <implementor block> ::=
 *   BEGIN <implementor name> <SQL token>... END <implementor name>
 *
 * <implementor name> ::= <identifier>
 *
 * <SQL token> ::= an SQL lexical unit specified by the term "<token>" in
 * Subclause 5.2, "<token>" and "<separator>", in ISO/IEC 9075-2.
* * @author Thomas Hallgren */ public class SQLDeploymentDescriptor { private final ArrayList m_installCommands = new ArrayList(); private final ArrayList m_removeCommands = new ArrayList(); private final StringBuffer m_buffer = new StringBuffer(); private final char[] m_image; private final String m_implementorName; private final Logger m_logger; private int m_position = 0; /** * Parses the deployment descriptor descImage using * implementorName as discriminator for implementor specific * blocks. The install and remove blocks are remembered for later execution * with calls to {@link #install install()} and {@link #remove remove()}. * @param descImage The image to parse * @param implementorName The discriminator to use for implementor blocks * @throws ParseException If a parse error is encountered */ public SQLDeploymentDescriptor(String descImage, String implementorName) throws ParseException { m_image = descImage.toCharArray(); m_implementorName = implementorName; m_logger = Logger.getAnonymousLogger(); this.readDescriptor(); } /** * Executes the INSTALL actions. * @param conn The connection to use for the execution. * @throws SQLException */ public void install(Connection conn) throws SQLException { this.executeArray(m_installCommands, conn); } /** * Executes the REMOVE actions. * @param conn The connection to use for the execution. * @throws SQLException */ public void remove(Connection conn) throws SQLException { this.executeArray(m_removeCommands, conn); } /** * Returns the original image. */ public String toString() { return new String(m_image); } private void executeArray(ArrayList array, Connection conn) throws SQLException { m_logger.entering("org.postgresql.pljava.management.SQLDeploymentDescriptor", "executeArray"); Session session = SessionManager.current(); int top = array.size(); for(int idx = 0; idx < top; ++idx) { String cmd = (String)array.get(idx); m_logger.finer(cmd); session.executeAsSessionUser(conn, cmd); } m_logger.exiting("org.postgresql.pljava.management.SQLDeploymentDescriptor", "executeArray"); } private ParseException parseError(String msg) { return new ParseException(msg, m_position); } private void readDescriptor() throws ParseException { m_logger.entering("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readDescriptor"); if(!"SQLACTIONS".equals(this.readIdentifier())) throw this.parseError("Excpected keyword 'SQLActions'"); this.readToken('['); this.readToken(']'); this.readToken('='); this.readToken('{'); for(;;) { readActionGroup(); if(readToken("},") == '}') { // Only whitespace allowed now // int c = this.skipWhite(); if(c >= 0) throw this.parseError( "Extraneous characters at end of descriptor"); m_logger.exiting("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readDescriptor"); return; } } } private void readActionGroup() throws ParseException { m_logger.entering("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readActionGroup"); this.readToken('"'); if(!"BEGIN".equals(this.readIdentifier())) throw this.parseError("Excpected keyword 'BEGIN'"); ArrayList commands; String actionType = this.readIdentifier(); if("INSTALL".equals(actionType)) commands = m_installCommands; else if("REMOVE".equals(actionType)) commands = m_removeCommands; else throw this.parseError("Excpected keyword 'INSTALL' or 'REMOVE'"); for(;;) { String cmd = this.readCommand(); // Check if the cmd is in the form: // // ::= // BEGIN ... END // // If it is, and if the implementor name corresponds to the one // defined for this deployment, then extract the SQL token stream. // int top = cmd.length(); if(top >= 15 && "BEGIN ".equalsIgnoreCase(cmd.substring(0, 6)) && Character.isJavaIdentifierStart(cmd.charAt(6))) { int pos; for(pos = 7; pos < top; ++pos) if(!Character.isJavaIdentifierPart(cmd.charAt(pos))) break; if(cmd.charAt(pos) != ' ') throw this.parseError( "Expected whitespace after "); String implementorName = cmd.substring(6, pos); int iLen = implementorName.length(); int endNamePos = top - iLen; int endPos = endNamePos - 4; if(!implementorName.equalsIgnoreCase(cmd.substring(endNamePos)) || !"END ".equalsIgnoreCase(cmd.substring(endPos, endNamePos))) throw this.parseError( "Implementor block must end with END "); if(implementorName.equalsIgnoreCase(m_implementorName)) cmd = cmd.substring(pos+1, endPos); else // Block is not intended for this implementor. // cmd = null; } if(cmd != null) commands.add(cmd.trim()); // Check if we have END INSTALL or END REMOVE // int savePos = m_position; try { String tmp = this.readIdentifier(); if("END".equals(tmp)) { tmp = this.readIdentifier(); if(actionType.equals(tmp)) break; } m_position = savePos; } catch(ParseException e) { m_position = savePos; } } this.readToken('"'); m_logger.exiting("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readActionGroup"); } private String readCommand() throws ParseException { m_logger.entering("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readCommand"); int startQuotePos = -1; int inQuote = 0; int c = this.skipWhite(); m_buffer.setLength(0); while(c != -1) { switch(c) { case '\\': m_buffer.append((char)c); c = this.read(); if(c != -1) { m_buffer.append((char)c); c = this.read(); } break; case '"': case '\'': if(inQuote == 0) { startQuotePos = m_position; inQuote = c; } else if(inQuote == c) { startQuotePos = -1; inQuote = 0; } m_buffer.append((char)c); c = this.read(); break; case ';': if(inQuote == 0) { String cmd = m_buffer.toString(); m_logger.exiting("org.postgresql.pljava.management.SQLDeploymentDescriptor", "readCommand", cmd); return cmd; } m_buffer.append((char)c); c = this.read(); break; default: if(inQuote == 0 && Character.isWhitespace((char)c)) { // Change multiple whitespace into one singe space. // m_buffer.append(' '); c = this.skipWhite(); } else { m_buffer.append((char)c); c = this.read(); } } } if(inQuote != 0) throw this.parseError("Untermintated " + (char)inQuote + " starting at position " + startQuotePos); throw this.parseError("Unexpected EOF. Expecting ';' to end command"); } private int skipWhite() throws ParseException { int c; for(;;) { c = this.read(); if(c >= 0 && Character.isWhitespace((char)c)) continue; if(c == '/') { switch(this.peek()) { // "//" starts a line comment. Skip until end of line. // case '/': this.skip(); for(;;) { c = this.read(); switch(c) { case '\n': case '\r': case -1: break; default: continue; } break; } continue; // "/*" starts a line comment. Skip until "*/" // case '*': this.skip(); for(;;) { c = this.read(); switch(c) { case -1: throw this.parseError( "Unexpected EOF when expecting end of multi line comment"); case '*': if(this.peek() == '/') { this.skip(); break; } continue; default: continue; } break; } continue; } } break; } return c; } private String readIdentifier() throws ParseException { int c = this.skipWhite(); if(c < 0) throw this.parseError("Unexpected EOF when expecting start of identifier"); char ch = (char)c; if(!Character.isJavaIdentifierStart(ch)) throw this.parseError( "Syntax error at '" + ch + "', expected identifier"); m_buffer.setLength(0); m_buffer.append(ch); for(;;) { c = this.peek(); if(c < 0) break; ch = (char)c; if(Character.isJavaIdentifierPart(ch)) { m_buffer.append(ch); this.skip(); continue; } break; } return m_buffer.toString().toUpperCase(); } private char readToken(String tokens) throws ParseException { int c = this.skipWhite(); if(c < 0) throw this.parseError("Unexpected EOF when expecting one of \"" + tokens + '"'); char ch = (char)c; if(tokens.indexOf(ch) < 0) throw this.parseError( "Syntax error at '" + ch + "', expected one of '" + tokens + "'"); return ch; } private char readToken(char token) throws ParseException { int c = this.skipWhite(); if(c < 0) throw this.parseError("Unexpected EOF when expecting token '" + token + '\''); char ch = (char)c; if(ch != token) throw this.parseError( "Syntax error at '" + ch + "', expected '" + token + "'"); return ch; } private int peek() { return (m_position >= m_image.length) ? -1 : m_image[m_position]; } private void skip() { m_position++; } private int read() { int pos = m_position++; return (pos >= m_image.length) ? -1 : m_image[pos]; } }pljava-1.4.3/src/java/pljava/org/postgresql/pljava/management/Commands.java0000644000014500000120000007507711634451403026116 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.management; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLData; import java.sql.SQLException; import java.sql.Statement; import java.text.ParseException; import java.util.ArrayList; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.logging.Logger; import org.postgresql.pljava.internal.AclId; import org.postgresql.pljava.internal.Backend; import org.postgresql.pljava.internal.Oid; import org.postgresql.pljava.jdbc.SQLUtils; import org.postgresql.pljava.sqlj.Loader; /** * This methods of this class are implementations of SQLJ commands. *

SQLJ functions

*

install_jar

* The install_jar command loads a jar file from a location appointed by an URL * or a binary image that constitutes the contents of a jar file into the SQLJ * jar repository. It is an error if a jar with the given name already exists in * the repository. *

Usage 1

*
SELECT sqlj.install_jar(<jar_url>, <jar_name>, <deploy>); *
*

Parameters

*
* * * * * * * * * * * * *
jar_urlThe URL that denotes the location of the jar that should be loaded
jar_nameThis is the name by which this jar can be referenced once it has been * loaded
deployTrue if the jar should be deployed according to a {@link * org.postgresql.pljava.management.SQLDeploymentDescriptor deployment * descriptor}, false otherwise
*

Usage 2

*
SELECT sqlj.install_jar(<jar_image>, <jar_name>, <deploy>); *
*

Parameters

*
* * * * * * * * * * * * *
jar_imageThe byte array that constitutes the contents of the jar that should be * loaded
jar_nameThis is the name by which this jar can be referenced once it has been * loaded
deployTrue if the jar should be deployed according to a {@link * org.postgresql.pljava.management.SQLDeploymentDescriptor deployment * descriptor}, false otherwise
*

replace_jar

* The replace_jar will replace a loaded jar with another jar. Use this command * to update already loaded files. It's an error if the jar is not found. *

Usage 1

*
SELECT sqlj.replace_jar(<jar_url>, <jar_name>, <redeploy>); *
*

Parameters

*
* * * * * * * * * * * * *
jar_urlThe URL that denotes the location of the jar that should be loaded
jar_nameThe name of the jar to be replaced
redeployTrue if the old and new jar should be undeployed and deployed according * to their respective {@link * org.postgresql.pljava.management.SQLDeploymentDescriptor deployment * descriptors}, false otherwise
*

Usage 2

*
SELECT sqlj.replace_jar(<jar_image>, <jar_name>, <redeploy>); *
*

Parameters

*
* * * * * * * * * * * * *
jar_imageThe byte array that constitutes the contents of the jar that should be * loaded
jar_nameThe name of the jar to be replaced
redeployTrue if the old and new jar should be undeployed and deployed according * to their respective {@link * org.postgresql.pljava.management.SQLDeploymentDescriptor deployment * descriptors}, false otherwise
*

remove_jar

* The remove_jar will drop the jar from the jar repository. Any classpath that * references this jar will be updated accordingly. It's an error if the jar is * not found. *

Usage

*
SELECT sqlj.remove_jar(<jar_name>, <undeploy>); *
*

Parameters

*
* * * * * * * * *
jar_nameThe name of the jar to be removed
undeployTrue if the jar should be undeployed according to its {@link * org.postgresql.pljava.management.SQLDeploymentDescriptor deployment * descriptor}, false otherwise
*

get_classpath

* The get_classpath will return the classpath that has been defined for the * given schema or NULL if the schema has no classpath. It's an error if the * given schema does not exist. *

Usage

*
SELECT sqlj.get_classpath(<schema>); *
*

Parameters

*
* * * * *
schemaThe name of the schema
*

set_classpath

* The set_classpath will define a classpath for the given schema. A classpath * consists of a colon separated list of jar names. It's an error if the given * schema does not exist or if one or more jar names references non existent * jars. *

Usage

*
SELECT sqlj.set_classpath(<schema>, <classpath>); *
*

Parameters

*
* * * * * * * * *
schemaThe name of the schema
classpathThe colon separated list of jar names
*

add_type_mapping

* The add_type_mapping defines the mapping between an SQL type and a Java * class. *

Usage

*
SELECT sqlj.add_type_mapping(<sqlTypeName>, <className>); *
*

Parameters

*
* * * * * * * * *
sqlTypeNameThe name of the SQL type. The name can be qualified with a * schema (namespace). If the schema is omitted, it will be resolved according * to the current setting of the search_path.
classNameThe name of the class. The class must be found in the classpath in * effect for the current schema
*

drop_type_mapping

* The drop_type_mapping removes the mapping between an SQL type and a Java * class. *

Usage

*
SELECT sqlj.drop_type_mapping(<sqlTypeName>); *
*

Parameters

*
* * * * *
sqlTypeNameThe name of the SQL type. The name can be qualified with a * schema (namespace). If the schema is omitted, it will be resolved according * to the current setting of the search_path.
* * @author Thomas Hallgren */ public class Commands { private final static Logger s_logger = Logger.getLogger(Commands.class .getName()); /** * Reads the jar found at the specified URL and stores the entries in the * jar_entry table. * * @param jarId The id used for the foreign key to the jar_repository table * @param urlStream The URL * @throws SQLException */ public static void addClassImages(int jarId, InputStream urlStream) throws SQLException { PreparedStatement stmt = null; PreparedStatement descIdStmt = null; ResultSet rs = null; try { int deployImageId = -1; byte[] buf = new byte[1024]; ByteArrayOutputStream img = new ByteArrayOutputStream(); stmt = SQLUtils .getDefaultConnection() .prepareStatement( "INSERT INTO sqlj.jar_entry(entryName, jarId, entryImage) VALUES(?, ?, ?)"); JarInputStream jis = new JarInputStream(urlStream); Manifest manifest = jis.getManifest(); if(manifest != null) { ByteArrayOutputStream out = new ByteArrayOutputStream(); manifest.write(out); PreparedStatement us = SQLUtils .getDefaultConnection() .prepareStatement( "UPDATE sqlj.jar_repository SET jarManifest = ? WHERE jarId = ?"); try { us.setString(1, new String(out.toByteArray(), "UTF8")); us.setInt(2, jarId); if(us.executeUpdate() != 1) throw new SQLException( "Jar repository update did not update 1 row"); } catch(UnsupportedEncodingException e) { // Excuse me? No UTF8 encoding? // throw new SQLException("JVM does not support UTF8!!"); } finally { SQLUtils.close(us); } } for(;;) { JarEntry je = jis.getNextJarEntry(); if(je == null) break; if(je.isDirectory()) continue; String entryName = je.getName(); Attributes attrs = je.getAttributes(); boolean isDepDescr = false; if(attrs != null) { isDepDescr = "true".equalsIgnoreCase(attrs .getValue("SQLJDeploymentDescriptor")); if(isDepDescr && deployImageId >= 0) throw new SQLException( "Only one SQLJDeploymentDescriptor allowed"); } int nBytes; img.reset(); while((nBytes = jis.read(buf)) > 0) img.write(buf, 0, nBytes); jis.closeEntry(); stmt.setString(1, entryName); stmt.setInt(2, jarId); stmt.setBytes(3, img.toByteArray()); if(stmt.executeUpdate() != 1) throw new SQLException( "Jar entry insert did not insert 1 row"); if(isDepDescr) { descIdStmt = SQLUtils.getDefaultConnection() .prepareStatement( "SELECT entryId FROM sqlj.jar_entry" + " WHERE jarId = ? AND entryName = ?"); descIdStmt.setInt(1, jarId); descIdStmt.setString(2, entryName); rs = descIdStmt.executeQuery(); if(!rs.next()) throw new SQLException( "Failed to refecth row in sqlj.jar_entry"); deployImageId = rs.getInt(1); } } if(deployImageId >= 0) { stmt.close(); stmt = SQLUtils .getDefaultConnection() .prepareStatement( "UPDATE sqlj.jar_repository SET deploymentDesc = ? WHERE jarId = ?"); stmt.setInt(1, deployImageId); stmt.setInt(2, jarId); if(stmt.executeUpdate() != 1) throw new SQLException( "Jar repository update did not insert 1 row"); } } catch(IOException e) { throw new SQLException("I/O exception reading jar file: " + e.getMessage()); } finally { SQLUtils.close(rs); SQLUtils.close(descIdStmt); SQLUtils.close(stmt); } } /** * Defines the mapping between an SQL type and a Java class. * * @param sqlTypeName The name of the SQL type. The name can be * qualified with a schema (namespace). If the schema is omitted, * it will be resolved according to the current setting of the * search_path. * @param javaClassName The name of the class. The class must be found in * the classpath in effect for the current schema * @throws SQLException */ public static void addTypeMapping(String sqlTypeName, String javaClassName) throws SQLException { PreparedStatement stmt = null; try { ClassLoader loader = Loader.getCurrentLoader(); Class cls = loader.loadClass(javaClassName); if(!SQLData.class.isAssignableFrom(cls)) throw new SQLException("Class " + javaClassName + " does not implement java.sql.SQLData"); sqlTypeName = getFullSqlName(sqlTypeName); stmt = SQLUtils .getDefaultConnection() .prepareStatement( "INSERT INTO sqlj.typemap_entry(javaName, sqlName) VALUES(?,?)"); stmt.setString(1, javaClassName); stmt.setString(2, sqlTypeName); stmt.executeUpdate(); } catch(ClassNotFoundException e) { throw new SQLException("No such class: " + javaClassName); } finally { SQLUtils.close(stmt); } Loader.clearSchemaLoaders(); } /** * Drops the mapping between an SQL type and a Java class. * * @param sqlTypeName The name of the SQL type. The name can be * qualified with a schema (namespace). If the schema is omitted, * it will be resolved according to the current setting of the * search_path. * @throws SQLException */ public static void dropTypeMapping(String sqlTypeName) throws SQLException { PreparedStatement stmt = null; try { sqlTypeName = getFullSqlName(sqlTypeName); stmt = SQLUtils.getDefaultConnection().prepareStatement( "DELETE FROM sqlj.typemap_entry WHERE sqlName = ?"); stmt.setString(1, sqlTypeName); stmt.executeUpdate(); } finally { SQLUtils.close(stmt); } Loader.clearSchemaLoaders(); } /** * Return the classpath that has been defined for the schema named * schemaName This method is exposed in SQL as * sqlj.get_classpath(VARCHAR). * * @param schemaName Name of the schema for which this path is valid. * @return The defined classpath or null if this schema has * no classpath. * @throws SQLException */ public static String getClassPath(String schemaName) throws SQLException { ResultSet rs = null; PreparedStatement stmt = null; try { if(schemaName == null || schemaName.length() == 0) schemaName = "public"; else schemaName = schemaName.toLowerCase(); stmt = SQLUtils .getDefaultConnection() .prepareStatement( "SELECT r.jarName" + " FROM sqlj.jar_repository r INNER JOIN sqlj.classpath_entry c ON r.jarId = c.jarId" + " WHERE c.schemaName = ? ORDER BY c.ordinal"); stmt.setString(1, schemaName); rs = stmt.executeQuery(); StringBuffer buf = null; while(rs.next()) { if(buf == null) buf = new StringBuffer(); else buf.append(':'); buf.append(rs.getString(1)); } return (buf == null) ? null : buf.toString(); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } public static String getCurrentSchema() throws SQLException { Statement stmt = SQLUtils.getDefaultConnection().createStatement(); ResultSet rs = null; try { rs = stmt.executeQuery("SELECT current_schema()"); if(!rs.next()) throw new SQLException("Unable to obtain current schema"); return rs.getString(1); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } /** * Installs a new Jar in the database jar repository under name * jarName. Once installed classpaths can be defined that * refrences this jar. This method is exposed in SQL as * sqlj.install_jar(BYTEA, VARCHAR, BOOLEAN). * * @param image The byte array that constitutes the jar content. * @param jarName The name by which the system will refer to this jar. * @param deploy If set, execute install commands found in the deployment * descriptor. * @throws SQLException if the jarName contains characters * that are invalid or if the named jar already exists in the * system. * @see #setClassPath */ public static void installJar(byte[] image, String jarName, boolean deploy) throws SQLException { installJar("streamed byte image", jarName, deploy, image); } /** * Installs a new Jar in the database jar repository under name * jarName. Once installed classpaths can be defined that * refrences this jar. This method is exposed in SQL as * sqlj.install_jar(VARCHAR, VARCHAR, BOOLEAN). * * @param urlString The location of the jar that will be installed. * @param jarName The name by which the system will refer to this jar. * @param deploy If set, execute install commands found in the deployment * descriptor. * @throws SQLException if the jarName contains characters * that are invalid or if the named jar already exists in the * system. * @see #setClassPath */ public static void installJar(String urlString, String jarName, boolean deploy) throws SQLException { installJar(urlString, jarName, deploy, null); } /** * Removes the jar named jarName from the database jar * repository. Class path entries that references this jar will also be * removed (just the entry, not the whole path). This method is exposed in * SQL as sqlj.remove_jar(VARCHAR, BOOLEAN). * * @param jarName The name by which the system referes this jar. * @param undeploy If set, execute remove commands found in the deployment * descriptor of the jar. * @throws SQLException if the named jar cannot be found in the repository. */ public static void removeJar(String jarName, boolean undeploy) throws SQLException { assertJarName(jarName); AclId[] ownerRet = new AclId[1]; int jarId = getJarId(jarName, ownerRet); if(jarId < 0) throw new SQLException("No Jar named '" + jarName + "' is known to the system"); AclId user = AclId.getSessionUser(); if(!(user.isSuperuser() || user.equals(ownerRet[0]))) throw new SecurityException( "Only super user or owner can remove a jar"); if(undeploy) deployRemove(jarId, jarName); PreparedStatement stmt = SQLUtils .getDefaultConnection() .prepareStatement("DELETE FROM sqlj.jar_repository WHERE jarId = ?"); try { stmt.setInt(1, jarId); if(stmt.executeUpdate() != 1) throw new SQLException( "Jar repository update did not update 1 row"); } finally { SQLUtils.close(stmt); } Loader.clearSchemaLoaders(); } /** * Replaces the image of jar named jarName in the database * jar repository. This method is exposed in SQL as * sqlj.replace_jar(BYTEA, VARCHAR, BOOLEAN). * * @param jarImage The byte array that constitutes the jar content. * @param jarName The name by which the system referes this jar. * @param redeploy If set, execute remove commands found in the deployment * descriptor of the old jar and install commands found in the * deployment descriptor of the new jar. * @throws SQLException if the named jar cannot be found in the repository. */ public static void replaceJar(byte[] jarImage, String jarName, boolean redeploy) throws SQLException { replaceJar("streamed byte image", jarName, redeploy, jarImage); } /** * Replaces the image of jar named jarName in the database * jar repository. This method is exposed in SQL as * sqlj.replace_jar(VARCHAR, VARCHAR, BOOLEAN). * * @param urlString The location of the jar that will be installed. * @param jarName The name by which the system referes this jar. * @param redeploy If set, execute remove commands found in the deployment * descriptor of the old jar and install commands found in the * deployment descriptor of the new jar. * @throws SQLException if the named jar cannot be found in the repository. */ public static void replaceJar(String urlString, String jarName, boolean redeploy) throws SQLException { replaceJar(urlString, jarName, redeploy, null); } /** * Define the class path to use for Java functions, triggers, and procedures * that are created in the schema named schemaName This * method is exposed in SQL as * sqlj.set_classpath(VARCHAR, VARCHAR). * * @param schemaName Name of the schema for which this path is valid. * @param path Colon separated list of names. Each name must denote the name * of a jar that is present in the jar repository. * @throws SQLException If no schema can be found with the givene name, or * if one or several names of the path denotes a nonexistant jar * file. */ public static void setClassPath(String schemaName, String path) throws SQLException { if(schemaName == null || schemaName.length() == 0) schemaName = "public"; if("public".equals(schemaName)) { if(!AclId.getSessionUser().isSuperuser()) throw new SQLException( "Permission denied. Only a super user can set the classpath of the public schema"); } else { schemaName = schemaName.toLowerCase(); Oid schemaId = getSchemaId(schemaName); if(schemaId == null) throw new SQLException("No such schema: " + schemaName); if(!AclId.getSessionUser().hasSchemaCreatePermission(schemaId)) throw new SQLException( "Permission denied. User must have create permission on the target schema in order to set the classpath"); } PreparedStatement stmt; ArrayList entries = null; if(path != null && path.length() > 0) { // Collect and verify that all entries in the path represents a // valid jar // entries = new ArrayList(); stmt = SQLUtils.getDefaultConnection().prepareStatement( "SELECT jarId FROM sqlj.jar_repository WHERE jarName = ?"); try { for(;;) { int colon = path.indexOf(':'); String jarName; if(colon >= 0) { jarName = path.substring(0, colon); path = path.substring(colon + 1); } else jarName = path; int jarId = getJarId(stmt, jarName, null); if(jarId < 0) throw new SQLException("No such jar: " + jarName); entries.add(new Integer(jarId)); if(colon < 0) break; } } finally { SQLUtils.close(stmt); } } // Delete the old classpath // stmt = SQLUtils.getDefaultConnection().prepareStatement( "DELETE FROM sqlj.classpath_entry WHERE schemaName = ?"); try { stmt.setString(1, schemaName); stmt.executeUpdate(); } finally { SQLUtils.close(stmt); } if(entries != null) { // Insert the new path. // stmt = SQLUtils .getDefaultConnection() .prepareStatement( "INSERT INTO sqlj.classpath_entry(schemaName, ordinal, jarId) VALUES(?, ?, ?)"); try { int top = entries.size(); for(int idx = 0; idx < top; ++idx) { int jarId = ((Integer)entries.get(idx)).intValue(); stmt.setString(1, schemaName); stmt.setInt(2, idx + 1); stmt.setInt(3, jarId); stmt.executeUpdate(); } } finally { SQLUtils.close(stmt); } } Loader.clearSchemaLoaders(); } private static boolean assertInPath(String jarName, String[] originalSchemaAndPath) throws SQLException { String currentSchema = getCurrentSchema(); String currentClasspath = getClassPath(currentSchema); originalSchemaAndPath[0] = currentSchema; originalSchemaAndPath[1] = currentClasspath; if(currentClasspath == null) { setClassPath(currentSchema, jarName); return true; } String[] elems = currentClasspath.split(":"); int idx = elems.length; boolean found = false; while(--idx >= 0) if(elems[idx].equals(jarName)) { found = true; break; } if(found) return false; setClassPath(currentSchema, jarName + ':' + currentClasspath); return true; } /** * Throws an exception if the given name cannot be used as the name of a * jar. * * @param jarName The naem to check. * @throws IOException */ private static void assertJarName(String jarName) throws SQLException { if(jarName != null) { int len = jarName.length(); if(len > 0 && Character.isJavaIdentifierStart(jarName.charAt(0))) { int idx = 1; for(; idx < len; ++idx) if(!Character.isJavaIdentifierPart(jarName.charAt(idx))) break; if(idx == len) return; } } throw new SQLException("The jar name '" + jarName + "' is not a valid name"); } private static void deployInstall(int jarId, String jarName) throws SQLException { SQLDeploymentDescriptor depDesc = getDeploymentDescriptor(jarId); if(depDesc == null) return; String[] originalSchemaAndPath = new String[2]; boolean classpathChanged = assertInPath(jarName, originalSchemaAndPath); depDesc.install(SQLUtils.getDefaultConnection()); if(classpathChanged) setClassPath(originalSchemaAndPath[0], originalSchemaAndPath[1]); } private static void deployRemove(int jarId, String jarName) throws SQLException { SQLDeploymentDescriptor depDesc = getDeploymentDescriptor(jarId); if(depDesc == null) return; String[] originalSchemaAndPath = new String[2]; boolean classpathChanged = assertInPath(jarName, originalSchemaAndPath); depDesc.remove(SQLUtils.getDefaultConnection()); if(classpathChanged) setClassPath(originalSchemaAndPath[0], originalSchemaAndPath[1]); } private static SQLDeploymentDescriptor getDeploymentDescriptor(int jarId) throws SQLException { ResultSet rs = null; PreparedStatement stmt = SQLUtils.getDefaultConnection() .prepareStatement( "SELECT e.entryImage" + " FROM sqlj.jar_repository r INNER JOIN sqlj.jar_entry e" + " ON r.deploymentDesc = e.entryId" + " WHERE r.jarId = ?"); try { stmt.setInt(1, jarId); rs = stmt.executeQuery(); if(!rs.next()) return null; byte[] bytes = rs.getBytes(1); if(bytes.length == 0) return null; // Accodring to the SQLJ standard, this entry must be // UTF8 encoded. // return new SQLDeploymentDescriptor(new String(bytes, "UTF8"), "postgresql"); } catch(UnsupportedEncodingException e) { // Excuse me? No UTF8 encoding? // throw new SQLException("JVM does not support UTF8!!"); } catch(ParseException e) { throw new SQLException(e.getMessage() + " at " + e.getErrorOffset()); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } private static String getFullSqlName(String sqlTypeName) throws SQLException { Oid typeId = Oid.forTypeName(sqlTypeName); s_logger.info("Type id = " + typeId.toString()); ResultSet rs = null; PreparedStatement stmt = SQLUtils.getDefaultConnection() .prepareStatement( "SELECT n.nspname, t.typname FROM pg_type t, pg_namespace n" + " WHERE t.oid = ? AND n.oid = t.typnamespace"); try { stmt.setObject(1, typeId); rs = stmt.executeQuery(); if(!rs.next()) throw new SQLException("Unable to obtain type info for " + typeId); return rs.getString(1) + '.' + rs.getString(2); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } private static int getJarId(PreparedStatement stmt, String jarName, AclId[] ownerRet) throws SQLException { stmt.setString(1, jarName); ResultSet rs = stmt.executeQuery(); try { if(!rs.next()) return -1; int id = rs.getInt(1); if(ownerRet != null) { String ownerName = rs.getString(2); ownerRet[0] = AclId.fromName(ownerName); } return id; } finally { SQLUtils.close(rs); } } /** * Returns the primary key identifier for the given Jar. * * @param conn The connection to use for the query. * @param jarName The name of the jar. * @return The primary key value of the given jar or -1 if no * such jar is found. * @throws SQLException */ private static int getJarId(String jarName, AclId[] ownerRet) throws SQLException { PreparedStatement stmt = SQLUtils .getDefaultConnection() .prepareStatement( "SELECT jarId, jarOwner FROM sqlj.jar_repository WHERE jarName = ?"); try { return getJarId(stmt, jarName, ownerRet); } finally { SQLUtils.close(stmt); } } /** * Returns the Oid for the given Schema. * * @param conn The connection to use for the query. * @param schemaName The name of the schema. * @return The Oid of the given schema or null if no such * schema is found. * @throws SQLException */ private static Oid getSchemaId(String schemaName) throws SQLException { ResultSet rs = null; PreparedStatement stmt = SQLUtils.getDefaultConnection() .prepareStatement("SELECT oid FROM pg_namespace WHERE nspname = ?"); try { stmt.setString(1, schemaName); rs = stmt.executeQuery(); if(!rs.next()) return null; return (Oid)rs.getObject(1); } finally { SQLUtils.close(rs); SQLUtils.close(stmt); } } private static void installJar(String urlString, String jarName, boolean deploy, byte[] image) throws SQLException { assertJarName(jarName); if(getJarId(jarName, null) >= 0) throw new SQLException("A jar named '" + jarName + "' already exists"); PreparedStatement stmt = SQLUtils .getDefaultConnection() .prepareStatement( "INSERT INTO sqlj.jar_repository(jarName, jarOrigin, jarOwner) VALUES(?, ?, ?)"); try { stmt.setString(1, jarName); stmt.setString(2, urlString); stmt.setString(3, AclId.getSessionUser().getName()); if(stmt.executeUpdate() != 1) throw new SQLException( "Jar repository insert did not insert 1 row"); } finally { SQLUtils.close(stmt); } AclId[] ownerRet = new AclId[1]; int jarId = getJarId(jarName, ownerRet); if(jarId < 0) throw new SQLException("Unable to obtain id of '" + jarName + "'"); if(image == null) Backend.addClassImages(jarId, urlString); else { InputStream imageStream = new ByteArrayInputStream(image); addClassImages(jarId, imageStream); } Loader.clearSchemaLoaders(); if(deploy) deployInstall(jarId, jarName); } private static void replaceJar(String urlString, String jarName, boolean redeploy, byte[] image) throws SQLException { AclId[] ownerRet = new AclId[1]; int jarId = getJarId(jarName, ownerRet); if(jarId < 0) throw new SQLException("No Jar named '" + jarName + "' is known to the system"); AclId user = AclId.getSessionUser(); if(!(user.isSuperuser() || user.equals(ownerRet[0]))) throw new SecurityException( "Only super user or owner can replace a jar"); if(redeploy) deployRemove(jarId, jarName); PreparedStatement stmt = SQLUtils .getDefaultConnection() .prepareStatement( "UPDATE sqlj.jar_repository SET jarOrigin = ?, jarOwner = ?, jarManifest = NULL, deploymentDesc = NULL WHERE jarId = ?"); try { stmt.setString(1, urlString); stmt.setString(2, user.getName()); stmt.setInt(3, jarId); if(stmt.executeUpdate() != 1) throw new SQLException( "Jar repository update did not update 1 row"); } finally { SQLUtils.close(stmt); } stmt = SQLUtils.getDefaultConnection().prepareStatement( "DELETE FROM sqlj.jar_entry WHERE jarId = ?"); try { stmt.setInt(1, jarId); stmt.executeUpdate(); } finally { SQLUtils.close(stmt); } if(image == null) Backend.addClassImages(jarId, urlString); else { InputStream imageStream = new ByteArrayInputStream(image); addClassImages(jarId, imageStream); } Loader.clearSchemaLoaders(); if(redeploy) deployInstall(jarId, jarName); } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/ResultSetProvider.java0000644000014500000120000000264511634451403025675 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.ResultSet; import java.sql.SQLException; /** * An implementation of this interface is returned from functions and procedures * that are declared to return SET OF a complex type. Functions that * return SET OF a simple type should simply return an * {@link java.util.Iterator Iterator}. * @author Thomas Hallgren */ public interface ResultSetProvider { /** * This method is called once for each row that should be returned from * a procedure that returns a set of rows. The receiver * is a {@link org.postgresql.pljava.jdbc.SingleRowWriter SingleRowWriter} * writer instance that is used for capturing the data for the row. * @param receiver Receiver of values for the given row. * @param currentRow Row number. First call will have row number 0. * @return true if a new row was provided, false * if not (end of data). * @throws SQLException */ boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException; /** * Called after the last row has returned or when the query evaluator decides * that it does not need any more rows. */ void close() throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/ResultSetHandle.java0000644000014500000120000000225111634451403025267 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.ResultSet; import java.sql.SQLException; /** * An implementation of this interface is returned from functions and procedures * that are declared to return SET OF a complex type in the form * of a {@link java.sql.ResultSet}. The primary motivation for this interface is * that an implementation that returns a ResultSet must be able to close the * connection and statement when no more rows are requested. * @author Thomas Hallgren */ public interface ResultSetHandle { /** * An implementation of this method will probably execute a query * and return the result of that query. * @return The ResultSet that represents the rows to be returned. * @throws SQLException */ ResultSet getResultSet() throws SQLException; /** * Called after the last row has returned or when the query evaluator decides * that it does not need any more rows. */ void close() throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/SessionManager.java0000644000014500000120000000253611634451403025145 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.SQLException; /** * The SessionManager makes the current {@link Session} available to the * caller. * @author Thomas Hallgren */ public class SessionManager { private static Method s_getSession; /** * Returns the current session. */ public static Session current() throws SQLException { try { if(s_getSession == null) { String sp = System.getProperty( "org.postgresql.pljava.sessionprovider", "org.postgresql.pljava.internal.Backend"); Class spc = Class.forName(sp); s_getSession = spc.getMethod("getSession", null); } return (Session)s_getSession.invoke(null, null); } catch (RuntimeException e) { throw e; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if(t instanceof SQLException) throw (SQLException)t; if(t instanceof RuntimeException) throw (RuntimeException)t; throw new SQLException(t.getMessage()); } catch (Exception e) { throw new SQLException(e.getMessage()); } } } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/ObjectPool.java0000644000014500000120000000170311634451403024262 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.SQLException; public interface ObjectPool { /** * Obtain a pooled object. A new instance is created if needed. The pooled * object is removed from the pool and activated. * * @return A new object or an object found in the pool. */ PooledObject activateInstance() throws SQLException; /** * Call the {@link PooledObject#passivate()} method and return the object * to the pool. * @param instance The instance to passivate. */ void passivateInstance(PooledObject instance) throws SQLException; /** * Call the {@link PooledObject#remove()} method and evict the object * from the pool. */ void removeInstance(PooledObject instance) throws SQLException; } pljava-1.4.3/src/java/pljava/org/postgresql/pljava/Session.java0000644000014500000120000000722211634451403023647 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava; import java.sql.Connection; import java.sql.SQLException; /** * A Session maintains transaction coordinated in-memory data. The data * added since the last commit will be lost on a transaction rollback, i.e. * the Session state is synchronized with the transaction. * * Please note that if nested objects (such as lists and maps) are stored * in the session, changes internal to those objects are not subject to * the session semantics since the session is unaware of them. * * @author Thomas Hallgren */ public interface Session { /** * Adds the specified listener to the list of listeners that will * receive savepoint events. This method does nothing if the listener * was already added. * @param listener The listener to be added. */ void addSavepointListener(SavepointListener listener); /** * Adds the specified listener to the list of listeners that will * receive transactional events. This method does nothing if the listener * was already added. * @param listener The listener to be added. */ void addTransactionListener(TransactionListener listener); /** * Obtain an attribute from the current session. * @param attributeName The name of the attribute * @return The value of the attribute */ Object getAttribute(String attributeName); /** * Return an object pool for the given class. The class must implement * the interface {@link PooledObject}. * @param cls * @return An object pool that pools object of the given class. */ ObjectPool getObjectPool(Class cls); /** * Return the name of the effective user. If the currently * executing funciton is declared with SECURITY DEFINER, * then this method returns the name of the user that defined * the function, otherwise, this method will return the same * as {@link #getSessionUserName()}. */ String getUserName(); /** * Return the name of the user that owns the current session. */ String getSessionUserName(); /** * Execute a statement as a session user rather then the effective * user. This is useful when functions declared using * SECURITY DEFINER wants to give up the definer * rights. * @param conn The connection used for the execution * @param statement The statement to execute * @throws SQLException if something goes wrong when executing. * @see java.sql.Statement#execute(java.lang.String) */ void executeAsSessionUser(Connection conn, String statement) throws SQLException; /** * Remove an attribute previously stored in the session. If * no attribute is found, nothing happens. * @param attributeName The name of the attribute. */ void removeAttribute(String attributeName); /** * Removes the specified listener from the list of listeners that will * receive savepoint events. This method does nothing unless the listener is * found. * @param listener The listener to be removed. */ void removeSavepointListener(SavepointListener listener); /** * Removes the specified listener from the list of listeners that will * receive transactional events. This method does nothing unless the listener is * found. * @param listener The listener to be removed. */ void removeTransactionListener(TransactionListener listener); /** * Set an attribute to a value in the current session. * @param attributeName * @param value */ void setAttribute(String attributeName, Object value); } pljava-1.4.3/src/java/pljava/Makefile0000644000014500000120000000520211634451403016546 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- NAME := pljava JAVADOCTITLE := 'PL/Java v$(PLJAVA_MAJOR_VER).$(PLJAVA_MINOR_VER) API Specification' include $(MODULEROOT)/Makefile.global SRCDIR_USC := $(subst .,^,$(SRCDIR)) mkclsrc = $(subst ^,.,$(subst .,/,$(1:%=$(SRCDIR_USC)/%^java))) JAVAH := javah INTPKG := org.postgresql.pljava.internal JDBCPKG := org.postgresql.pljava.jdbc JNI_CLASSES := \ $(INTPKG).Backend \ $(INTPKG).SPI \ $(INTPKG).AclId \ $(INTPKG).ErrorData \ $(INTPKG).Oid \ $(INTPKG).ExecutionPlan \ $(INTPKG).JavaWrapper \ $(INTPKG).LargeObject \ $(INTPKG).PgSavepoint \ $(INTPKG).Portal \ $(INTPKG).Relation \ $(INTPKG).Session \ $(INTPKG).SubXactListener \ $(INTPKG).TriggerData \ $(INTPKG).Tuple \ $(INTPKG).TupleDesc \ $(INTPKG).XactListener \ $(JDBCPKG).Invocation \ $(JDBCPKG).SingleRowReader \ $(JDBCPKG).SQLInputFromChunk \ $(JDBCPKG).SQLOutputToChunk \ $(JDBCPKG).SQLInputFromTuple \ $(JDBCPKG).SQLOutputToTuple JNISRCS := $(call mkclsrc,$(JNI_CLASSES)) ifdef USE_GCJ # We include both the pljava_jar.o and the pjlava.jar here although # pljava doesn't need the latter. Most java compilers will need it # in order to compile triggers and functions later. # JNIHDRS = $(patsubst %,$(JNIDIR)/%.h,$(subst .,_,$(JNI_CLASSES))) mkcname = $(subst _,.,$(1:$(JNIDIR)/%.h=%)) all: $(OBJDIR)/$(NAME)_jar.o $(JARFILE) $(JNIDIR)/.timestamp $(OBJDIR)/$(NAME)_jar.o: .timestamp @-mkdir -p $(@D) @echo $(GCJ) -c -fpic -fjni -o $@ '' @$(GCJ) -c -fpic -fjni -o $@ $(SRCS) # gcjh fails to clear its function name cache between files (see # gcc bugzilla #17575) so we must do each file separately to # avoid name ambiguities that causes names to be generated with # parameter type info. # $(JNIHDRS): $(JNISRCS) @-mkdir -p $(@D) @gcjh -jni --classpath=. -d $(@D) $(call mkcname,$@) # gcjh generated headers don't define final constants (see gcc # bugzilla #16843) so we must copy our own version of java.sql.Types. # $(JNIDIR)/.timestamp: $(JNIHDRS) @-mkdir -p $(@D) @cp $(PROJDIR)/fixes/gcj/java_sql_Types.h $(@D) @touch $@ else all: $(JARFILE) $(JNIDIR)/.timestamp $(JNIDIR)/.timestamp: $(JNISRCS) @-mkdir -p $(@D) @echo javah -classpath . -d $(@D) '' @javah -classpath . -d $(@D) $(JNI_CLASSES) java.sql.Types @touch $@ endif $(JARFILE): .timestamp $(JAR) cf $@ . pljava-1.4.3/src/java/test/0000755000014500000120000000000011634451404014612 5ustar johannstaffpljava-1.4.3/src/java/test/build.xml0000644000014500000120000000066611634451404016443 0ustar johannstaff pljava-1.4.3/src/java/test/org/0000755000014500000120000000000011634451404015401 5ustar johannstaffpljava-1.4.3/src/java/test/org/postgresql/0000755000014500000120000000000011634451404017604 5ustar johannstaffpljava-1.4.3/src/java/test/org/postgresql/pljava/0000755000014500000120000000000011634451404021061 5ustar johannstaffpljava-1.4.3/src/java/test/org/postgresql/pljava/test/0000755000014500000120000000000011634451404022040 5ustar johannstaffpljava-1.4.3/src/java/test/org/postgresql/pljava/test/Environment.java0000644000014500000120000000636511634451404025221 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Thomas Hallgren */ public class Environment { private static final Pattern s_envPattern = Pattern.compile("^(\\w+)=(.*)$"); private static final boolean s_isWindows; private final TreeMap m_env; static { Pattern osPattern = Pattern.compile("^windows\\W", Pattern.CASE_INSENSITIVE); s_isWindows = osPattern.matcher(System.getProperty("os.name")).lookingAt(); } private static String getEnvCommand() { return s_isWindows ? "cmd /C set" : "sh -c env"; } public static boolean isWindows() { return s_isWindows; } public Environment() throws IOException { String line; TreeMap env = new TreeMap(); m_env = env; Process proc = Runtime.getRuntime().exec(getEnvCommand()); BufferedReader lr = new BufferedReader(new InputStreamReader(proc.getInputStream())); while((line = lr.readLine()) != null) { Matcher matcher = s_envPattern.matcher(line); if(matcher.matches()) { String key = matcher.group(1); String val = matcher.group(2); if(s_isWindows) env.put(key.toLowerCase(), new String[] { key, val } ); else env.put(key, val); } } } public String get(String key) { if(s_isWindows) { String[] entry = (String[])m_env.get(key.toLowerCase()); return (entry == null) ? null : entry[1]; } return (String)m_env.get(key); } public void put(String key, String val) { if(s_isWindows) { String lowKey = key.toLowerCase(); String[] entry = (String[])m_env.get(lowKey); if(entry == null) m_env.put(lowKey, new String[] { key, val }); else entry[1] = val; } else m_env.put(key, val); } public String[] asArray() { StringBuffer bld = new StringBuffer(); ArrayList envArr = new ArrayList(); Iterator itor = m_env.entrySet().iterator(); while(itor.hasNext()) { Map.Entry entry = (Map.Entry)itor.next(); if(s_isWindows) { String[] kv = (String[])entry.getValue(); bld.append(kv[0]); bld.append('='); bld.append(kv[1]); } else { bld.append(entry.getKey()); bld.append('='); bld.append(entry.getValue()); } envArr.add(bld.toString()); bld.setLength(0); } return (String[])envArr.toArray(new String[envArr.size()]); } public String toString() { StringBuffer bld = new StringBuffer(); String newLine = System.getProperty("line.separator"); Iterator itor = m_env.entrySet().iterator(); while(itor.hasNext()) { Map.Entry entry = (Map.Entry)itor.next(); if(s_isWindows) { String[] kv = (String[])entry.getValue(); bld.append(kv[0]); bld.append(" = "); bld.append(kv[1]); bld.append(newLine); } else { bld.append(entry.getKey()); bld.append(" = "); bld.append(entry.getValue()); bld.append(newLine); } } return bld.toString(); } } pljava-1.4.3/src/java/test/org/postgresql/pljava/test/Path.java0000644000014500000120000000302411634451404023576 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.io.File; import java.util.ArrayList; /** * PL/Java Test harness * * @author Thomas Hallgren */ public class Path { public final ArrayList m_path; public Path(String path) { m_path = new ArrayList(); if(path != null) { char pathSep = File.pathSeparatorChar; int sep = path.indexOf(pathSep); while(sep >= 0) { if(sep > 0) this.addLast(new File(path.substring(0, sep))); path = path.substring(sep + 1); sep = path.indexOf(pathSep); } if(path.length() > 0) this.addLast(new File(path)); } } public void addFirst(File dir) { int pos = m_path.indexOf(dir); if(pos >= 0) { if(pos == 0) return; m_path.remove(pos); } m_path.add(0, dir); } public void addLast(File dir) { int pos = m_path.indexOf(dir); if(pos >= 0) { if(pos == m_path.size() - 1) return; m_path.remove(pos); } m_path.add(dir); } public String toString() { int top = m_path.size(); if(top == 0) return ""; String first = m_path.get(0).toString(); if(top == 1) return first; StringBuffer bld = new StringBuffer(); char pathSep = File.pathSeparatorChar; bld.append(first); for(int idx = 1; idx < top; ++idx) { bld.append(pathSep); bld.append(m_path.get(idx)); } return bld.toString(); } } pljava-1.4.3/src/java/test/org/postgresql/pljava/test/TableBuilder.java0000644000014500000120000000417711634451404025252 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TableBuilder { private final Connection m_conn; public static void main(String[] args) { try { TableBuilder t = new TableBuilder(); t.createTable(); t.createJavaFunction(); t.doSelects(); } catch(Exception e) { e.printStackTrace(); } } public TableBuilder() throws Exception { Class.forName("org.postgresql.Driver"); m_conn = DriverManager.getConnection("jdbc:postgresql://localhost/thhal", "thhal", null); } public void createTable() throws Exception { Statement stmt = m_conn.createStatement(); try { stmt.executeUpdate("drop table xtable"); } catch(SQLException e) {} stmt.executeUpdate("create table xtable (cl int4, tstove varchar)"); PreparedStatement insert = m_conn.prepareStatement("INSERT INTO xtable(cl, tstove) VALUES (?, ?)"); for(int idx = 0; idx < 100000; ++idx) { insert.setInt(1, idx); insert.setString(2, "xyz_" + (idx % 100)); insert.executeUpdate(); } insert.close(); m_conn.commit(); } public void createJavaFunction() throws Exception { Statement stmt = m_conn.createStatement(); stmt.executeUpdate("CREATE OR REPLACE FUNCTION getstovename(character varying, character varying) RETURNS text" + " AS $$java.lang.System.getProperty$$" + " LANGUAGE java"); } public void doSelects() throws Exception { Statement stmt = m_conn.createStatement(); int cnt = 0; ResultSet rs = stmt.executeQuery("SELECT getstovename('my.non.property', xtable.tstove) FROM xtable"); while(rs.next()) { String tstove = rs.getString(1); if(!tstove.startsWith("xyz_")) throw new Exception("Tstove was " + tstove); ++cnt; } rs.close(); System.out.println("Succesfully read " + cnt + " rows"); } } pljava-1.4.3/src/java/test/org/postgresql/pljava/test/Tester.java0000644000014500000120000006017511634451404024162 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.sql.Timestamp; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.regex.Pattern; import java.io.PrintStream; /** * Some fairly crude tests. All tests are confided to the schema * "javatest" * * @author Thomas Hallgren */ public class Tester { private static final int CMD_AMBIGUOUS = -2; private static final int CMD_UNKNOWN = -1; private static final int CMD_USER = 0; private static final int CMD_PASSWORD = 1; private static final int CMD_DATABASE = 2; private static final int CMD_HOSTNAME = 3; private static final int CMD_PORT = 4; private static final int CMD_DEBUG = 5; private static final int CMD_EXCLUDE = 6; private final Connection m_connection; private static final ArrayList s_commands = new ArrayList(); static { s_commands.add(CMD_USER, "user"); s_commands.add(CMD_PASSWORD, "password"); s_commands.add(CMD_DATABASE, "database"); s_commands.add(CMD_HOSTNAME, "host"); s_commands.add(CMD_PORT, "port"); s_commands.add(CMD_DEBUG, "debug"); s_commands.add(CMD_EXCLUDE, "exclude"); } private static final int getCommand(String arg) { int top = s_commands.size(); int candidateCmd = CMD_UNKNOWN; for(int idx = 0; idx < top; ++idx) { if(((String)s_commands.get(idx)).startsWith(arg)) { if(candidateCmd != CMD_UNKNOWN) return CMD_AMBIGUOUS; candidateCmd = idx; } } return candidateCmd; } public static void printUsage() { PrintStream out = System.err; out.println("usage: java org.postgresql.pljava.test.Tester"); out.println(" [ -host ] # default is localhost"); out.println(" [ -port ] # default is blank"); out .println(" [ -database ] # default is name of current user"); out .println(" [ -user ] # default is name of current user"); out.println(" [ -password ] # default is no password"); out .println(" [ -exclude excludePattern ] # exclude tests matching pattern"); out .println(" [ -debug ] # wait for debugger to attach to backend"); } public static void main(String[] argv) { String driverClass = "org.postgresql.Driver"; String hostName = "localhost"; String portNumber = null; String userName = System.getProperty("user.name", "postgres"); String database = userName; String subsystem = "postgresql"; String password = null; String exclude = null; boolean debug = false; int top = argv.length; for(int idx = 0; idx < top; ++idx) { String arg = argv[idx]; if(arg.length() < 2) { printUsage(); return; } if(arg.charAt(0) == '-') { int optCmd = getCommand(arg.substring(1)); switch(optCmd) { case CMD_DEBUG: debug = true; break; case CMD_USER: if(++idx < top) { userName = argv[idx]; if(userName.length() > 0 && userName.charAt(0) != '-') break; } printUsage(); return; case CMD_PASSWORD: if(++idx < top) { password = argv[idx]; if(password.length() > 0 && password.charAt(0) != '-') break; } printUsage(); return; case CMD_DATABASE: if(++idx < top) { database = argv[idx]; if(database.length() > 0 && database.charAt(0) != '-') break; } printUsage(); return; case CMD_HOSTNAME: if(++idx < top) { hostName = argv[idx]; if(hostName.length() > 0 && hostName.charAt(0) != '-') break; } printUsage(); return; case CMD_EXCLUDE: if(++idx < top) { exclude = argv[idx]; if(exclude.length() > 0 && exclude.charAt(0) != '-') break; } printUsage(); return; case CMD_PORT: if(++idx < top) { portNumber = argv[idx]; if(portNumber.length() > 0 && portNumber.charAt(0) != '-') break; } printUsage(); return; default: printUsage(); return; } } } try { Class.forName(driverClass); StringBuffer cc = new StringBuffer(); cc.append("jdbc:"); cc.append(subsystem); cc.append("://"); cc.append(hostName); if(portNumber != null) { cc.append(':'); cc.append(portNumber); } cc.append('/'); cc.append(database); Connection c = DriverManager.getConnection(cc.toString(), userName, password); Tester t = new Tester(c); if(debug) { System.out.println("Attach debugger to backend"); Thread.sleep(30000); System.out.println("continuing"); } for(int idx = 0; idx < 10; ++idx) { Pattern p = (exclude == null) ? null : Pattern.compile(exclude, Pattern.CASE_INSENSITIVE); if(p == null || !p.matcher("parameters").matches()) t.testParameters(); if(p == null || !p.matcher("usernametrigger").matches()) t.testInsertUsernameTrigger(); if(p == null || !p.matcher("moddatetimetrigger").matches()) t.testModdatetimeTrigger(); if(p == null || !p.matcher("spiactions").matches()) t.testSPIActions(); if(p == null || !p.matcher("tuplereturn").matches()) t.testTupleReturn(); if(p == null || !p.matcher("setreturn").matches()) t.testSetReturn(); if(p == null || !p.matcher("callincall").matches()) t.testCallInCall(); if(p == null || !p.matcher("currentdir").matches()) t.testCurrentDir(); if(p == null || !p.matcher("usingproperties").matches()) t.testUsingProperties(); if(p == null || !p.matcher("usingscalarproperties").matches()) t.testUsingScalarProperties(); if(p == null || !p.matcher("usingresultsetproperties").matches()) t.testUsingResultSetProperties(); if(p == null || !p.matcher("savepointsanity").matches()) t.testSavepointSanity(); if(p == null || !p.matcher("transactionrecovery").matches()) t.testTransactionRecovery(); if(p == null || !p.matcher("trustedsecurity").matches()) t.testTrustedSecurity(); if(p == null || !p.matcher("binarycolumns").matches()) t.testBinaryColumns(); if(p == null || !p.matcher("databasemetadata").matches()) t.testDatabaseMetaData(); if(p == null || !p.matcher("resultset").matches()) t.testResultSet(); if(p == null || !p.matcher("complexscalar").matches()) t.testComplexScalar(); if(p == null || !p.matcher("complextuple").matches()) t.testComplexTuple(); } t.close(); } catch(Exception e) { e.printStackTrace(); } } public Tester(Connection conn) throws SQLException { Statement stmt = conn.createStatement(); stmt.execute("SET search_path TO javatest,public"); stmt.close(); m_connection = conn; } public void close() throws SQLException { m_connection.close(); } public void testParameters() throws SQLException { this.testTimestamp(); this.testInt(); } public void testSPIActions() throws SQLException { System.out.println("*** testSPIActions()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM employees1"); stmt.execute("DELETE FROM employees2"); stmt.execute("INSERT INTO employees1 VALUES(" + "1, 'Calvin Forrester', 10000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "2, 'Edwin Archer', 20000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "3, 'Rebecka Shawn', 30000)"); stmt.execute("INSERT INTO employees1 VALUES(" + "4, 'Priscilla Johnson', 25000)"); stmt.execute("SELECT transferPeople(20000)"); ResultSet rs = stmt.executeQuery("SELECT * FROM employees2"); while(rs.next()) { int id = rs.getInt(1); String name = rs.getString(2); int salary = rs.getInt(3); System.out.println("Id = \"" + id + "\", name = \"" + name + "\", salary = \"" + salary + "\""); } rs.close(); } public void testTupleReturn() throws SQLException { System.out.println("*** testTupleReturn()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT tupleReturnToString(tupleReturnExample(1, 5))"); while(rs.next()) { String str = rs.getString(1); System.out.println(str); } rs.close(); rs = stmt.executeQuery("SELECT tupleReturnToString(tupleReturnExample2(1, NULL))"); while(rs.next()) { String str = rs.getString(1); System.out.println(str); } rs.close(); } public void testSetReturn() throws SQLException { System.out.println("*** testSetReturn()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT base, incbase, ctime FROM setReturnExample(1, 5)"); while(rs.next()) { int base = rs.getInt(1); int incbase = rs.getInt(2); Timestamp ctime = rs.getTimestamp(3); System.out.println("Base = \"" + base + "\", incbase = \"" + incbase + "\", ctime = \"" + ctime + "\""); } rs.close(); } public void testUsingProperties() throws SQLException { System.out.println("*** testUsingProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT name, value FROM propertyExample()"); while(rs.next()) { String name = rs.getString(1); String value = rs.getString(2); System.out.println("Name = \"" + name + "\", value = \"" + value + "\""); } rs.close(); } public void testUsingResultSetProperties() throws SQLException { System.out.println("*** testUsingResultSetProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT name, value FROM resultSetPropertyExample()"); while(rs.next()) { String name = rs.getString(1); String value = rs.getString(2); System.out.println("Name = \"" + name + "\", value = \"" + value + "\""); } rs.close(); } public void testUsingScalarProperties() throws SQLException { System.out.println("*** testUsingScalarProperties()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT scalarPropertyExample()"); while(rs.next()) System.out.println(rs.getString(1)); rs.close(); } public void testBinaryColumns() throws SQLException { System.out.println("*** testBinaryColumns()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM binaryColumnTest()"); while(rs.next()) { byte[] b1 = rs.getBytes(1); byte[] b2 = rs.getBytes(2); if(!Arrays.equals(b1, b2)) throw new SQLException("binary columns differ"); } rs.close(); } public void testCallInCall() throws SQLException { System.out.println("*** testCallInCall()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT maxFromSetReturnExample(10, 8)"); while(rs.next()) { int max = rs.getInt(1); System.out.println("Max = \"" + max + "\""); } } public void testModdatetimeTrigger() throws SQLException { System.out.println("*** testModdatetimeTrigger()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM mdt"); stmt.execute("INSERT INTO mdt VALUES (1, 'first')"); stmt.execute("INSERT INTO mdt VALUES (2, 'second')"); stmt.execute("INSERT INTO mdt VALUES (3, 'third')"); ResultSet rs = stmt.executeQuery("SELECT * FROM mdt"); while(rs.next()) { int id = rs.getInt(1); String idesc = rs.getString(2); Timestamp moddate = rs.getTimestamp(3); System.out.println("Id = \"" + id + "\", idesc = \"" + idesc + "\", moddate = \"" + moddate + "\""); } rs.close(); stmt.execute("UPDATE mdt SET id = 4 WHERE id = 1"); stmt.execute("UPDATE mdt SET id = 5 WHERE id = 2"); stmt.execute("UPDATE mdt SET id = 6 WHERE id = 3"); rs = stmt.executeQuery("SELECT * FROM mdt"); while(rs.next()) { int id = rs.getInt(1); String idesc = rs.getString(2); Timestamp moddate = rs.getTimestamp(3); System.out.println("Id = \"" + id + "\", idesc = \"" + idesc + "\", moddate = \"" + moddate + "\""); } rs.close(); stmt.close(); } public void testInsertUsernameTrigger() throws SQLException { System.out.println("*** testInsertUsernameTrigger()"); Statement stmt = m_connection.createStatement(); stmt.execute("DELETE FROM username_test"); stmt.execute("INSERT INTO username_test VALUES ('nothing', 'thomas')"); stmt.execute("INSERT INTO username_test VALUES ('null', null)"); stmt.execute("INSERT INTO username_test VALUES ('empty string', '')"); stmt.execute("INSERT INTO username_test VALUES ('space', ' ')"); stmt.execute("INSERT INTO username_test VALUES ('tab', ' ')"); stmt.execute("INSERT INTO username_test VALUES ('name', 'name')"); ResultSet rs = stmt.executeQuery("SELECT * FROM username_test"); while(rs.next()) { String name = rs.getString(1); String username = rs.getString(2); System.out.println("Name = \"" + name + "\", username = \"" + username + "\""); } rs.close(); // Test the update trigger as well as leaking statements // // System.out.println("Doing 800 updates with double triggers that leak // statements"); // System.out.println("(but not leak memory). One trigger executes SQL // that reenters PL/Java"); // for(int idx = 0; idx < 200; ++idx) // { // stmt.execute("UPDATE username_test SET username = 'Kalle Kula' WHERE // username = 'name'"); // stmt.execute("UPDATE username_test SET username = 'Pelle Kanin' WHERE // username = 'thomas'"); // stmt.execute("UPDATE username_test SET username = 'thomas' WHERE // username = 'Kalle Kula'"); // stmt.execute("UPDATE username_test SET username = 'name' WHERE // username = 'Pelle Kanin'"); // } stmt.close(); } public void testTimestamp() throws SQLException { System.out.println("*** testTimestamp()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_getTimestamp(), java_getTimestamptz()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Timestamp = " + rs.getTimestamp(1) + ", Timestamptz = " + rs.getTimestamp(2)); rs.close(); // Test parameter overloading. Set log_min_messages (in posgresql.conf) // to INFO or higher and watch the result. // stmt.execute("SELECT print(current_date)"); stmt.execute("SELECT print(current_time)"); stmt.execute("SELECT print(current_timestamp)"); stmt.close(); } public void testInt() throws SQLException { System.out.println("*** testInt()"); /* * Test parameter override from int primitive to java.lang.Integer Test * return value override (stipulated by the Java method rather than in * the function declaration. Seems to be that way according to the * SQL-2003 spec). Test function call within function call and function * that returns an object rather than a primitive to reflect null * values. */ Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_addOne(java_addOne(54)), nullOnEven(1), nullOnEven(2)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else { System.out.println("54 + 2 = " + rs.getInt(1)); int n = rs.getInt(2); System.out.println("nullOnEven(1) = " + (rs.wasNull() ? "null" : Integer.toString(n))); n = rs.getInt(3); System.out.println("nullOnEven(2) = " + (rs.wasNull() ? "null" : Integer.toString(n))); } rs.close(); stmt.close(); } public void testComplexScalar() throws SQLException { System.out.println("*** testComplexScalar()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT logcomplex('(34.56,12.78)'::complex)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println(rs.getString(1)); rs.close(); stmt.close(); } public void testComplexTuple() throws SQLException { System.out.println("*** testComplexTuple()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT logcomplex((34.56,12.78)::complextuple)"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println(rs.getString(1)); rs.close(); stmt.close(); } public void testCurrentDir() throws SQLException { System.out.println("*** testCurrentDir()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt .executeQuery("SELECT java_getSystemProperty('user.dir')"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Server directory = " + rs.getString(1)); rs.close(); stmt.close(); } public void testSavepointSanity() throws SQLException { System.out.println("*** testSavepointSanity()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT testSavepointSanity()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Savepoint sanity = " + rs.getInt(1)); rs.close(); stmt.close(); } public void testTransactionRecovery() throws SQLException { System.out.println("*** testTransactionRecovery()"); Statement stmt = m_connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT testTransactionRecovery()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Transaction recovery = " + rs.getInt(1)); rs.close(); stmt.close(); } public void testTrustedSecurity() throws SQLException { System.out.println("*** testTrustedSecurity()"); Statement stmt = m_connection.createStatement(); ResultSet rs = null; tryCreateTempFile(true); boolean funcCreated = false; try { rs = stmt.executeQuery("SHOW IS_SUPERUSER"); if(!(rs.next() && rs.getString(1).equals("on"))) { System.out .println("Tester is not superuser so tests on untrusted language cannot be performed"); return; } // Try the same java method again, this time using language javaU // System.out.println("*** testUntrustedSecurity()"); stmt .execute("CREATE OR REPLACE FUNCTION javatest.create_temp_file_untrusted()" + " RETURNS varchar" + " AS 'org.postgresql.pljava.example.Security.createTempFile'" + " LANGUAGE javaU"); tryCreateTempFile(false); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } if(funcCreated) stmt .execute("DROP FUNCTION javatest.create_temp_file_untrusted()"); stmt.close(); } } private void tryCreateTempFile(boolean trusted) throws SQLException { Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { if(trusted) rs = stmt.executeQuery("SELECT create_temp_file_trusted()"); else rs = stmt.executeQuery("SELECT create_temp_file_untrusted()"); if(!rs.next()) System.out.println("Unable to position ResultSet"); else System.out.println("Name of created temp file = " + rs.getString(1)); if(trusted) throw new RuntimeException( "ERROR: Tempfile creation succeded although language is trusted!"); } catch(SQLException e) { if(!trusted) throw e; System.out .println("OK, creation of temp file was *unsuccessful* as it should be"); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } stmt.close(); } } public void testDatabaseMetaData() throws SQLException { Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { System.out.println("*** DatabaseMetaData 'String' functions:"); rs = stmt .executeQuery("SELECT * FROM javatest.getMetaDataStrings()"); while(rs.next()) { String methodName = rs.getString(1); String methodResult = rs.getString(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); System.out.println("*** DatabaseMetaData 'boolean' functions:"); rs = stmt .executeQuery("SELECT * FROM javatest.getMetaDataBooleans()"); while(rs.next()) { String methodName = rs.getString(1); boolean methodResult = rs.getBoolean(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); System.out.println("*** DatabaseMetaData 'int' functions:"); rs = stmt.executeQuery("SELECT * FROM javatest.getMetaDataInts()"); while(rs.next()) { String methodName = rs.getString(1); int methodResult = rs.getInt(2); System.out.println("Method = \"" + methodName + "\", result = \"" + methodResult + "\""); } rs.close(); executeMetaDataFunction(stmt, "getAttributes((String)null,\"javatest\",\"%\",\"%\")"); executeMetaDataFunction(stmt, "getBestRowIdentifier((String)null,\"sqlj\",\"jar_repository\",0,FALSE)"); executeMetaDataFunction(stmt, "getCatalogs()"); executeMetaDataFunction(stmt, "getColumnPrivileges((String)null,\"sqlj\",\"jar_repository\",\"jarid\")"); executeMetaDataFunction(stmt, "getColumns((String)null,\"sqlj\",\"jar_repository\",\"%\")"); executeMetaDataFunction( stmt, "getCrossReference((String)null,\"sqlj\",\"jar_repository\",(String)null,\"sqlj\",\"jar_entry\")"); executeMetaDataFunction(stmt, "getExportedKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getImportedKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getIndexInfo((String)null,\"sqlj\",\"jar_repository\",TRUE,FALSE)"); executeMetaDataFunction(stmt, "getPrimaryKeys((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getProcedureColumns((String)null,\"sqlj\",\"install_jar\",(String)null)"); executeMetaDataFunction(stmt, "getProcedures((String)null,\"sqlj\",\"%\")"); executeMetaDataFunction(stmt, "getSchemas()"); executeMetaDataFunction(stmt, "getSuperTables((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getSuperTypes((String)null,\"sqlj\",\"%\")"); executeMetaDataFunction(stmt, "getTablePrivileges((String)null,\"sqlj\",\"jar_repository\")"); executeMetaDataFunction(stmt, "getTables((String)null,\"sqlj\",\"jar%\",{\"TABLE\"})"); executeMetaDataFunction(stmt, "getTableTypes()"); executeMetaDataFunction(stmt, "getTypeInfo()"); executeMetaDataFunction(stmt, "getUDTs((String)null,\"sqlj\",\"%\",(int[])null)"); executeMetaDataFunction(stmt, "getVersionColumns((String)null,\"sqlj\",\"jar_repository\")"); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } private void executeMetaDataFunction(Statement stmt, String functionCall) throws SQLException { ResultSet rs = null; try { System.out.println("*** " + functionCall + ":"); rs = stmt .executeQuery("SELECT * FROM javatest.callMetaDataMethod('" + functionCall + "')"); while(rs.next()) { System.out.println(rs.getString(1)); } rs.close(); } catch(Exception e) { System.out.println(" Failed: " + e.getMessage()); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } public void testResultSet() throws SQLException { String sql; Statement stmt = m_connection.createStatement(); ResultSet rs = null; try { System.out.println("*** ResultSet test:"); sql = "SELECT * FROM javatest.executeSelect(" + "'select ''Foo'' as t_varchar, 1::integer as t_integer, " + "1.5::float as t_float, 23.67::decimal(8,2) as t_decimal, " + "''2005-06-01''::date as t_date, ''20:56''::time as t_time, " + "''2006-02-04 23:55:10''::timestamp as t_timestamp')"; rs = stmt.executeQuery(sql); System.out.println("SQL = " + sql); System.out.println("results:"); while(rs.next()) { System.out.println(rs.getString(1)); } rs.close(); } finally { if(rs != null) { try { rs.close(); } catch(SQLException e) { } rs = null; } } } } pljava-1.4.3/src/java/test/org/postgresql/pljava/test/TestPLJava.java0000644000014500000120000001161011634451404024657 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * PL/Java Test harness * * @author Thomas Hallgren */ public class TestPLJava { // private static final int CMD_AMBIGUOUS = -2; // private static final int CMD_UNKNOWN = -1; private static final int CMD_PGSQLHOME = 0; private static final int CMD_TESTDIR = 1; private static final int CMD_PORT = 2; private static final ArrayList s_commands = new ArrayList(); private final File m_pgsqlHome; private final File m_pljavaHome; private final File m_pljavaBin; private final File m_testHome; private final int m_majorVer; private final int m_minorVer; static { s_commands.add(CMD_PGSQLHOME, "pgsqlhome"); s_commands.add(CMD_TESTDIR, "testdir"); s_commands.add(CMD_PORT, "port"); } /* private static final int getCommand(String arg) { int top = s_commands.size(); int candidateCmd = CMD_UNKNOWN; for(int idx = 0; idx < top; ++idx) { if(((String)s_commands.get(idx)).startsWith(arg)) { if(candidateCmd != CMD_UNKNOWN) return CMD_AMBIGUOUS; candidateCmd = idx; } } return candidateCmd; } */ public static void printUsage() { PrintStream out = System.err; out.println("usage: java org.postgresql.pljava.test.TestPLJava"); out.println(" [ -port ] # default is 5432"); out.println(" [ -pgsqlhome ] # default is /usr/local/pgsql"); out.println(" [ -testdir ] # default is current directory"); out.println(" [ -printenv ] # print the env for the postmaster"); out.println(" [ -windows ] # If the server is on a Windows machine"); } class KillPostmaster extends Thread { } private int[] getPostgreSQLVersion() throws IOException { // Get the PostgreSQL version using pg_ctl // CommandReader pg_ctl = CommandReader.create( new String[] { "pg_ctl", "--version" }, this.getPostgresEnvironment().asArray()); String verLine = pg_ctl.readLine(); pg_ctl.close(); int exitVal = pg_ctl.getExitValue(); if(exitVal != 0) throw new IOException("pg_ctl exit value " + exitVal); Pattern verPattern = Pattern.compile("\\D+(\\d+)\\.(\\d+)\\D"); Matcher verMatcher = verPattern.matcher(verLine); if(!verMatcher.lookingAt()) throw new IOException("Unable to determine PostgreSQL version from " + verLine); return new int[] { Integer.parseInt(verMatcher.group(1)), Integer.parseInt(verMatcher.group(2)) }; } public TestPLJava(String pgsqlHome, String pljavaHome) throws IOException { m_pgsqlHome = new File(pgsqlHome); m_pljavaHome = new File(pljavaHome); m_testHome = new File(m_pljavaHome, "test"); m_pljavaBin = new File(new File(new File(m_pljavaHome, "bin"), "build"), "pljava"); int[] ver = this.getPostgreSQLVersion(); m_majorVer = ver[0]; m_minorVer = ver[1]; } class Postmaster extends Thread { Postmaster() { } public void run() { List args = new ArrayList(); args.add("postmaster"); args.add("-D"); args.add(new File(m_testHome, "db").getAbsolutePath()); if(m_minorVer < 5) { args.add("-c"); args.add("tcpip_socket=true"); } else { args.add("-c"); args.add("custom_variable_classes=pljava"); } args.add("-c"); args.add("log_min_messages=debug1"); args.add("-c"); args.add("dynamic_library_path=" + m_pljavaBin.getAbsolutePath()); Runtime rt = Runtime.getRuntime(); KillPostmaster killer = new KillPostmaster(); rt.addShutdownHook(killer); try { rt.exec((String[])args.toArray(new String[args.size()])); } catch(IOException e) { } rt.removeShutdownHook(killer); } } public Environment getPostgresEnvironment() throws IOException { Environment env = new Environment(); Path path = new Path(env.get("PATH")); File pgsqlBin = new File(m_pgsqlHome, "bin"); File pgsqlLib = new File(m_pgsqlHome, "lib"); if(Environment.isWindows()) path.addFirst(pgsqlLib); else { Path ldPath = new Path(env.get("LD_LIBRARY_PATH")); ldPath.addFirst(pgsqlLib); env.put("LD_LIBRARY_PATH", ldPath.toString()); } path.addFirst(pgsqlBin); env.put("PATH", path.toString()); return env; } public static void main(String[] args) { try { TestPLJava tpj = new TestPLJava("c:\\msys\\local\\pgsql", "c:\\"); System.out.println(tpj.getPostgresEnvironment()); } catch(Exception e) { e.printStackTrace(); } } public void initdb() { } public final int getMajorVer() { return m_majorVer; } public final int getMinorVer() { return m_minorVer; } } pljava-1.4.3/src/java/test/org/postgresql/pljava/test/CommandReader.java0000644000014500000120000000267511634451404025416 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; /** * @author Thomas Hallgren */ public class CommandReader extends BufferedReader { private final Process m_process; private BufferedReader m_errorReader; private CommandReader(Process proc, Reader rdr) { super(rdr); m_process = proc; } public static CommandReader create(String[] args, String[] env) throws IOException { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec(args, env); return new CommandReader(proc, new InputStreamReader(proc.getInputStream())); } public synchronized BufferedReader getErrorReader() { if(m_errorReader == null) m_errorReader = new BufferedReader(new InputStreamReader(m_process.getErrorStream())); return m_errorReader; } public void close() throws IOException { super.close(); if(m_errorReader != null) m_errorReader.close(); } public int getExitValue() throws IllegalThreadStateException { try { return m_process.exitValue(); } catch(IllegalThreadStateException e) { try { m_process.waitFor(); } catch(InterruptedException e2) {} return m_process.exitValue(); } } } pljava-1.4.3/src/java/test/Makefile0000644000014500000120000000103611634451404016252 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- NAME := test JAVADOCTITLE := 'PL/Java test API Specification' include $(MODULEROOT)/Makefile.global all: $(JARFILE) $(JARFILE): .timestamp $(JAR) cf $@ . pljava-1.4.3/src/java/deploy/0000755000014500000120000000000011634451404015127 5ustar johannstaffpljava-1.4.3/src/java/deploy/org/0000755000014500000120000000000011634451404015716 5ustar johannstaffpljava-1.4.3/src/java/deploy/org/postgresql/0000755000014500000120000000000011634451404020121 5ustar johannstaffpljava-1.4.3/src/java/deploy/org/postgresql/pljava/0000755000014500000120000000000011634451404021376 5ustar johannstaffpljava-1.4.3/src/java/deploy/org/postgresql/pljava/deploy/0000755000014500000120000000000011634451404022672 5ustar johannstaffpljava-1.4.3/src/java/deploy/org/postgresql/pljava/deploy/Deployer.java0000644000014500000120000003224711634451404025330 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.deploy; import java.io.PrintStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Savepoint; import java.sql.Statement; import java.sql.ResultSet; import java.util.ArrayList; /** * When running the deployer, you must use a classpath that can see the * deploy.jar found in the Pl/Java distribution and the postgresql.jar from the * PostgreSQL distribution. The former contains the code for the deployer * command and the second includes the PostgreSQL JDBC driver. You then run the * deployer with the command: *

*
* java -cp <your classpath> org.postgresql.pljava.deploy.Deployer [ options ] *
*

* It's recommended that create a shell script or a .bat script that does this * for you so that you don't have to do this over and over again. *

*

Deployer options

*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
OptionDescription
-installInstalls the Java language along with the sqlj procedures. The deployer * will fail if the language is installed already.
-reinstallReinstalls the Java language and the sqlj procedures. This will * effectively drop all jar files that have been loaded.
-removeDrops the Java language and the sqjl procedures and loaded jars
-user <user name>Name of user that connects to the database. Default is current user
-password <password>Password of user that connects to the database. Default is no password *
-database <database>The name of the database to connect to. Default is current user
-host <hostname>Name of the host. Default is "localhost"
-windowsUse this option if the host runs on a windows platform. Affects the * name used for the Pl/Java dynamic library
* * @author Thomas Hallgren */ public class Deployer { private static final int CMD_AMBIGUOUS = -2; private static final int CMD_UNKNOWN = -1; private static final int CMD_UNINSTALL = 0; private static final int CMD_INSTALL = 1; private static final int CMD_REINSTALL = 2; private static final int CMD_USER = 3; private static final int CMD_PASSWORD = 4; private static final int CMD_DATABASE = 5; private static final int CMD_HOSTNAME = 6; private static final int CMD_PORT = 7; private final Connection m_connection; private static final ArrayList s_commands = new ArrayList(); static { s_commands.add(CMD_UNINSTALL, "uninstall"); s_commands.add(CMD_INSTALL, "install"); s_commands.add(CMD_REINSTALL, "reinstall"); s_commands.add(CMD_USER, "user"); s_commands.add(CMD_PASSWORD, "password"); s_commands.add(CMD_DATABASE, "database"); s_commands.add(CMD_HOSTNAME, "host"); s_commands.add(CMD_PORT, "port"); } private static final int getCommand(String arg) { int top = s_commands.size(); int candidateCmd = CMD_UNKNOWN; for(int idx = 0; idx < top; ++idx) { if(((String)s_commands.get(idx)).startsWith(arg)) { if(candidateCmd != CMD_UNKNOWN) return CMD_AMBIGUOUS; candidateCmd = idx; } } return candidateCmd; } public static void printUsage() { PrintStream out = System.err; out.println("usage: java org.postgresql.pljava.deploy.Deployer"); out.println(" {-install | -uninstall | -reinstall}"); out.println(" [ -host ] # default is localhost"); out.println(" [ -port ] # default is blank"); out.println(" [ -database ] # default is name of current user"); out.println(" [ -user ] # default is name of current user"); out.println(" [ -password ] # default is no password"); } public static void main(String[] argv) { String driverClass = "org.postgresql.Driver"; String hostName = "localhost"; String userName = System.getProperty("user.name", "postgres"); String database = userName; String subsystem = "postgresql"; String password = null; String portNumber = null; int cmd = CMD_UNKNOWN; int top = argv.length; for(int idx = 0; idx < top; ++idx) { String arg = argv[idx]; if(arg.length() < 2) { printUsage(); return; } if(arg.charAt(0) == '-') { int optCmd = getCommand(arg.substring(1)); switch(optCmd) { case CMD_INSTALL: case CMD_UNINSTALL: case CMD_REINSTALL: if(cmd != CMD_UNKNOWN) { printUsage(); return; } cmd = optCmd; break; case CMD_USER: if(++idx < top) { userName = argv[idx]; if(userName.length() > 0 && userName.charAt(0) != '-') break; } printUsage(); return; case CMD_PASSWORD: if(++idx < top) { password = argv[idx]; if(password.length() > 0 && password.charAt(0) != '-') break; } printUsage(); return; case CMD_DATABASE: if(++idx < top) { database = argv[idx]; if(database.length() > 0 && database.charAt(0) != '-') break; } printUsage(); return; case CMD_HOSTNAME: if(++idx < top) { hostName = argv[idx]; if(hostName.length() > 0 && hostName.charAt(0) != '-') break; } printUsage(); return; case CMD_PORT: if(++idx < top) { portNumber = argv[idx]; if(portNumber.length() > 0 && portNumber.charAt(0) != '-') break; } printUsage(); return; default: printUsage(); return; } } } if(cmd == CMD_UNKNOWN) { printUsage(); return; } try { Class.forName(driverClass); StringBuffer cc = new StringBuffer(); cc.append("jdbc:"); cc.append(subsystem); cc.append("://"); cc.append(hostName); if(portNumber != null) { cc.append(':'); cc.append(portNumber); } cc.append('/'); cc.append(database); Connection c = DriverManager.getConnection( cc.toString(), userName, password); checkIfConnectedAsSuperuser(c); c.setAutoCommit(false); Deployer deployer = new Deployer(c); if(cmd == CMD_UNINSTALL || cmd == CMD_REINSTALL) { deployer.dropSQLJSchema(); } if(cmd == CMD_INSTALL || cmd == CMD_REINSTALL) { deployer.createSQLJSchema(); deployer.initJavaHandlers(); deployer.initializeSQLJSchema(); } c.commit(); c.close(); } catch(Exception e) { e.printStackTrace(); } } public Deployer(Connection c) { m_connection = c; } public static void checkIfConnectedAsSuperuser(Connection conn) throws SQLException { Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SHOW IS_SUPERUSER"); try { if(rs.next() && rs.getString(1).equals("on")) return; } finally { rs.close(); stmt.close(); } throw new SQLException( "You must be a superuser to deploy/undeploy pl/Java."); } public void dropSQLJSchema() throws SQLException { Statement stmt = m_connection.createStatement(); Savepoint p = null; try { if (m_connection.getMetaData().supportsSavepoints()) p = m_connection.setSavepoint(); stmt.execute("DROP LANGUAGE java CASCADE"); stmt.execute("DROP LANGUAGE javaU CASCADE"); } catch(SQLException e) { /* roll back to savepoint (if available) * or restart the transaction (if no savepoint is available) * & ignore the exception */ if (p != null) m_connection.rollback(p); else /* Assuming that the dropSQLJSchema is the * first method called in a transaction, * we can afford to restart the transaction. * * This solution is designed for PostgreSQL < 8 (no savepoints available) */ m_connection.rollback(); } finally { if (p != null) m_connection.releaseSavepoint(p); } stmt.execute("DROP SCHEMA sqlj CASCADE"); stmt.close(); } public void createSQLJSchema() throws SQLException { Statement stmt = m_connection.createStatement(); stmt.execute("CREATE SCHEMA sqlj"); stmt.execute("GRANT USAGE ON SCHEMA sqlj TO public"); stmt.close(); } public void initializeSQLJSchema() throws SQLException { Statement stmt = m_connection.createStatement(); stmt.execute( "CREATE TABLE sqlj.jar_repository(" + " jarId SERIAL PRIMARY KEY," + " jarName VARCHAR(100) UNIQUE NOT NULL," + " jarOrigin VARCHAR(500) NOT NULL," + " jarOwner NAME NOT NULL," + " jarManifest TEXT," + " deploymentDesc INT" + ")"); stmt.execute("GRANT SELECT ON sqlj.jar_repository TO public"); stmt.execute( "CREATE TABLE sqlj.jar_entry(" + " entryId SERIAL PRIMARY KEY," + " entryName VARCHAR(200) NOT NULL," + " jarId INT NOT NULL REFERENCES sqlj.jar_repository ON DELETE CASCADE," + " entryImage BYTEA NOT NULL," + " UNIQUE(jarId, entryName)" + ")"); stmt.execute("GRANT SELECT ON sqlj.jar_entry TO public"); stmt.execute( "ALTER TABLE sqlj.jar_repository" + " ADD FOREIGN KEY (deploymentDesc) REFERENCES sqlj.jar_entry ON DELETE SET NULL"); // Create the table maintaining the class path. // stmt.execute( "CREATE TABLE sqlj.classpath_entry(" + " schemaName VARCHAR(30) NOT NULL," + " ordinal INT2 NOT NULL," + // Ordinal in class path " jarId INT NOT NULL REFERENCES sqlj.jar_repository ON DELETE CASCADE," + " PRIMARY KEY(schemaName, ordinal)" + ")"); stmt.execute("GRANT SELECT ON sqlj.classpath_entry TO public"); // Create the table maintaining the SQL to Java type mappings // stmt.execute( "CREATE TABLE sqlj.typemap_entry(" + " mapId SERIAL PRIMARY KEY," + " javaName VARCHAR(200) NOT NULL," + " sqlName NAME NOT NULL" + ")"); stmt.execute("GRANT SELECT ON sqlj.typemap_entry TO public"); // These are the proposed SQL standard methods. // stmt.execute( "CREATE FUNCTION sqlj.install_jar(VARCHAR, VARCHAR, BOOLEAN) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.installJar'" + " LANGUAGE java SECURITY DEFINER"); stmt.execute( "CREATE FUNCTION sqlj.replace_jar(VARCHAR, VARCHAR, BOOLEAN) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.replaceJar'" + " LANGUAGE java SECURITY DEFINER"); stmt.execute( "CREATE FUNCTION sqlj.remove_jar(VARCHAR, BOOLEAN) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.removeJar'" + " LANGUAGE java SECURITY DEFINER"); // Not proposed, but very useful if you want to send the image over // your JDBC connection. // stmt.execute( "CREATE FUNCTION sqlj.install_jar(BYTEA, VARCHAR, BOOLEAN) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.installJar'" + " LANGUAGE java SECURITY DEFINER"); stmt.execute( "CREATE FUNCTION sqlj.replace_jar(BYTEA, VARCHAR, BOOLEAN) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.replaceJar'" + " LANGUAGE java SECURITY DEFINER"); // This function is not as proposed. It's more Java'ish. The proposal // using sqlj.alter_jar_path is in my opinion bloated and will not be // well received in the Java community. Luckily, the support is suggested // to be optional. // stmt.execute( "CREATE FUNCTION sqlj.set_classpath(VARCHAR, VARCHAR) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.setClassPath'" + " LANGUAGE java SECURITY DEFINER"); stmt.execute( "CREATE FUNCTION sqlj.get_classpath(VARCHAR) RETURNS VARCHAR" + " AS 'org.postgresql.pljava.management.Commands.getClassPath'" + " LANGUAGE java STABLE SECURITY DEFINER"); // The following functions are not included in the standard. Type mapping // is radically different in SQL 2003 and requires a lot of additions to // the PostgreSQL dialect. // stmt.execute( "CREATE FUNCTION sqlj.add_type_mapping(VARCHAR, VARCHAR) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.addTypeMapping'" + " LANGUAGE java SECURITY DEFINER"); stmt.execute( "CREATE FUNCTION sqlj.drop_type_mapping(VARCHAR) RETURNS void" + " AS 'org.postgresql.pljava.management.Commands.dropTypeMapping'" + " LANGUAGE java SECURITY DEFINER"); stmt.close(); } public void initJavaHandlers() throws SQLException { Statement stmt = m_connection.createStatement(); stmt.execute( "CREATE FUNCTION sqlj.java_call_handler()" + " RETURNS language_handler" + " AS 'pljava'" + " LANGUAGE C"); stmt.execute("CREATE TRUSTED LANGUAGE java HANDLER sqlj.java_call_handler"); stmt.execute( "CREATE FUNCTION sqlj.javau_call_handler()" + " RETURNS language_handler" + " AS 'pljava'" + " LANGUAGE C"); stmt.execute("CREATE LANGUAGE javaU HANDLER sqlj.javau_call_handler"); stmt.close(); } } pljava-1.4.3/src/java/deploy/Makefile0000644000014500000120000000111211634451404016562 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- NAME := deploy JAVADOCTITLE := 'PL/Java deployer API Specification' include $(MODULEROOT)/Makefile.global all: $(JARFILE) $(JARFILE): .timestamp $(JAR) cmf $(SRCDIR)/META-INF/manifest.txt $@ . pljava-1.4.3/src/java/deploy/META-INF/0000755000014500000120000000000011634451404016267 5ustar johannstaffpljava-1.4.3/src/java/deploy/META-INF/manifest.txt0000644000014500000120000000006211634451404020634 0ustar johannstaffMain-Class: org.postgresql.pljava.deploy.Deployer pljava-1.4.3/src/java/examples/0000755000014500000120000000000011634451404015451 5ustar johannstaffpljava-1.4.3/src/java/examples/deployment/0000755000014500000120000000000011634451404017631 5ustar johannstaffpljava-1.4.3/src/java/examples/deployment/examples.manifest0000644000014500000120000000012411634451404023174 0ustar johannstaffManifest-Version: 1.0 Name: deployment/examples.ddr SQLJDeploymentDescriptor: TRUE pljava-1.4.3/src/java/examples/deployment/examples.ddr0000644000014500000120000003304211634451404022144 0ustar johannstaffSQLActions[ ] = { "BEGIN INSTALL CREATE SCHEMA javatest; BEGIN PostgreSQL SET search_path TO javatest,public ENd postgreSQL; CREATE FUNCTION javatest.java_getTimestamp() RETURNS timestamp AS 'org.postgresql.pljava.example.Parameters.getTimestamp' LANGUAGE java; CREATE FUNCTION javatest.java_getTimestamptz() RETURNS timestamptz AS 'org.postgresql.pljava.example.Parameters.getTimestamp' LANGUAGE java; CREATE FUNCTION javatest.print(date) RETURNS void AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(timetz) RETURNS void AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(timestamptz) RETURNS void AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print("char") RETURNS "char" AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(bytea) RETURNS bytea AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int2) RETURNS int2 AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int2[]) RETURNS int2[] AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int4) RETURNS int4 AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int4[]) RETURNS int4[] AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int8) RETURNS int8 AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(int8[]) RETURNS int8[] AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(real) RETURNS real AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(real[]) RETURNS real[] AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(double precision) RETURNS double precision AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.print(double precision[]) RETURNS double precision[] AS 'org.postgresql.pljava.example.Parameters.print' LANGUAGE java; CREATE FUNCTION javatest.printObj(int[]) RETURNS int[] AS 'org.postgresql.pljava.example.Parameters.print(java.lang.Integer[])' LANGUAGE java; CREATE FUNCTION javatest.java_addOne(int) RETURNS int AS 'org.postgresql.pljava.example.Parameters.addOne(java.lang.Integer)' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.nullOnEven(int) RETURNS int AS 'org.postgresql.pljava.example.Parameters.nullOnEven' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.java_getSystemProperty(varchar) RETURNS varchar AS 'java.lang.System.getProperty' LANGUAGE java; /* This function should fail since file system access is * prohibited when the language is trusted. */ CREATE FUNCTION javatest.create_temp_file_trusted() RETURNS varchar AS 'org.postgresql.pljava.example.Security.createTempFile' LANGUAGE java; CREATE TABLE javatest.username_test ( name text, username text not null ) WITH OIDS; CREATE FUNCTION javatest.insert_username() RETURNS trigger AS 'org.postgresql.pljava.example.Triggers.insertUsername' LANGUAGE java; CREATE FUNCTION javatest.after_username_insert() RETURNS trigger AS 'org.postgresql.pljava.example.Triggers.afterUsernameInsert' LANGUAGE java; CREATE FUNCTION javatest.after_username_update() RETURNS trigger AS 'org.postgresql.pljava.example.Triggers.afterUsernameUpdate' LANGUAGE java; CREATE FUNCTION javatest.leak_statements() RETURNS trigger AS 'org.postgresql.pljava.example.Triggers.leakStatements' LANGUAGE java; CREATE TRIGGER insert_usernames BEFORE INSERT OR UPDATE ON username_test FOR EACH ROW EXECUTE PROCEDURE insert_username (username); CREATE TRIGGER after_insert_usernames AFTER INSERT ON username_test FOR EACH ROW EXECUTE PROCEDURE after_username_insert (username); CREATE TRIGGER after_username_updates AFTER UPDATE ON username_test FOR EACH ROW EXECUTE PROCEDURE after_username_update (username); CREATE TRIGGER username_leak BEFORE UPDATE ON username_test FOR EACH ROW EXECUTE PROCEDURE leak_statements(); CREATE TABLE javatest.mdt ( id int4, idesc text, moddate timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL ); CREATE FUNCTION javatest.moddatetime() RETURNS trigger AS 'org.postgresql.pljava.example.Triggers.moddatetime' LANGUAGE java; CREATE TRIGGER mdt_moddatetime BEFORE UPDATE ON mdt FOR EACH ROW EXECUTE PROCEDURE moddatetime (moddate); CREATE TABLE javatest.employees1 ( id int PRIMARY KEY, name varchar(200), salary int ); CREATE TABLE javatest.employees2 ( id int PRIMARY KEY, name varchar(200), salary int, transferDay date, transferTime time ); CREATE FUNCTION javatest.transferPeople(int) RETURNS int AS 'org.postgresql.pljava.example.SPIActions.transferPeopleWithSalary' LANGUAGE java; CREATE TYPE javatest._testSetReturn AS (base integer, incbase integer, ctime timestamptz); CREATE FUNCTION javatest.tupleReturnExample(int, int) RETURNS _testSetReturn AS 'org.postgresql.pljava.example.TupleReturn.tupleReturn' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.tupleReturnExample2(int, int) RETURNS _testSetReturn AS 'org.postgresql.pljava.example.TupleReturn.tupleReturn(java.lang.Integer, java.lang.Integer, java.sql.ResultSet)' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.tupleReturnToString(_testSetReturn) RETURNS VARCHAR AS 'org.postgresql.pljava.example.TupleReturn.makeString' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.setReturnExample(int, int) RETURNS SETOF javatest._testSetReturn AS 'org.postgresql.pljava.example.TupleReturn.setReturn' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.hugeResult(int) RETURNS SETOF javatest._testSetReturn AS 'org.postgresql.pljava.example.HugeResultSet.executeSelect' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.hugeNonImmutableResult(int) RETURNS SETOF javatest._testSetReturn AS 'org.postgresql.pljava.example.HugeResultSet.executeSelect' LANGUAGE java; CREATE FUNCTION javatest.maxFromSetReturnExample(int, int) RETURNS int AS 'org.postgresql.pljava.example.SPIActions.maxFromSetReturnExample' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.nestedStatements(int) RETURNS void AS 'org.postgresql.pljava.example.SPIActions.nestedStatements' LANGUAGE java; CREATE TYPE javatest._properties AS (name varchar(200), value varchar(200)); CREATE FUNCTION javatest.propertyExample() RETURNS SETOF javatest._properties AS 'org.postgresql.pljava.example.UsingProperties.getProperties' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.resultSetPropertyExample() RETURNS SETOF javatest._properties AS 'org.postgresql.pljava.example.UsingPropertiesAsResultSet.getProperties' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.scalarPropertyExample() RETURNS SETOF varchar AS 'org.postgresql.pljava.example.UsingPropertiesAsScalarSet.getProperties' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.randomInts(int) RETURNS SETOF int AS 'org.postgresql.pljava.example.RandomInts.createIterator' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.listSupers() RETURNS SETOF pg_user AS 'org.postgresql.pljava.example.Users.listSupers' LANGUAGE java; CREATE FUNCTION javatest.listNonSupers() RETURNS SETOF pg_user AS 'org.postgresql.pljava.example.Users.listNonSupers' LANGUAGE java; CREATE FUNCTION javatest.testSavepointSanity() RETURNS int AS 'org.postgresql.pljava.example.SPIActions.testSavepointSanity' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.testTransactionRecovery() RETURNS int AS 'org.postgresql.pljava.example.SPIActions.testTransactionRecovery' IMMUTABLE LANGUAGE java; CREATE FUNCTION javatest.getDateAsString() RETURNS varchar AS 'org.postgresql.pljava.example.SPIActions.getDateAsString' STABLE LANGUAGE java; CREATE FUNCTION javatest.getTimeAsString() RETURNS varchar AS 'org.postgresql.pljava.example.SPIActions.getTimeAsString' STABLE LANGUAGE java; CREATE FUNCTION javatest.logMessage(varchar, varchar) RETURNS void AS 'org.postgresql.pljava.example.LoggerTest.logMessage' IMMUTABLE LANGUAGE java; CREATE TYPE javatest.BinaryColumnPair AS (col1 bytea, col2 bytea); CREATE FUNCTION javatest.binaryColumnTest() RETURNS SETOF javatest.BinaryColumnPair AS 'org.postgresql.pljava.example.BinaryColumnTest.getBinaryPairs' IMMUTABLE LANGUAGE java; CREATE TYPE javatest.MetaDataBooleans AS (method_name varchar(200), result boolean); CREATE FUNCTION javatest.getMetaDataBooleans() RETURNS SETOF javatest.MetaDataBooleans AS 'org.postgresql.pljava.example.MetaDataBooleans.getDatabaseMetaDataBooleans' LANGUAGE java; CREATE TYPE javatest.MetaDataStrings AS (method_name varchar(200), result varchar); CREATE FUNCTION javatest.getMetaDataStrings() RETURNS SETOF javatest.MetaDataStrings AS 'org.postgresql.pljava.example.MetaDataStrings.getDatabaseMetaDataStrings' LANGUAGE java; CREATE TYPE javatest.MetaDataInts AS (method_name varchar(200), result int); CREATE FUNCTION javatest.getMetaDataInts() RETURNS SETOF javatest.MetaDataInts AS 'org.postgresql.pljava.example.MetaDataInts.getDatabaseMetaDataInts' LANGUAGE java; CREATE FUNCTION javatest.callMetaDataMethod(varchar) RETURNS SETOF varchar AS 'org.postgresql.pljava.example.MetaDataTest.callMetaDataMethod' LANGUAGE java; CREATE FUNCTION javatest.executeSelect(varchar) RETURNS SETOF VARCHAR AS 'org.postgresql.pljava.example.ResultSetTest.executeSelect' LANGUAGE java; CREATE FUNCTION javatest.executeSelectToRecords(varchar) RETURNS SETOF RECORD AS 'org.postgresql.pljava.example.SetOfRecordTest.executeSelect' LANGUAGE java; CREATE FUNCTION javatest.countNulls(record) RETURNS int AS 'org.postgresql.pljava.example.Parameters.countNulls' LANGUAGE java; CREATE FUNCTION javatest.countNulls(int[]) RETURNS int AS 'org.postgresql.pljava.example.Parameters.countNulls(java.lang.Integer[])' LANGUAGE java; /* Here is an example of a scalar type that maps to a Java class. */ /* This is a dummy function that we must have in order to create a * PostgreSQL 'shell' type (i.e. a type that doesn't really exist * yet). If we where using language 'C', the shell type would be * created automatically. Instead, we use a well known C function * to force this behavior (ugly, it's been reported to the postgresql * dev mailing list). The suggested solution will probably be to * implement the following syntax for shell types: * * CREATE TYPE javatest.complex; */ CREATE FUNCTION javatest.complexdummy(cstring) RETURNS javatest.complex AS 'lower' LANGUAGE INTERNAL; /* The scalar input function */ CREATE FUNCTION complex_in(cstring) RETURNS javatest.complex AS 'UDT[org.postgresql.pljava.example.ComplexScalar] input' LANGUAGE java IMMUTABLE STRICT; /* The scalar output function */ CREATE FUNCTION complex_out(javatest.complex) RETURNS cstring AS 'UDT[org.postgresql.pljava.example.ComplexScalar] output' LANGUAGE java IMMUTABLE STRICT; /* The scalar receive function */ CREATE FUNCTION complex_recv(internal) RETURNS javatest.complex AS 'UDT[org.postgresql.pljava.example.ComplexScalar] receive' LANGUAGE java IMMUTABLE STRICT; /* The scalar send function */ CREATE FUNCTION complex_send(javatest.complex) RETURNS bytea AS 'UDT[org.postgresql.pljava.example.ComplexScalar] send' LANGUAGE java IMMUTABLE STRICT; /* The scalar type declaration */ CREATE TYPE javatest.complex ( internallength = 16, input = javatest.complex_in, output = javatest.complex_out, receive = javatest.complex_recv, send = javatest.complex_send, alignment = double ); /* A test function that just logs and returns its argument. */ CREATE FUNCTION javatest.logcomplex(javatest.complex) RETURNS javatest.complex AS 'org.postgresql.pljava.example.ComplexScalar.logAndReturn' LANGUAGE java IMMUTABLE STRICT; /* Here's an example of a tuple based UDT that maps * to a Java class. */ CREATE TYPE javatest.complextuple AS (x float8, y float8); /* Install the actual type mapping. */ SELECT sqlj.add_type_mapping('javatest.complextuple', 'org.postgresql.pljava.example.ComplexTuple'); /* A test function that just logs and returns its argument. */ CREATE FUNCTION javatest.logcomplex(javatest.complextuple) RETURNS javatest.complextuple AS 'org.postgresql.pljava.example.ComplexTuple.logAndReturn' LANGUAGE java IMMUTABLE STRICT; /* * An example using the ANY type */ CREATE FUNCTION javatest.loganyelement(anyelement) RETURNS anyelement AS 'org.postgresql.pljava.example.AnyTest.logAnyElement' LANGUAGE java IMMUTABLE STRICT; CREATE FUNCTION javatest.logany("any") RETURNS void AS 'org.postgresql.pljava.example.AnyTest.logAny' LANGUAGE java IMMUTABLE STRICT; CREATE FUNCTION javatest.makearray(anyelement) RETURNS anyarray AS 'org.postgresql.pljava.example.AnyTest.makeArray' LANGUAGE java IMMUTABLE STRICT; END INSTALL", "BEGIN REMOVE DROP SCHEMA javatest CASCADE; END REMOVE" } pljava-1.4.3/src/java/examples/org/0000755000014500000120000000000011634451404016240 5ustar johannstaffpljava-1.4.3/src/java/examples/org/postgresql/0000755000014500000120000000000011634451404020443 5ustar johannstaffpljava-1.4.3/src/java/examples/org/postgresql/pljava/0000755000014500000120000000000011634451404021720 5ustar johannstaffpljava-1.4.3/src/java/examples/org/postgresql/pljava/example/0000755000014500000120000000000011634451404023353 5ustar johannstaffpljava-1.4.3/src/java/examples/org/postgresql/pljava/example/HugeResultSet.java0000644000014500000120000000241411634451404026762 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Random; import java.util.logging.Logger; import org.postgresql.pljava.ResultSetProvider; public class HugeResultSet implements ResultSetProvider { private final int m_rowCount; private final Random m_random; public static ResultSetProvider executeSelect(int rowCount) throws SQLException { return new HugeResultSet(rowCount); } public HugeResultSet(int rowCount) throws SQLException { m_rowCount = rowCount; m_random = new Random(System.currentTimeMillis()); } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { // Stop when we reach rowCount rows. // if(currentRow >= m_rowCount) { Logger.getAnonymousLogger().info("HugeResultSet ends"); return false; } receiver.updateInt(1, currentRow); receiver.updateInt(2, m_random.nextInt()); receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis())); return true; } public void close() {} } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/AnyTest.java0000644000014500000120000000163711634451404025614 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.logging.Logger; public class AnyTest { private static Logger s_logger = Logger.getAnonymousLogger(); public static void logAny(Object param) throws SQLException { s_logger.info("logAny received an object of class " + param.getClass()); } public static Object logAnyElement(Object param) throws SQLException { s_logger.info("logAnyElement received an object of class " + param.getClass()); return param; } public static Object[] makeArray(Object param) { Object[] result = (Object[])Array.newInstance(param.getClass(), 1); result[0] = param; return result; } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/UsingPropertiesAsResultSet.java0000644000014500000120000000216211634451404031520 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.ResultSetHandle; /** * This implementation uses another function that returns a set of a complex * type and returns the ResultSet produced by a query. * * @author Thomas Hallgren */ public class UsingPropertiesAsResultSet implements ResultSetHandle { private PreparedStatement m_statement; public ResultSet getResultSet() throws SQLException { m_statement = DriverManager.getConnection("jdbc:default:connection").prepareStatement("SELECT * FROM propertyExample()"); return m_statement.executeQuery(); } public void close() throws SQLException { m_statement.close(); m_statement = null; } public static ResultSetHandle getProperties() throws SQLException { return new UsingPropertiesAsResultSet(); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/ComplexTuple.java0000644000014500000120000000224611634451404026643 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.SQLData; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.SQLOutput; import java.util.logging.Logger; public class ComplexTuple implements SQLData { private static Logger s_logger = Logger.getAnonymousLogger(); private double m_x; private double m_y; private String m_typeName; public String getSQLTypeName() { return m_typeName; } public void readSQL(SQLInput stream, String typeName) throws SQLException { m_typeName = typeName; m_x = stream.readDouble(); m_y = stream.readDouble(); s_logger.info(typeName + " from SQLInput"); } public void writeSQL(SQLOutput stream) throws SQLException { stream.writeDouble(m_x); stream.writeDouble(m_y); s_logger.info(m_typeName + " to SQLOutput"); } public static ComplexTuple logAndReturn(ComplexTuple cpl) { s_logger.info(cpl.getSQLTypeName() + "(" + cpl.m_x + ", " + cpl.m_y + ")"); return cpl; } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Parameters.java0000644000014500000120000001455311634451404026331 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.math.BigDecimal; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.TimeZone; import java.util.logging.Logger; /** * Some methods used for testing parameter and return value coersion and resolution * of overloaded methods. * * @author Thomas Hallgren */ public class Parameters { static void log(String msg) { // GCJ has a somewhat serious bug (reported) // if("GNU libgcj".equals(System.getProperty("java.vm.name"))) { System.out.print("INFO: "); System.out.println(msg); } else Logger.getAnonymousLogger().info(msg); } public static int addOne(int value) { return value + 1; } public static int addOne(Integer value) { return value.intValue() + 1; } public static Integer nullOnEven(int value) { return (value % 2) == 0 ? null : new Integer(value); } public static int addOneLong(long value) { return (int)value + 1; } public static double addNumbers(short a, int b, long c, BigDecimal d, BigDecimal e, float f, double g) { return d.doubleValue() + e.doubleValue() + a + b + c + f + g; } public static Date getDate() { return new Date(System.currentTimeMillis()); } public static Time getTime() { return new Time(System.currentTimeMillis()); } public static Timestamp getTimestamp() { return new Timestamp(System.currentTimeMillis()); } public static int countNulls(ResultSet input) throws SQLException { int nullCount = 0; int top = input.getMetaData().getColumnCount(); for(int idx = 1; idx <= top; ++idx) { input.getObject(idx); if(input.wasNull()) nullCount++; } return nullCount; } public static int countNulls(Integer[] intArray) throws SQLException { int nullCount = 0; int top = intArray.length; for(int idx = 0; idx < top; ++idx) { if(intArray[idx] == null) nullCount++; } return nullCount; } public static byte print(byte value) { log("byte " + value); return value; } public static byte[] print(byte[] byteArray) { StringBuffer buf = new StringBuffer(); int top = byteArray.length; buf.append("byte[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(byteArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(byteArray[idx]); } buf.append('}'); } log(buf.toString()); return byteArray; } public static short print(short value) { log("short " + value); return value; } public static short[] print(short[] shortArray) { StringBuffer buf = new StringBuffer(); int top = shortArray.length; buf.append("short[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(shortArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(shortArray[idx]); } buf.append('}'); } log(buf.toString()); return shortArray; } public static int print(int value) { log("int " + value); return value; } public static int[] print(int[] intArray) { StringBuffer buf = new StringBuffer(); int top = intArray.length; buf.append("int[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(intArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(intArray[idx]); } buf.append('}'); } log(buf.toString()); return intArray; } public static long print(long value) { log("long " + value); return value; } public static long[] print(long[] longArray) { StringBuffer buf = new StringBuffer(); int top = longArray.length; buf.append("long[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(longArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(longArray[idx]); } buf.append('}'); } log(buf.toString()); return longArray; } public static float print(float value) { log("float " + value); return value; } public static float[] print(float[] floatArray) { StringBuffer buf = new StringBuffer(); int top = floatArray.length; buf.append("float[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(floatArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(floatArray[idx]); } buf.append('}'); } log(buf.toString()); return floatArray; } public static double print(double value) { log("double " + value); return value; } public static double[] print(double[] doubleArray) { StringBuffer buf = new StringBuffer(); int top = doubleArray.length; buf.append("double[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(doubleArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(doubleArray[idx]); } buf.append('}'); } log(buf.toString()); return doubleArray; } public static Integer[] print(Integer[] intArray) { StringBuffer buf = new StringBuffer(); int top = intArray.length; buf.append("Integer[] of size " + top); if(top > 0) { buf.append(" {"); buf.append(intArray[0]); for(int idx = 1; idx < top; ++idx) { buf.append(','); buf.append(intArray[idx]); } buf.append('}'); } log(buf.toString()); return intArray; } public static void print(Date time) { DateFormat p = DateFormat.getDateInstance(DateFormat.FULL); log("Local Date is " + p.format(time)); p.setTimeZone(TimeZone.getTimeZone("UTC")); log("UTC Date is " + p.format(time)); log("TZ = " + TimeZone.getDefault().getDisplayName()); } public static void print(Time time) { DateFormat p = new SimpleDateFormat("HH:mm:ss z Z"); log("Local Time is " + p.format(time)); p.setTimeZone(TimeZone.getTimeZone("UTC")); log("UTC Time is " + p.format(time)); log("TZ = " + TimeZone.getDefault().getDisplayName()); } public static void print(Timestamp time) { DateFormat p = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL); log("Local Timestamp is " + p.format(time)); p.setTimeZone(TimeZone.getTimeZone("UTC")); log("UTC Timestamp is " + p.format(time)); log("TZ = " + TimeZone.getDefault().getDisplayName()); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/ComplexScalar.java0000644000014500000120000000443111634451404026755 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.sql.SQLData; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.SQLOutput; import java.util.logging.Logger; public class ComplexScalar implements SQLData { private static Logger s_logger = Logger.getAnonymousLogger(); private double m_x; private double m_y; private String m_typeName; public static ComplexScalar parse(String input, String typeName) throws SQLException { try { StreamTokenizer tz = new StreamTokenizer(new StringReader(input)); if(tz.nextToken() == '(' && tz.nextToken() == StreamTokenizer.TT_NUMBER) { double x = tz.nval; if(tz.nextToken() == ',' && tz.nextToken() == StreamTokenizer.TT_NUMBER) { double y = tz.nval; if(tz.nextToken() == ')') { s_logger.info(typeName + " from string"); return new ComplexScalar(x, y, typeName); } } } throw new SQLException("Unable to parse complex from string \"" + input + '"'); } catch(IOException e) { throw new SQLException(e.getMessage()); } } public ComplexScalar() { } public ComplexScalar(double x, double y, String typeName) { m_x = x; m_y = y; m_typeName = typeName; } public String getSQLTypeName() { return m_typeName; } public void readSQL(SQLInput stream, String typeName) throws SQLException { s_logger.info(typeName + " from SQLInput"); m_x = stream.readDouble(); m_y = stream.readDouble(); m_typeName = typeName; } public void writeSQL(SQLOutput stream) throws SQLException { s_logger.info(m_typeName + " to SQLOutput"); stream.writeDouble(m_x); stream.writeDouble(m_y); } public String toString() { s_logger.info(m_typeName + " toString"); StringBuffer sb = new StringBuffer(); sb.append('('); sb.append(m_x); sb.append(','); sb.append(m_y); sb.append(')'); return sb.toString(); } public static ComplexScalar logAndReturn(ComplexScalar cpl) { s_logger.info(cpl.getSQLTypeName() + cpl); return cpl; } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/TupleReturn.java0000644000014500000120000000426111634451404026512 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import org.postgresql.pljava.ResultSetProvider; /** * @author Thomas Hallgren */ public class TupleReturn implements ResultSetProvider { private final int m_base; private final int m_increment; public TupleReturn(int base, int increment) { m_base = base; m_increment = increment; } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { // Stop when we reach 12 rows. // if(currentRow >= 12) return false; receiver.updateInt(1, m_base); receiver.updateInt(2, m_base + m_increment * currentRow); receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis())); return true; } public void close() { } public static ResultSetProvider setReturn(int base, int increment) throws SQLException { return new TupleReturn(base, increment); } public static boolean tupleReturn(int base, int increment, ResultSet receiver) throws SQLException { receiver.updateInt(1, base); receiver.updateInt(2, base + increment); receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis())); return true; } public static boolean tupleReturn(Integer base, Integer increment, ResultSet receiver) throws SQLException { if(base == null) { receiver.updateNull(1); receiver.updateNull(2); } else { receiver.updateInt(1, base.intValue()); if(increment == null) receiver.updateNull(2); else receiver.updateInt(2, base.intValue() + increment.intValue()); } receiver.updateTimestamp(3, new Timestamp(System.currentTimeMillis())); return true; } public static String makeString(ResultSet _testSetReturn) throws SQLException { int base = _testSetReturn.getInt(1); int incbase = _testSetReturn.getInt(2); Timestamp ctime = _testSetReturn.getTimestamp(3); return "Base = \"" + base + "\", incbase = \"" + incbase + "\", ctime = \"" + ctime + "\""; } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/LoggerTest.java0000644000014500000120000000074711634451404026305 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.util.logging.Level; import java.util.logging.Logger; public class LoggerTest { public static void logMessage(String logLevel, String message) { Logger.getAnonymousLogger().log(Level.parse(logLevel), message); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/ResultSetTest.java0000644000014500000120000000424511634451404027015 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; /** * @author Filip Hrbek */ public class ResultSetTest { public static Iterator executeSelect(String selectSQL) throws SQLException { if (!selectSQL.toUpperCase().trim().startsWith("SELECT ")) { throw new SQLException("Not a SELECT statement"); } return new ResultSetTest(selectSQL).iterator(); } private ArrayList m_results; public ResultSetTest(String selectSQL) throws SQLException { Connection conn = DriverManager .getConnection("jdbc:default:connection"); m_results = new ArrayList(); StringBuffer result; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(selectSQL); ResultSetMetaData rsmd = rs.getMetaData(); int cnt = rsmd.getColumnCount(); result = new StringBuffer(); for (int i=1; i <= cnt; i++) { result.append( (rsmd.getColumnName(i) + "(" + rsmd.getColumnClassName(i) + ")" ) .replaceAll("(\\\\|;)","\\$1") + ";"); } m_results.add(result.toString()); while (rs.next()) { result = new StringBuffer(); Object rsObject = null; for(int i=1; i <= cnt; i++) { rsObject = rs.getObject(i); if (rsObject == null) { rsObject = ""; } result.append(rsObject.toString() .replaceAll("(\\\\|;)","\\$1") + ";"); } m_results.add(result.toString()); } rs.close(); } private Iterator iterator() { return m_results.iterator(); } public void close() { } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/example.properties0000644000014500000120000000017111634451404027123 0ustar johannstafffirst.example.property = 1 second.example.property = two third.example.property = "Hmm, take a guess. Could it be three?"pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/UsingPropertiesAsScalarSet.java0000644000014500000120000000274211634451404031453 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; /** * This implementation uses another function that returns a set of a complex * type, concatenates the name and value of that type and returns this as * a set of a scalar type. Somewhat cumbersome way to display properties * but it's a good test. * * @author Thomas Hallgren */ public class UsingPropertiesAsScalarSet { public static Iterator getProperties() throws SQLException { StringBuffer bld = new StringBuffer(); ArrayList list = new ArrayList(); Connection conn = DriverManager.getConnection("jdbc:default:connection"); Statement stmt = conn.createStatement(); try { ResultSet rs = stmt.executeQuery("SELECT name, value FROM propertyExample()"); try { while(rs.next()) { bld.setLength(0); bld.append(rs.getString(1)); bld.append(" = "); bld.append(rs.getString(2)); list.add(bld.toString()); } return list.iterator(); } finally { try { rs.close(); } catch(SQLException e) {} } } finally { try { stmt.close(); } catch(SQLException e) {} } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Threads.java0000644000014500000120000000354311634451404025615 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.util.logging.Level; import java.util.logging.Logger; /** * This class contains thread related methods. * * @author Thomas Hallgren */ public class Threads { private final static String s_lockOne = "lock number one"; private final static String s_lockTwo = "lock number two"; static class Locker extends Thread { private final String m_name; private final String m_first; private final String m_second; Locker(String name, String first, String second) { m_name = name; m_first = first; m_second = second; } public void run() { Logger log = Logger.getAnonymousLogger(); try { log.info("Thread " + m_name + " wants " + m_first); synchronized(m_first) { log.info("Thread " + m_name + " got " + m_first); Thread.sleep(100); log.info("Thread " + m_name + " wants " + m_second); synchronized(m_second) { log.info("Thread " + m_name + " got " + m_second); } } } catch(Exception e) { log.log(Level.INFO, "Thread " + m_name + " got exception: ", e); } } } public static void forceDeadlock() { // Cause a deadlock. // Locker x = new Locker("x", s_lockOne, s_lockTwo); Locker y = new Locker("y", s_lockTwo, s_lockOne); x.start(); y.start(); // Sleep for a while. The logger will fail if trying to access the // backend when it is not in a Java call. // try { Logger.getAnonymousLogger().log(Level.INFO, "Main thread sleeps a while"); Thread.sleep(1000); } catch(Exception e) { Logger.getAnonymousLogger().log(Level.INFO, "Main thread interrupted while sleeping: ", e); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/SPIActions.java0000644000014500000120000002220011634451404026166 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Time; import java.util.logging.Logger; import org.postgresql.pljava.SavepointListener; import org.postgresql.pljava.Session; import org.postgresql.pljava.SessionManager; /** * Some methods used for testing the SPI JDBC driver. * * @author Thomas Hallgren */ public class SPIActions { static void log(String msg) { // GCJ has a somewhat serious bug (reported) // if("GNU libgcj".equals(System.getProperty("java.vm.name"))) { System.out.print("INFO: "); System.out.println(msg); } else Logger.getAnonymousLogger().info(msg); } private static final String SP_CHECKSTATE = "sp.checkState"; private static final SavepointListener spListener = new SavepointListener() { public void onAbort(Session session, Savepoint savepoint, Savepoint parent) throws SQLException { log("Abort of savepoint " + savepoint.getSavepointId()); nextState(session, 3, 4); } public void onCommit(Session session, Savepoint savepoint, Savepoint parent) throws SQLException { log("Commit of savepoint " + savepoint.getSavepointId()); nextState(session, 3, 4); } public void onStart(Session session, Savepoint savepoint, Savepoint parent) throws SQLException { log("Start of savepoint " + savepoint.getSavepointId()); nextState(session, 0, 1); } }; private static void nextState(Session session, int expected, int next) throws SQLException { Integer state = (Integer)session.getAttribute(SP_CHECKSTATE); if(state == null || state.intValue() != expected) throw new SQLException(SP_CHECKSTATE + ": Expected " + expected + ", got " + state); session.setAttribute(SP_CHECKSTATE, new Integer(next)); } public static int testSavepointSanity() throws SQLException { Connection conn = DriverManager.getConnection("jdbc:default:connection"); // Create an anonymous savepoint. // log("Attempting to set an anonymous savepoint"); Session currentSession = SessionManager.current(); currentSession.setAttribute(SP_CHECKSTATE, new Integer(0)); currentSession.addSavepointListener(spListener); Savepoint sp = conn.setSavepoint(); nextState(currentSession, 1, 2); try { Statement stmt = conn.createStatement(); log("Attempting to set a SAVEPOINT using SQL (should fail)"); stmt.execute("SAVEPOINT foo"); } catch(SQLException e) { log("It failed allright. Everything OK then"); log("Rolling back to anonymous savepoint"); nextState(currentSession, 2, 3); conn.rollback(sp); nextState(currentSession, 4, 5); return 1; } finally { currentSession.removeSavepointListener(spListener); } throw new SQLException("SAVEPOINT through SQL succeeded. That's bad news!"); } public static int testTransactionRecovery() throws SQLException { Connection conn = DriverManager.getConnection("jdbc:default:connection"); // Create an anonymous savepoint. // log("Attempting to set an anonymous savepoint"); Session currentSession = SessionManager.current(); currentSession.setAttribute(SP_CHECKSTATE, new Integer(0)); currentSession.addSavepointListener(spListener); Statement stmt = conn.createStatement(); Savepoint sp = conn.setSavepoint(); nextState(currentSession, 1, 2); try { log("Attempting to execute a statement with a syntax error"); stmt.execute("THIS MUST BE A SYNTAX ERROR"); } catch(SQLException e) { log("It failed. Let's try to recover " + "by rolling back to anonymous savepoint"); nextState(currentSession, 2, 3); conn.rollback(sp); nextState(currentSession, 4, 5); log("Rolled back."); log("Now let's try to execute a correct statement."); currentSession.setAttribute(SP_CHECKSTATE, new Integer(0)); sp = conn.setSavepoint(); nextState(currentSession, 1, 2); ResultSet rs = stmt.executeQuery("SELECT 'OK'"); while (rs.next()) { log("Expected: OK; Retrieved: " + rs.getString(1)); } rs.close(); stmt.close(); nextState(currentSession, 2, 3); conn.releaseSavepoint(sp); nextState(currentSession, 4, 5); return 1; } finally { currentSession.removeSavepointListener(spListener); } //Should never get here return -1; } public static int transferPeopleWithSalary(int salary) throws SQLException { Connection conn = DriverManager.getConnection("jdbc:default:connection"); PreparedStatement select = null; PreparedStatement insert = null; PreparedStatement delete = null; ResultSet rs = null; String stmt; try { stmt = "SELECT id, name, salary FROM employees1 WHERE salary > ?"; log(stmt); select = conn.prepareStatement(stmt); stmt = "INSERT INTO employees2(id, name, salary, transferDay, transferTime) VALUES (?, ?, ?, ?, ?)"; log(stmt); insert = conn.prepareStatement(stmt); stmt = "DELETE FROM employees1 WHERE id = ?"; log(stmt); delete = conn.prepareStatement(stmt); log("assigning parameter value " + salary); select.setInt(1, salary); log("Executing query"); rs = select.executeQuery(); int rowNo = 0; log("Doing next"); while(rs.next()) { log("Processing row " + ++rowNo); int id = rs.getInt(1); String name = rs.getString(2); int empSal = rs.getInt(3); insert.setInt(1, id); insert.setString(2, name); insert.setInt(3, empSal); long now = System.currentTimeMillis(); insert.setDate(4, new Date(now)); insert.setTime(5, new Time(now)); int nRows = insert.executeUpdate(); log("Insert processed " + nRows + " rows"); delete.setInt(1, id); nRows = delete.executeUpdate(); log("Delete processed " + nRows + " rows"); log("Doing next"); } if(rowNo == 0) log("No row found"); return rowNo; } finally { if(select != null) select.close(); if(insert != null) insert.close(); if(delete != null) delete.close(); conn.close(); } } public static int maxFromSetReturnExample(int base, int increment) throws SQLException { int max = Integer.MIN_VALUE; Connection conn = DriverManager.getConnection("jdbc:default:connection"); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = conn.prepareStatement("SELECT base FROM setReturnExample(?, ?)"); stmt.setInt(1, base); stmt.setInt(2, increment); rs = stmt.executeQuery(); while(rs.next()) { base = rs.getInt(1); if(base > max) max = base; } return base; } finally { if(rs != null) rs.close(); if(stmt != null) stmt.close(); conn.close(); } } /** * Test of bug #1556 * */ public static void nestedStatements(int innerCount) throws SQLException { Connection connection = DriverManager.getConnection("jdbc:default:connection"); Statement statement = connection.createStatement(); // Create a set of ID's so that we can do somthing semi-useful during // the long loop. // statement.execute("DELETE FROM javatest.employees1"); statement.execute("INSERT INTO javatest.employees1 VALUES(" + "1, 'Calvin Forrester', 10000)"); statement.execute("INSERT INTO javatest.employees1 VALUES(" + "2, 'Edwin Archer', 20000)"); statement.execute("INSERT INTO javatest.employees1 VALUES(" + "3, 'Rebecka Shawn', 30000)"); statement.execute("INSERT INTO javatest.employees1 VALUES(" + "4, 'Priscilla Johnson', 25000)"); int idx = 1; ResultSet results = statement.executeQuery("SELECT * FROM javatest.hugeResult(" + innerCount + ")"); while(results.next()) { Statement innerStatement = connection.createStatement(); innerStatement.executeUpdate( "UPDATE javatest.employees1 SET salary = salary + 1 WHERE id=" + idx); innerStatement.close(); if(++idx == 5) idx = 0; } results.close(); statement.close(); connection.close(); } public static String getDateAsString() throws SQLException { ResultSet rs = null; Statement stmt = null; Connection conn = DriverManager.getConnection("jdbc:default:connection"); try { stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT CURRENT_DATE"); if(rs.next()) return rs.getDate(1).toString(); return "Date could not be retrieved"; } finally { if(rs != null) rs.close(); if(stmt != null) stmt.close(); conn.close(); } } public static String getTimeAsString() throws SQLException { ResultSet rs = null; Statement stmt = null; Connection conn = DriverManager.getConnection("jdbc:default:connection"); try { stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT CURRENT_TIME"); if(rs.next()) return rs.getTime(1).toString(); return "Time could not be retrieved"; } finally { if(rs != null) rs.close(); if(stmt != null) stmt.close(); conn.close(); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/MetaDataBooleans.java0000644000014500000120000000516411634451404027367 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.*; import java.lang.reflect.Method; import java.util.Arrays; import java.util.ArrayList; import java.util.Comparator; import java.util.logging.Logger; import org.postgresql.pljava.ResultSetProvider; /** * @author Filip Hrbek */ public class MetaDataBooleans implements ResultSetProvider { String[] methodNames; Boolean[] methodResults; public MetaDataBooleans() throws SQLException { Logger log = Logger.getAnonymousLogger(); class MethodComparator implements Comparator { public int compare(Object a, Object b) { return ((Method)a).getName().compareTo(((Method)b).getName()); } } Connection conn = DriverManager .getConnection("jdbc:default:connection"); DatabaseMetaData md = conn.getMetaData(); Method[] m = DatabaseMetaData.class.getMethods(); Arrays.sort(m, new MethodComparator()); Class prototype[]; Class returntype; Object[] args = new Object[0]; Boolean result = null; ArrayList mn = new ArrayList(); ArrayList mr = new ArrayList(); for(int i = 0; i < m.length; i++) { prototype = m[i].getParameterTypes(); if(prototype.length > 0) continue; returntype = m[i].getReturnType(); if(!returntype.equals(boolean.class)) continue; try { result = (Boolean)m[i].invoke(md, args); } catch(Exception e) { log.info("Method: " + m[i].getName() + " => " + e.getMessage()); } catch(AbstractMethodError e) { // probably a JDBC 4 method that isn't supported yet log.info("Method: " + m[i].getName() + " => " + e.getMessage()); } mn.add(m[i].getName()); mr.add(result); } methodNames = (String[])mn.toArray(new String[0]); methodResults = (Boolean[])mr.toArray(new Boolean[0]); } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(currentRow < methodNames.length) { receiver.updateString(1, methodNames[currentRow]); receiver.updateBoolean(2, methodResults[currentRow].booleanValue()); return true; } return false; } public void close() { } public static ResultSetProvider getDatabaseMetaDataBooleans() throws SQLException { try { return new MetaDataBooleans(); } catch(SQLException e) { throw new SQLException("Error reading DatabaseMetaData", e .getMessage()); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/UsingProperties.java0000644000014500000120000000474411634451404027371 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.io.IOException; import java.io.InputStream; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.logging.Logger; import org.postgresql.pljava.ObjectPool; import org.postgresql.pljava.PooledObject; import org.postgresql.pljava.ResultSetProvider; import org.postgresql.pljava.SessionManager; /** * @author Thomas Hallgren */ public class UsingProperties implements ResultSetProvider, PooledObject { private static Logger s_logger = Logger.getAnonymousLogger(); private final Properties m_properties; private final ObjectPool m_pool; private Iterator m_propertyIterator; public UsingProperties(ObjectPool pool) throws IOException { m_pool = pool; m_properties = new Properties(); s_logger.info("** UsingProperties()"); InputStream propStream = this.getClass().getResourceAsStream("example.properties"); if(propStream == null) { s_logger.info("example.properties was null"); } else { m_properties.load(propStream); propStream.close(); s_logger.info("example.properties has " + m_properties.size() + " entries"); } } public void activate() { s_logger.info("** UsingProperties.activate()"); m_propertyIterator = m_properties.entrySet().iterator(); } public void remove() { s_logger.info("** UsingProperties.remove()"); m_properties.clear(); } public void passivate() { s_logger.info("** UsingProperties.passivate()"); m_propertyIterator = null; } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(!m_propertyIterator.hasNext()) { s_logger.fine("no more rows, returning false"); return false; } Map.Entry propEntry = (Map.Entry)m_propertyIterator.next(); receiver.updateString(1, (String)propEntry.getKey()); receiver.updateString(2, (String)propEntry.getValue()); // s_logger.fine("next row created, returning true"); return true; } public void close() throws SQLException { m_pool.passivateInstance(this); } public static ResultSetProvider getProperties() throws SQLException { ObjectPool pool = SessionManager.current().getObjectPool(UsingProperties.class); return (ResultSetProvider)pool.activateInstance(); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/MetaDataInts.java0000644000014500000120000000511511634451404026536 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.ArrayList; import java.util.Comparator; import java.util.logging.Logger; import org.postgresql.pljava.ResultSetProvider; /** * @author Filip Hrbek */ public class MetaDataInts implements ResultSetProvider { String[] methodNames; Integer[] methodResults; public MetaDataInts() throws SQLException { Logger log = Logger.getAnonymousLogger(); class MethodComparator implements Comparator { public int compare(Object a, Object b) { return ((Method)a).getName().compareTo(((Method)b).getName()); } } Connection conn = DriverManager .getConnection("jdbc:default:connection"); DatabaseMetaData md = conn.getMetaData(); Method[] m = DatabaseMetaData.class.getMethods(); Arrays.sort(m, new MethodComparator()); Class prototype[]; Class returntype; Object[] args = new Object[0]; Integer result = null; ArrayList mn = new ArrayList(); ArrayList mr = new ArrayList(); for(int i = 0; i < m.length; i++) { prototype = m[i].getParameterTypes(); if(prototype.length > 0) continue; returntype = m[i].getReturnType(); if(!returntype.equals(int.class)) continue; try { result = (Integer)m[i].invoke(md, args); } catch(InvocationTargetException e) { log.info("Method: " + m[i].getName() + " => " + e.getTargetException().getMessage()); result = new Integer(-1); } catch(Exception e) { log.info("Method: " + m[i].getName() + " => " + e.getMessage()); result = new Integer(-1); } mn.add(m[i].getName()); mr.add(result); } methodNames = (String[])mn.toArray(new String[mn.size()]); methodResults = (Integer[])mr.toArray(new Integer[mr.size()]); } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(currentRow < methodNames.length) { receiver.updateString(1, methodNames[currentRow]); receiver.updateInt(2, methodResults[currentRow].intValue()); return true; } return false; } public void close() { } public static ResultSetProvider getDatabaseMetaDataInts() throws SQLException { try { return new MetaDataInts(); } catch(SQLException e) { throw new SQLException("Error reading DatabaseMetaData", e .getMessage()); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Users.java0000644000014500000120000000206311634451404025320 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.postgresql.pljava.ResultSetHandle; public class Users implements ResultSetHandle { private final String m_filter; private Statement m_statement; public Users(String filter) { m_filter = filter; } public ResultSet getResultSet() throws SQLException { m_statement = DriverManager.getConnection("jdbc:default:connection").createStatement(); return m_statement.executeQuery("SELECT * FROM pg_user WHERE " + m_filter); } public void close() throws SQLException { m_statement.close(); } public static ResultSetHandle listSupers() { return new Users("usesuper = true"); } public static ResultSetHandle listNonSupers() { return new Users("usesuper = false"); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Security.java0000644000014500000120000000142511634451404026027 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.io.File; import java.io.IOException; import java.sql.SQLException; public class Security { /** * The following method should fail if the language in use * is untrusted. * @return The name of a created temporary file. * @throws SQLException */ public static String createTempFile() throws SQLException { try { File tmp = File.createTempFile("pljava", ".test"); tmp.deleteOnExit(); return tmp.getAbsolutePath(); } catch(IOException e) { throw new SQLException(e.getMessage()); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/SetOfRecordTest.java0000644000014500000120000000204411634451404027235 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.ResultSetHandle; public class SetOfRecordTest implements ResultSetHandle { private final PreparedStatement m_statement; public static ResultSetHandle executeSelect(String selectSQL) throws SQLException { return new SetOfRecordTest(selectSQL); } public SetOfRecordTest(String selectSQL) throws SQLException { Connection conn = DriverManager .getConnection("jdbc:default:connection"); m_statement = conn.prepareStatement(selectSQL); } public void close() throws SQLException { m_statement.close(); } public ResultSet getResultSet() throws SQLException { return m_statement.executeQuery(); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/MetaDataTest.java0000644000014500000120000001716111634451404026544 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.*; import java.lang.reflect.*; import java.util.regex.*; import java.util.Iterator; import java.util.ArrayList; /** * @author Filip Hrbek */ public class MetaDataTest { public static Iterator callMetaDataMethod(String methodCall) throws SQLException { return new MetaDataTest(methodCall).iterator(); } private String m_methodName; private Object[] m_methodArgs; private Class[] m_methodArgTypes; private ArrayList m_results; public MetaDataTest(String methodCall) throws SQLException { Connection conn = DriverManager .getConnection("jdbc:default:connection"); DatabaseMetaData md = conn.getMetaData(); ResultSet rs; m_results = new ArrayList(); StringBuffer result; parseMethodCall(methodCall); try { Method m = DatabaseMetaData.class .getMethod(m_methodName, m_methodArgTypes); if (!m.getReturnType().equals(ResultSet.class)) { throw new NoSuchMethodException("Unexpected return type"); } rs = (ResultSet)m.invoke(md, m_methodArgs); ResultSetMetaData rsmd = rs.getMetaData(); int cnt = rsmd.getColumnCount(); result = new StringBuffer(); for (int i=1; i <= cnt; i++) { result.append( (rsmd.getColumnName(i) + "(" + rsmd.getColumnClassName(i) + ")" ) .replaceAll("(\\\\|;)","\\$1") + ";"); } m_results.add(result.toString()); while (rs.next()) { result = new StringBuffer(); Object rsObject = null; for(int i=1; i <= cnt; i++) { rsObject = rs.getObject(i); if (rsObject == null) { rsObject = ""; } result.append(rsObject.toString() .replaceAll("(\\\\|;)","\\$1") + ";"); } m_results.add(result.toString()); } rs.close(); } catch (NoSuchMethodException nme) { StringBuffer sb = new StringBuffer(); for (int i=0; i < m_methodArgTypes.length; i++) { if (sb.length() > 0) { sb.append(","); } sb.append(m_methodArgTypes[i].getName()); } throw new SQLException("No such method or non-resultset return type: " + m_methodName + "(" + sb.toString() + ")"); } catch (InvocationTargetException ite) { throw new SQLException(ite.getTargetException().toString()); } catch (Exception e) { throw new SQLException("Method error: " + e.toString()); } } private Iterator iterator() { return m_results.iterator(); } public void close() { } /** * Method call parser. * Let's say that only String, String[], int, int[] and boolean types are accepted. * Examples: * "foo" => String * {"foo","bar"} => String[] * {} => String[] (zero length array, not null!) * 10 => int * -7 => int * [10,3] => int[] * [] => int[] (zero length array, not null!) * TRUE => boolean * (String)null => String (value null) * (String[])null => String[] (value null) * (int[])null => int[] (value null) * * Complete example: * select * from callMetaDataMethod('getTables((String)null,"sqlj","jar%",{"TABLE"})'); * * It makes the implementation simplier, and it is sufficient for * DatabaseMetaData testing. */ private void parseMethodCall(String methodCall) throws SQLException { try { Pattern p = Pattern.compile("^\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\((.*)\\)\\s*$"); Matcher m = p.matcher(methodCall); String paramString; String auxParamString; String param; ArrayList objects = new ArrayList(); ArrayList types = new ArrayList(); if (m.matches()) { m_methodName = m.group(1); paramString = m.group(2).trim(); p = Pattern.compile( "^\\s*(" + "\\(\\s*(?:String|int)\\s*(?:\\[\\s*\\])?\\s*\\)\\s*null|" + //String, String[] or int[] null "TRUE|" + //boolean TRUE "FALSE|" + //boolean FALSE "(?:\\-|\\+)?[0-9]+|" + //int "\\[((?:[^\\[\\]])*)\\]|" + //int[] "\"((?:[^\\\\\"]|\\\\.)*)\"|" + //String "\\{((?:[^\\{\\}]|\"(?:[^\\\\\"]|\\\\.)*\")*)\\}" + //String[] ")\\s*" + "(?:,|$)" + //comma separator "(.*)$"); //rest of the string auxParamString = paramString; while(!auxParamString.equals("")) { m = p.matcher(auxParamString); if (!m.matches()) { throw new SQLException("Invalid parameter list: " + paramString); } param = m.group(1); if (param.startsWith("\"")) //it is a string { param = m.group(3); //string without the quotes objects.add(param); types.add(String.class); } else if (param.startsWith("{")) //it is a string array { param = m.group(4); //string array without the curly brackets Pattern parr = Pattern.compile("^\\s*\"((?:[^\\\\\"]|\\\\.)*)\"\\s*(?:,|$)(.*)$"); Matcher marr; String auxParamArr = param.trim(); ArrayList strList = new ArrayList(); while(!auxParamArr.equals("")) { marr = parr.matcher(auxParamArr); if (!marr.matches()) { throw new SQLException("Invalid string array: " + param); } strList.add(marr.group(1)); auxParamArr = marr.group(2).trim(); } objects.add(strList.toArray(new String[0])); types.add(String[].class); } else if (param.equals("TRUE") || param.equals("FALSE")) //it is a boolean { objects.add(new Boolean(param)); types.add(Boolean.TYPE); } else if (param.startsWith("(")) //it is String, String[] or int[] null { Pattern pnull = Pattern.compile("^\\(\\s*(String|int)\\s*(\\[\\s*\\])?\\s*\\)\\s*null\\s*$"); Matcher mnull = pnull.matcher(param); if (mnull.matches()) { objects.add(null); if (mnull.group(2) == null) { if (mnull.group(1).equals("String")) { types.add(String.class); } else { throw new SQLException("Primitive 'int' cannot be null"); } } else { if (mnull.group(1).equals("String")) { types.add(String[].class); } else { types.add(int[].class); } } } else { throw new SQLException("Invalid null value: " + param); } } else if (param.startsWith("[")) //it is a int array { param = m.group(2); //int array without the square brackets Pattern parr = Pattern.compile("^\\s*(\\d+)\\s*(?:,|$)(.*)$"); Matcher marr; String auxParamArr = param.trim(); ArrayList intList = new ArrayList(); while(!auxParamArr.equals("")) { marr = parr.matcher(auxParamArr); if (!marr.matches()) { throw new SQLException("Invalid int array: " + param); } intList.add(new Integer(marr.group(1))); auxParamArr = marr.group(2).trim(); } objects.add(intList.toArray(new Integer[0])); types.add(int[].class); } else //it is an int { objects.add(new Integer(param)); types.add(Integer.TYPE); } auxParamString = m.group(5).trim(); } } else { throw new SQLException("Syntax error"); } m_methodArgs = objects.toArray(new Object[0]); m_methodArgTypes = (Class[])types.toArray(new Class[0]); } catch (Exception e) { throw new SQLException("Invalid method call: " + methodCall + ". Cause: " + e.toString()); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Point.java0000644000014500000120000000216011634451404025306 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.SQLData; import java.sql.SQLException; import java.sql.SQLInput; import java.sql.SQLOutput; import java.util.logging.Logger; public class Point implements SQLData { private static Logger s_logger = Logger.getAnonymousLogger(); private double m_x; private double m_y; private String m_typeName; public String getSQLTypeName() { return m_typeName; } public void readSQL(SQLInput stream, String typeName) throws SQLException { s_logger.info(typeName + " from SQLInput"); m_x = stream.readDouble(); m_y = stream.readDouble(); m_typeName = typeName; } public void writeSQL(SQLOutput stream) throws SQLException { s_logger.info(m_typeName + " to SQLOutput"); stream.writeDouble(m_x); stream.writeDouble(m_y); } public static Point logAndReturn(Point cpl) { s_logger.info(cpl.getSQLTypeName() + cpl); return cpl; } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/MetaDataStrings.java0000644000014500000120000000460011634451404027250 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.*; import java.lang.reflect.Method; import java.util.Arrays; import java.util.ArrayList; import java.util.Comparator; import java.util.logging.Logger; import org.postgresql.pljava.ResultSetProvider; /** * @author Filip Hrbek */ public class MetaDataStrings implements ResultSetProvider { String[] methodNames; String[] methodResults; public MetaDataStrings() throws SQLException { Logger log = Logger.getAnonymousLogger(); class MethodComparator implements Comparator { public int compare(Object a, Object b) { return ((Method)a).getName().compareTo(((Method)b).getName()); } } Connection conn = DriverManager .getConnection("jdbc:default:connection"); DatabaseMetaData md = conn.getMetaData(); Method[] m = DatabaseMetaData.class.getMethods(); Arrays.sort(m, new MethodComparator()); Class prototype[]; Class returntype; Object[] args = new Object[0]; String result = null; ArrayList mn = new ArrayList(); ArrayList mr = new ArrayList(); for(int i = 0; i < m.length; i++) { prototype = m[i].getParameterTypes(); if(prototype.length > 0) continue; returntype = m[i].getReturnType(); if(!returntype.equals(String.class)) continue; try { result = (String)m[i].invoke(md, args); log.info("Method: " + m[i].getName() + " => Success"); } catch(Exception e) { log.info("Method: " + m[i].getName() + " => " + e.getMessage()); } mn.add(m[i].getName()); mr.add(result); } methodNames = (String[])mn.toArray(new String[0]); methodResults = (String[])mr.toArray(new String[0]); } public boolean assignRowValues(ResultSet receiver, int currentRow) throws SQLException { if(currentRow < methodNames.length) { receiver.updateString(1, methodNames[currentRow]); receiver.updateString(2, methodResults[currentRow]); return true; } return false; } public void close() { } public static ResultSetProvider getDatabaseMetaDataStrings() throws SQLException { try { return new MetaDataStrings(); } catch(SQLException e) { throw new SQLException("Error reading DatabaseMetaData", e .getMessage()); } } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/RandomInts.java0000644000014500000120000000207411634451404026277 0ustar johannstaff/* * Copyright (c) 2004 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.SQLException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Random; public class RandomInts implements Iterator { private final int m_rowCount; private final Random m_random; private int m_currentRow; public static Iterator createIterator(int rowCount) throws SQLException { return new RandomInts(rowCount); } public RandomInts(int rowCount) throws SQLException { m_rowCount = rowCount; m_random = new Random(System.currentTimeMillis()); } public boolean hasNext() { return m_currentRow < m_rowCount; } public Object next() { if(m_currentRow < m_rowCount) { ++m_currentRow; return new Integer(m_random.nextInt()); } throw new NoSuchElementException(); } public void remove() { throw new UnsupportedOperationException(); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/BinaryColumnTest.java0000644000014500000120000000246411634451404027466 0ustar johannstaff/* * Copyright (c) 2004 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; import org.postgresql.pljava.ResultSetProvider; public class BinaryColumnTest implements ResultSetProvider { public boolean assignRowValues(ResultSet rs, int rowCount) throws SQLException { try { if(rowCount >= 100) return false; int offset = rowCount * 100; ByteArrayOutputStream bld = new ByteArrayOutputStream(); DataOutputStream da = new DataOutputStream(bld); for(int idx = 0; idx < 100; ++idx) da.writeInt(offset + idx); byte[] bytes = bld.toByteArray(); ByteArrayInputStream input = new ByteArrayInputStream(bytes); rs.updateBinaryStream(1, input, bytes.length); rs.updateBytes(2, bytes); return true; } catch(IOException e) { throw new SQLException(e.getMessage()); } } public void close() throws SQLException { } public static ResultSetProvider getBinaryPairs() { return new BinaryColumnTest(); } } pljava-1.4.3/src/java/examples/org/postgresql/pljava/example/Triggers.java0000644000014500000120000001067611634451404026016 0ustar johannstaff/* * Copyright (c) 2004, 2005 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.logging.Logger; import org.postgresql.pljava.TriggerException; import org.postgresql.pljava.TriggerData; import org.postgresql.pljava.SessionManager; /** * This class contains some triggers that I found written in C under the * contrib/spi directory of the postgres source distribution. Code to create the * necessary tables, functions, triggers, and some code to actually * execute them can be found in class {@link org.postgresql.pljava.test.Tester}. * * @author Thomas Hallgren */ public class Triggers { /** * insert user name in response to a trigger. */ public static void insertUsername(TriggerData td) throws SQLException { if(td.isFiredForStatement()) throw new TriggerException(td, "can't process STATEMENT events"); if(td.isFiredAfter()) throw new TriggerException(td, "must be fired before event"); if(td.isFiredByDelete()) throw new TriggerException(td, "can't process DELETE events"); ResultSet _new = td.getNew(); String[] args = td.getArguments(); if(args.length != 1) throw new TriggerException(td, "one argument was expected"); if(_new.getString(args[0]) == null) _new.updateString(args[0], SessionManager.current().getUserName()); } public static void leakStatements(TriggerData td) throws SQLException { StringBuffer buf = new StringBuffer(); buf.append("Trigger "); buf.append(td.getName()); buf.append(" declared on table "); buf.append(td.getTableName()); buf.append(" was fired "); if(td.isFiredAfter()) buf.append("after"); else buf.append("before"); buf.append(' '); if(td.isFiredByDelete()) buf.append("delete"); else if(td.isFiredByInsert()) buf.append("insert"); else buf.append("update"); if(td.isFiredForEachRow()) buf.append(" on each row"); // DON'T DO LIKE THIS!!! Connection, PreparedStatement, and ResultSet instances // should always be closed. // int max = Integer.MIN_VALUE; Connection conn = DriverManager.getConnection("jdbc:default:connection"); PreparedStatement stmt = conn.prepareStatement("SELECT base FROM setReturnExample(?, ?)"); stmt.setInt(1, 5); stmt.setInt(2, 8); ResultSet rs = stmt.executeQuery(); while(rs.next()) { int base = rs.getInt(1); if(base > max) max = base; } buf.append(" reports max = " + max); stmt = conn.prepareStatement("INSERT INTO javatest.mdt (idesc) VALUES (?)"); stmt.setString(1, buf.toString()); stmt.executeUpdate(); } public static void afterUsernameInsert(TriggerData td) throws SQLException { Logger log = Logger.getAnonymousLogger(); log.info("After username insert, oid of tuple = " + td.getNew().getInt("oid")); } public static void afterUsernameUpdate(TriggerData td) throws SQLException { Logger log = Logger.getAnonymousLogger(); if(td.isFiredForStatement()) throw new TriggerException(td, "can't process STATEMENT events"); if(td.isFiredBefore()) throw new TriggerException(td, "must be fired after event"); if(!td.isFiredByUpdate()) throw new TriggerException(td, "can't process DELETE or INSERT events"); ResultSet _new = td.getNew(); String[] args = td.getArguments(); if(args.length != 1) throw new TriggerException(td, "one argument was expected"); String colName = args[0]; ResultSet _old = td.getOld(); log.info("Old name is \"" + _old.getString(colName) + '"'); log.info("New name is \"" + _new.getString(colName) + '"'); } /** * Update a modification time when the row is updated. */ public static void moddatetime(TriggerData td) throws SQLException { if(td.isFiredForStatement()) throw new TriggerException(td, "can't process STATEMENT events"); if(td.isFiredAfter()) throw new TriggerException(td, "must be fired before event"); if(!td.isFiredByUpdate()) throw new TriggerException(td, "can only process UPDATE events"); ResultSet _new = td.getNew(); String[] args = td.getArguments(); if(args.length != 1) throw new TriggerException(td, "one argument was expected"); _new.updateTimestamp(args[0], new Timestamp(System.currentTimeMillis())); } } pljava-1.4.3/src/java/examples/Makefile0000644000014500000120000000157711634451404017123 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- NAME := examples JAVADOCTITLE := 'PL/Java examples API Specification' JAVAFLAGS := -classpath $(TARGETDIR)/classes/pljava include $(MODULEROOT)/Makefile.global all: $(JARFILE) $(JARFILE): .timestamp \ $(SRCDIR)/deployment/examples.manifest \ $(SRCDIR)/deployment/examples.ddr \ $(SRCDIR)/org/postgresql/pljava/example/example.properties $(JAR) cmf $(SRCDIR)/deployment/examples.manifest $@ . \ -C $(SRCDIR) deployment/examples.ddr \ -C $(SRCDIR) org/postgresql/pljava/example/example.properties pljava-1.4.3/src/java/Makefile.global~0000644000014500000120000000263111634451404016732 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT. # # @author Thomas Hallgren #------------------------------------------------------------------------- SRCDIR := $(MODULEROOT)/$(NAME) SRCS := $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.java -print) CLASSES := $(shell find . -type f -name \*.class) JAR := jar JAVADOC := javadoc JAVAFLAGS += $(findstring -g,$(shell ${PG_CONFIG} --cflags)) ifdef USE_GCJ GCJ := gcj JAVAC := $(GCJ) -C else JAVAC := javac -source 1.4 -target 1.4 endif JARFILE := $(TARGETDIR)/$(NAME).jar .timestamp: $(SRCS) @echo $(JAVAC) -d . $(JAVAFLAGS) '' @$(JAVAC) -d . $(JAVAFLAGS) $(SRCS) @touch $@ .PHONY: javadoc $(TARGETDIR)/docs/COPYRIGHT.txt: $(PROJDIR)/COPYRIGHT @-mkdir -p $(@D) @cp $(PROJDIR)/COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: $(TARGETDIR)/docs/$(NAME)/.timestamp $(TARGETDIR)/docs/$(NAME)/.timestamp: $(SRCS) $(JAVADOC) -d $(TARGETDIR)/docs/$(NAME) \ -sourcepath $(SRCDIR) \ -subpackages org.postgresql.pljava \ -breakiterator \ -doctitle $(JAVADOCTITLE) \ -protected \ -bottom 'Copyright (c) 2003, 2004, 2005 TADA AB - Taby Sweden. \ Distributed under the terms shown in COPYRIGHT' @touch $(TARGETDIR)/docs/$(NAME)/.timestamp pljava-1.4.3/src/java/.#Makefile.global.1.100000644000014500000120000000262711634451404017240 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT. # # @author Thomas Hallgren #------------------------------------------------------------------------- SRCDIR := $(MODULEROOT)/$(NAME) SRCS := $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.java -print) CLASSES := $(shell find . -type f -name \*.class) JAR := jar JAVADOC := javadoc JAVAFLAGS += $(findstring -g,$(shell ${PG_CONFIG} --cflags)) ifdef USE_GCJ GCJ := gcj JAVAC := $(GCJ) -C else JAVAC := javac -source 1.4 -target 1.4 endif JARFILE := $(TARGETDIR)/$(NAME).jar .timestamp: $(SRCS) @echo $(JAVAC) -d . $(JAVAFLAGS) '' @$(JAVAC) -d . $(JAVAFLAGS) $(SRCS) @touch $@ .PHONY: javadoc $(TARGETDIR)/docs/COPYRIGHT.txt: $(PROJDIR)/COPYRIGHT @-mkdir -p $(@D) @cp $(PROJDIR)/COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: $(TARGETDIR)/docs/$(NAME)/.timestamp $(TARGETDIR)/docs/$(NAME)/.timestamp: $(SRCS) $(JAVADOC) -d $(TARGETDIR)/docs/$(NAME) \ -sourcepath $(SRCDIR) \ -subpackages org.postgresql.pljava \ -breakiterator \ -doctitle $(JAVADOCTITLE) \ -private \ -bottom 'Copyright (c) 2003, 2004, 2005 TADA AB - Taby Sweden. \ Distributed under the terms shown in COPYRIGHT' @touch $(TARGETDIR)/docs/$(NAME)/.timestamp pljava-1.4.3/src/java/Makefile.global0000644000014500000120000000263111634451404016534 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT. # # @author Thomas Hallgren #------------------------------------------------------------------------- SRCDIR := $(MODULEROOT)/$(NAME) SRCS := $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.java -print) CLASSES := $(shell find . -type f -name \*.class) JAR := jar JAVADOC := javadoc JAVAFLAGS += $(findstring -g,$(shell ${PG_CONFIG} --cflags)) ifdef USE_GCJ GCJ := gcj JAVAC := $(GCJ) -C else JAVAC := javac -source 1.4 -target 1.4 endif JARFILE := $(TARGETDIR)/$(NAME).jar .timestamp: $(SRCS) @echo $(JAVAC) -d . $(JAVAFLAGS) '' @$(JAVAC) -d . $(JAVAFLAGS) $(SRCS) @touch $@ .PHONY: javadoc $(TARGETDIR)/docs/COPYRIGHT.txt: $(PROJDIR)/COPYRIGHT @-mkdir -p $(@D) @cp $(PROJDIR)/COPYRIGHT $(TARGETDIR)/docs/COPYRIGHT.txt javadoc: $(TARGETDIR)/docs/$(NAME)/.timestamp $(TARGETDIR)/docs/$(NAME)/.timestamp: $(SRCS) $(JAVADOC) -d $(TARGETDIR)/docs/$(NAME) \ -sourcepath $(SRCDIR) \ -subpackages org.postgresql.pljava \ -breakiterator \ -doctitle $(JAVADOCTITLE) \ -protected \ -bottom 'Copyright (c) 2003, 2004, 2005 TADA AB - Taby Sweden. \ Distributed under the terms shown in COPYRIGHT' @touch $(TARGETDIR)/docs/$(NAME)/.timestamp pljava-1.4.3/src/java/tasks/0000755000014500000120000000000011634451404014760 5ustar johannstaffpljava-1.4.3/src/java/tasks/org/0000755000014500000120000000000011634451404015547 5ustar johannstaffpljava-1.4.3/src/java/tasks/org/postgresql/0000755000014500000120000000000011634451404017752 5ustar johannstaffpljava-1.4.3/src/java/tasks/org/postgresql/pljava/0000755000014500000120000000000011634451404021227 5ustar johannstaffpljava-1.4.3/src/java/tasks/org/postgresql/pljava/tasks/0000755000014500000120000000000011634451404022354 5ustar johannstaffpljava-1.4.3/src/java/tasks/org/postgresql/pljava/tasks/JarLoaderTask.java0000644000014500000120000000737611634451404025722 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.tasks; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.tools.ant.BuildException; public class JarLoaderTask extends PLJavaTask { private boolean m_deploy = true; private boolean m_reload = false; private File m_file; private String m_name; private String m_schema; public void execute() throws BuildException { if(m_file == null) throw new BuildException("file must be set", this.getLocation()); if(m_name == null) throw new BuildException("name must be set", this.getLocation()); InputStream in = null; byte[] image; try { int count; in = new FileInputStream(m_file); ByteArrayOutputStream bld = new ByteArrayOutputStream(); byte[] buf = new byte[0x1000]; while((count = in.read(buf)) > 0) bld.write(buf, 0, count); image = bld.toByteArray(); } catch(IOException e) { throw new BuildException("Unable to read file " + m_file, e, this.getLocation()); } finally { if(in != null) try { in.close(); } catch(IOException e) {} } ResultSet rs = null; PreparedStatement stmt = null; Connection conn = this.getConnection(); try { stmt = conn.prepareStatement(m_reload ? "SELECT sqlj.replace_jar(?, ?, ?)" : "SELECT sqlj.install_jar(?, ?, ?)"); stmt.setBytes(1, image); stmt.setString(2, m_name); stmt.setBoolean(3, m_deploy); stmt.executeQuery().close(); if(m_schema != null) { boolean found = false; stmt.close(); stmt = null; stmt = conn.prepareStatement("SELECT sqlj.get_classpath(?)"); stmt.setString(1, m_schema); rs = stmt.executeQuery(); String path = null; if(!rs.next()) { path = rs.getString(1); if(path != null) { String[] jars = path.split(":"); int idx = jars.length; while(--idx >= 0) if(jars[idx].equals(m_name)) { found = true; break; } } } rs.close(); rs = null; if(!found) { if(path != null && path.length() > 0) path = path + ":" + m_name; else path = m_name; stmt.close(); stmt = null; stmt = conn.prepareStatement("SELECT sqlj.set_classpath(?, ?)"); stmt.setString(1, m_schema); stmt.setString(2, path); stmt.executeQuery().close(); } } conn.commit(); } catch(SQLException e) { try { conn.rollback(); } catch(SQLException e2) {} throw new BuildException("Unable to do install_jar: " + e.getMessage(), e, this.getLocation()); } finally { if(rs != null) try { rs.close(); } catch(SQLException e) {} if(stmt != null) try { stmt.close(); } catch(SQLException e) {} try { conn.close(); } catch(SQLException ingore) {} } } public final boolean isDeploy() { return m_deploy; } public final void setDeploy(boolean deploy) { m_deploy = deploy; } public final File getFile() { return m_file; } public final void setFile(File file) { m_file = file; } public final String getName() { return m_name; } public final void setName(String name) { m_name = name; } public final boolean isReload() { return m_reload; } public final void setReload(boolean reload) { m_reload = reload; } public final String getSchema() { return m_schema; } public final void setSchema(String schema) { m_schema = schema; } } pljava-1.4.3/src/java/tasks/org/postgresql/pljava/tasks/PLJavaTask.java0000644000014500000120000000304211634451404025156 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root directory of this distribution or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ package org.postgresql.pljava.tasks; import java.sql.Connection; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.taskdefs.JDBCTask; public abstract class PLJavaTask extends JDBCTask { private String m_hostName; private String m_database; private int m_portNumber = -1; public PLJavaTask() { this.setDriver("org.postgresql.Driver"); } public final String getDatabase() { return m_database == null ? this.getUserId() : m_database; } public final void setDatabase(String database) { m_database = database; } public final String getHostName() { return m_hostName == null ? "localhost" : m_hostName; } public final void setHostName(String hostName) { m_hostName = hostName; } public final int getPortNumber() { return m_portNumber; } public final void setPortNumber(int portNumber) { m_portNumber = portNumber; } protected Connection getConnection() throws BuildException { String url = this.getUrl(); if(url == null) { StringBuffer cc = new StringBuffer(); cc.append("jdbc:postgresql://"); cc.append(this.getHostName()); int port = this.getPortNumber(); if(port != -1) { cc.append(':'); cc.append(port); } cc.append('/'); cc.append(this.getDatabase()); this.setUrl(cc.toString()); } return super.getConnection(); } } pljava-1.4.3/src/sql/0000755000014500000120000000000011634451403013510 5ustar johannstaffpljava-1.4.3/src/sql/uninstall.sql0000644000014500000120000000012311634451403016236 0ustar johannstaffDROP LANGUAGE java CASCADE; DROP LANGUAGE javaU CASCADE; DROP SCHEMA sqlj CASCADE; pljava-1.4.3/src/sql/install.sql0000644000014500000120000000550511634451403015704 0ustar johannstaffCREATE SCHEMA sqlj; GRANT USAGE ON SCHEMA sqlj TO public; CREATE FUNCTION sqlj.java_call_handler() RETURNS language_handler AS 'pljava' LANGUAGE C; CREATE TRUSTED LANGUAGE java HANDLER sqlj.java_call_handler; CREATE FUNCTION sqlj.javau_call_handler() RETURNS language_handler AS 'pljava' LANGUAGE C; CREATE LANGUAGE javaU HANDLER sqlj.javau_call_handler; CREATE TABLE sqlj.jar_repository( jarId SERIAL PRIMARY KEY, jarName VARCHAR(100) UNIQUE NOT NULL, jarOrigin VARCHAR(500) NOT NULL, jarOwner NAME NOT NULL, jarManifest TEXT, deploymentDesc INT ); GRANT SELECT ON sqlj.jar_repository TO public; CREATE TABLE sqlj.jar_entry( entryId SERIAL PRIMARY KEY, entryName VARCHAR(200) NOT NULL, jarId INT NOT NULL REFERENCES sqlj.jar_repository ON DELETE CASCADE, entryImage BYTEA NOT NULL, UNIQUE(jarId, entryName) ); GRANT SELECT ON sqlj.jar_entry TO public; ALTER TABLE sqlj.jar_repository ADD FOREIGN KEY (deploymentDesc) REFERENCES sqlj.jar_entry ON DELETE SET NULL; CREATE TABLE sqlj.classpath_entry( schemaName VARCHAR(30) NOT NULL, ordinal INT2 NOT NULL, jarId INT NOT NULL REFERENCES sqlj.jar_repository ON DELETE CASCADE, PRIMARY KEY(schemaName, ordinal) ); GRANT SELECT ON sqlj.classpath_entry TO public; CREATE TABLE sqlj.typemap_entry( mapId SERIAL PRIMARY KEY, javaName VARCHAR(200) NOT NULL, sqlName NAME NOT NULL ); GRANT SELECT ON sqlj.typemap_entry TO public; CREATE FUNCTION sqlj.install_jar(VARCHAR, VARCHAR, BOOLEAN) RETURNS void AS 'org.postgresql.pljava.management.Commands.installJar' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.install_jar(BYTEA, VARCHAR, BOOLEAN) RETURNS void AS 'org.postgresql.pljava.management.Commands.installJar' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.replace_jar(VARCHAR, VARCHAR, BOOLEAN) RETURNS void AS 'org.postgresql.pljava.management.Commands.replaceJar' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.replace_jar(BYTEA, VARCHAR, BOOLEAN) RETURNS void AS 'org.postgresql.pljava.management.Commands.replaceJar' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.remove_jar(VARCHAR, BOOLEAN) RETURNS void AS 'org.postgresql.pljava.management.Commands.removeJar' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.set_classpath(VARCHAR, VARCHAR) RETURNS void AS 'org.postgresql.pljava.management.Commands.setClassPath' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.get_classpath(VARCHAR) RETURNS VARCHAR AS 'org.postgresql.pljava.management.Commands.getClassPath' LANGUAGE java STABLE SECURITY DEFINER; CREATE FUNCTION sqlj.add_type_mapping(VARCHAR, VARCHAR) RETURNS void AS 'org.postgresql.pljava.management.Commands.addTypeMapping' LANGUAGE java SECURITY DEFINER; CREATE FUNCTION sqlj.drop_type_mapping(VARCHAR) RETURNS void AS 'org.postgresql.pljava.management.Commands.dropTypeMapping' LANGUAGE java SECURITY DEFINER; pljava-1.4.3/src/C/0000755000014500000120000000000011634451404013074 5ustar johannstaffpljava-1.4.3/src/C/include/0000755000014500000120000000000011634451404014517 5ustar johannstaffpljava-1.4.3/src/C/include/pljava/0000755000014500000120000000000011634451404015774 5ustar johannstaffpljava-1.4.3/src/C/include/pljava/HashMap.h~0000644000014500000120000001214411634451404017666 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_HashMap_h #define __pljava_HashMap_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /************************************************************* * The HashMap class. Maintains mappings between HashKey instances * and values. The mappings are stored in hash bucket chains * containing Entry instances. * * All Entries and HashKeys will be allocated using the same * MemoryContext as the one used when creating the HashMap. * A HashKey used for storing (HashKey_put) is cloned so that * the HashMap maintains its own copy. * * @author Thomas Hallgren * *************************************************************/ struct HashKey_; typedef struct HashKey_* HashKey; struct Iterator_; typedef struct Iterator_* Iterator; struct Entry_; typedef struct Entry_* Entry; struct HashMap_; typedef struct HashMap_* HashMap; /* * Creates a new HashMap with an initial capacity. If ctx is NULL, CurrentMemoryContext * will be used. */ extern HashMap HashMap_create(uint32 initialCapacity, MemoryContext ctx); /* * Clears the HashMap. */ extern void HashMap_clear(HashMap self); /* * Returns an iterator that iterates over the entries of * this HashMap. */ extern Iterator HashMap_entries(HashMap self); /* * Returns the object stored using the given key or NULL if no * such object can be found. */ extern void* HashMap_get(HashMap self, HashKey key); /* * Returns the object stored using the given null * terminated string or NULL if no such object can be found. */ extern void* HashMap_getByString(HashMap self, const char* key); /* * Returns the object stored using the given Oid or NULL * if no such object can be found. */ extern void* HashMap_getByOid(HashMap self, Oid key); /* * Returns the object stored using the given Opaque pointer or NULL * if no such object can be found. */ extern void* HashMap_getByOpaque(HashMap self, void* key); /* * Stores the given value under the given key. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. * This method will make a private copy of the key argument. */ extern void* HashMap_put(HashMap self, HashKey key, void* value); /* * Stores the given value under the given null terminated string. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. */ extern void* HashMap_putByString(HashMap self, const char* key, void* value); /* * Stores the given value under the given Oid. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOid(HashMap self, Oid key, void* value); /* * Stores the given value under the given Opaque pointer. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOpaque(HashMap self, void* key, void* value); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_remove(HashMap self, HashKey key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. The key associated with the value is deleted. */ extern void* HashMap_removeByString(HashMap self, const char* key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOid(HashMap self, Oid key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOpaque(HashMap self, void* key); /* * Returns the number of entries currently in the HashMap */ extern uint32 HashMap_size(HashMap self); /************************************************************* * An instance of the Entry class holds a mapping between one * HashKey and its associated value. *************************************************************/ /* * Returns the value of the Entry. */ extern void* Entry_getValue(Entry self); /* * Assigns a new value to the Entry. Returns the old value. */ extern void* Entry_setValue(Entry self, void* value); /* * Returns the key of the Entry. Can be used for removal. */ extern HashKey Entry_getKey(Entry self); /************************************************************* * The HashKey is an abstract class. Currently, three different * implementations are used. Oid, Opaque (void*), and String. *************************************************************/ /* * Clone the key. The clone is allocated in the given context. */ extern HashKey HashKey_clone(HashKey self, MemoryContext ctx); /* * Return true if the key is equal to another key. */ extern bool HashKey_equals(HashKey self, HashKey other); /* * Return the hash code for the key */ extern uint32 HashKey_hashCode(HashKey self); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/SPI.h0000644000014500000120000000226011634451404016600 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_SPI_h #define __pljava_SPI_h #include "pljava/PgObject.h" #include #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * Some needed additions to the SPI set of functions. * * @author Thomas Hallgren * ***********************************************************************/ typedef struct { SubTransactionId xid; int nestingLevel; char name[1]; } Savepoint; /* infant is set to the savepoint that is being created durin a setSavepoint call. * It is used by the onStart callback. */ extern Savepoint* infant; extern Savepoint* SPI_setSavepoint(const char* name); extern void SPI_releaseSavepoint(Savepoint* sp); extern void SPI_rollbackSavepoint(Savepoint* sp); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 1) extern char* SPI_getnspname(Relation rel); #endif #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/.#Backend.h.1.140000644000014500000120000000205311634451404020177 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Copyright (c) 2011 2ndQuadrant Ltd. * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Backend_h #define __pljava_Backend_h #include "pljava/Function.h" /* #ifdef PG_GETCONFIGOPTION */ /* #error PG_GETCONFIGOPTION already defined, it needs to be renamed. */ /* #endif */ /* #if */ /* #define PG_GETCONFIGOPTION( key ) GetConfigOption( */ #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Backend contains the call handler, initialization of the * PL/Java, access to config variables, and logging. * * @author Thomas Hallgren *****************************************************************/ extern bool integerDateTimes; void Backend_setJavaSecurity(bool trusted); int Backend_setJavaLogLevel(int logLevel); #ifdef __cplusplus } #endif #endif /* !__pljava_Backend_h */ pljava-1.4.3/src/C/include/pljava/Invocation.h0000644000014500000120000000547011634451404020264 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Invocation_h #define __pljava_Invocation_h #include #include "pljava/pljava.h" #ifdef __cplusplus extern "C" { #endif struct CallLocal_; typedef struct CallLocal_ CallLocal; struct Invocation_ { /** * A Java object representing the current invocation. This * field will be NULL if no such object has been requested. */ jobject invocation; /** * The context to use when allocating values that are to be * returned from the call. */ MemoryContext upperContext; /** * Set when an SPI_connect is issued. Ensures that SPI_finish * is called when the function exits. */ bool hasConnected; /** * Set to true if the call originates from an ExprContextCallback. When * it does, we should not close any cursors. */ bool inExprContextCB; /** * Set to true if the executing function is trusted */ bool trusted; /** * The currently executing Function. */ Function function; /** * Set to true if an elog with a severity >= ERROR * has occured. All calls from Java to the backend will * be prevented until this flag is reset (by a rollback * of a savepoint or function exit). */ bool errorOccured; /** * List of call local structures that has been wrapped * during this invocation. */ CallLocal* callLocals; /** * The previous call context when nested function calls * are made or 0 if this call is at the top level. */ Invocation* previous; }; extern Invocation* currentInvocation; extern void Invocation_assertConnect(void); extern void Invocation_assertDisconnect(void); extern void Invocation_pushBootContext(Invocation* ctx); extern void Invocation_popBootContext(void); extern void Invocation_pushInvocation(Invocation* ctx, bool trusted); extern void Invocation_popInvocation(bool wasException); extern jlong Invocation_createLocalWrapper(void* pointer); extern void* Invocation_getWrappedPointer(jlong wrapper); extern void Invocation_freeLocalWrapper(jlong wrapper); extern jobject Invocation_getTypeMap(void); /* * Switch memory context to a context that is durable between calls to * the call manager but not durable between queries. The old context is * returned. This method can be used when creating values that will be * returned from the Pl/Java routines. Once the values have been created * a call to MemoryContextSwitchTo(oldContext) must follow where oldContext * is the context returned from this call. */ extern MemoryContext Invocation_switchToUpperContext(void); #ifdef __cplusplus } #endif #endif /* !__pljava_Invocation_h */ pljava-1.4.3/src/C/include/pljava/SQLOutputToTuple.h0000644000014500000120000000153111634451404021342 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_SQLOutputToTuple_h #define __pljava_SQLOutputToTuple_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif #include /*********************************************************************** * Provides mapping between java.sql.SQLOutput and a HeapTuple * * @author Thomas Hallgren * ***********************************************************************/ extern jobject SQLOutputToTuple_create(TupleDesc td); extern HeapTuple SQLOutputToTuple_getTuple(jobject sqlOutput); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/.#HashMap.h.1.70000644000014500000120000001221311634451404020112 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_HashMap_h #define __pljava_HashMap_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /************************************************************* * The HashMap class. Maintains mappings between HashKey instances * and values. The mappings are stored in hash bucket chains * containing Entry instances. * * All Entries and HashKeys will be allocated using the same * MemoryContext as the one used when creating the HashMap. * A HashKey used for storing (HashKey_put) is cloned so that * the HashMap maintains its own copy. * * @author Thomas Hallgren * *************************************************************/ struct HashKey_; typedef struct HashKey_* HashKey; struct Iterator_; typedef struct Iterator_* Iterator; struct Entry_; typedef struct Entry_* Entry; struct HashMap_; typedef struct HashMap_* HashMap; /* * Creates a new HashMap with an initial capacity. If ctx is NULL, CurrentMemoryContext * will be used. */ extern HashMap HashMap_create(uint32 initialCapacity, MemoryContext ctx); /* * Clears the HashMap. */ extern void HashMap_clear(HashMap self); /* * Returns an iterator that iterates over the entries of * this HashMap. */ extern Iterator HashMap_entries(HashMap self); /* * Returns the object stored using the given key or NULL if no * such object can be found. */ extern void* HashMap_get(HashMap self, HashKey key); /* * Returns the object stored using the given null * terminated string or NULL if no such object can be found. */ extern void* HashMap_getByString(HashMap self, const char* key); /* * Returns the object stored using the given Oid or NULL * if no such object can be found. */ extern void* HashMap_getByOid(HashMap self, Oid key); /* * Returns the object stored using the given Opaque pointer or NULL * if no such object can be found. */ extern void* HashMap_getByOpaque(HashMap self, void* key); /* * Stores the given value under the given key. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. * This method will make a private copy of the key argument. */ extern void* HashMap_put(HashMap self, HashKey key, void* value); /* * Stores the given value under the given null terminated string. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. */ extern void* HashMap_putByString(HashMap self, const char* key, void* value); /* * Stores the given value under the given Oid. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOid(HashMap self, Oid key, void* value); /* * Stores the given value under the given Opaque pointer. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOpaque(HashMap self, void* key, void* value); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_remove(HashMap self, HashKey key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. The key associated with the value is deleted. */ extern void* HashMap_removeByString(HashMap self, const char* key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOid(HashMap self, Oid key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOpaque(HashMap self, void* key); /* * Returns the number of entries currently in the HashMap */ extern uint32 HashMap_size(HashMap self); /************************************************************* * An instance of the Entry class holds a mapping between one * HashKey and its associated value. *************************************************************/ /* * Returns the value of the Entry. */ extern void* Entry_getValue(Entry self); /* * Assigns a new value to the Entry. Returns the old value. */ extern void* Entry_setValue(Entry self, void* value); /* * Returns the key of the Entry. Can be used for removal. */ extern HashKey Entry_getKey(Entry self); /************************************************************* * The HashKey is an abstract class. Currently, three different * implementations are used. Oid, Opaque (void*), and String. *************************************************************/ /* * Clone the key. The clone is allocated in the given context. */ extern HashKey HashKey_clone(HashKey self, MemoryContext ctx); /* * Return true if the key is equal to another key. */ extern bool HashKey_equals(HashKey self, HashKey other); /* * Return the hash code for the key */ extern uint32 HashKey_hashCode(HashKey self); extern void HashMap_initialize(void); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/PgObject_priv.h0000644000014500000120000000410411634451404020701 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_PgObject_priv_h #define __pljava_PgObject_priv_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * Private section of the PgObject class * * @author Thomas Hallgren *****************************************************************/ /* * In C++, this would be known as the virtual destructor. */ typedef void (*Finalizer) (PgObject self); /* * C++ has no instantiated class. This is comparable to * the virtual function table (vtbl) but it includes some * stuff in addition to functions. */ struct PgObjectClass_ { /* * Allocated size for instances of this class. */ Size instanceSize; /* * Name of class. */ const char* name; /* * "virtual" finalizer. */ Finalizer finalize; }; struct PgObject_ { PgObjectClass m_class; }; /* * Internal bogus. Someone forgot to replace a function * pointer somewhere. */ extern void _PgObject_pureVirtualCalled(PgObject self); /* * Throw an exception indicating that wanted member could not be * found. */ extern void PgObject_throwMemberError(jclass cls, const char* memberName, const char* signature, bool isMethod, bool isStatic); /* * Allocate an instance in the given MemoryContext. */ extern PgObject PgObjectClass_allocInstance(PgObjectClass clazz, MemoryContext ctx); /* * Initialize a class instance with given name, instance size, and finalizer. If * the finalizer is NULL, the default PgObject_finalize (which does nothing) * will be used. */ extern void PgObjectClass_init(PgObjectClass self, const char* name, Size instanceSize, Finalizer finalizer); /* * Create a default class instance with given name and finalizer. */ extern PgObjectClass PgObjectClass_create(const char* name, Size instanceSize, Finalizer finalizer); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/JNICalls.h0000644000014500000120000002451311634451404017551 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_JNICalls_h #define __pljava_JNICalls_h #include "pljava/pljava.h" #ifdef __cplusplus extern "C" { #endif #define BEGIN_NATIVE_NO_ERRCHECK if(beginNativeNoErrCheck(env)) { #define BEGIN_NATIVE if(beginNative(env)) { #define END_NATIVE JNI_setEnv(0); } /*********************************************************************** * All calls to and from the JVM uses this header. The calls are implemented * using a fence mechanism that prevents multiple threads to access * the backend simultaniously. * * @author Thomas Hallgren * ***********************************************************************/ /* * Entry guards for when the JVM calls into native code */ extern bool beginNative(JNIEnv* env); extern bool beginNativeNoErrCheck(JNIEnv* env); extern jclass ServerException_class; extern jmethodID ServerException_getErrorData; extern jmethodID ServerException_init; extern jclass Class_class; extern jmethodID Class_getName; extern jclass Throwable_class; extern jmethodID Throwable_getMessage; extern jmethodID Throwable_printStackTrace; extern jclass IllegalArgumentException_class; extern jmethodID IllegalArgumentException_init; extern jclass SQLException_class; extern jmethodID SQLException_init; extern jmethodID SQLException_getSQLState; extern jclass UnsupportedOperationException_class; extern jmethodID UnsupportedOperationException_init; /* * Misc JNIEnv mappings. See for more info. */ extern jboolean JNI_callBooleanMethod(jobject object, jmethodID methodID, ...); extern jboolean JNI_callBooleanMethodV(jobject object, jmethodID methodID, va_list args); extern jbyte JNI_callByteMethod(jobject object, jmethodID methodID, ...); extern jbyte JNI_callByteMethodV(jobject object, jmethodID methodID, va_list args); extern jdouble JNI_callDoubleMethod(jobject object, jmethodID methodID, ...); extern jdouble JNI_callDoubleMethodV(jobject object, jmethodID methodID, va_list args); extern jfloat JNI_callFloatMethod(jobject object, jmethodID methodID, ...); extern jfloat JNI_callFloatMethodV(jobject object, jmethodID methodID, va_list args); extern jint JNI_callIntMethod(jobject object, jmethodID methodID, ...); extern jint JNI_callIntMethodV(jobject object, jmethodID methodID, va_list args); extern jlong JNI_callLongMethod(jobject object, jmethodID methodID, ...); extern jlong JNI_callLongMethodV(jobject object, jmethodID methodID, va_list args); extern jobject JNI_callObjectMethod(jobject object, jmethodID methodID, ...); extern jobject JNI_callObjectMethodV(jobject object, jmethodID methodID, va_list args); extern jshort JNI_callShortMethod(jobject object, jmethodID methodID, ...); extern jshort JNI_callShortMethodV(jobject object, jmethodID methodID, va_list args); extern jboolean JNI_callStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jbyte JNI_callStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jdouble JNI_callStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jfloat JNI_callStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jint JNI_callStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jlong JNI_callStaticLongMethod(jclass clazz, jmethodID methodID, ...); extern jlong JNI_callStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jlong JNI_callStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args); extern jobject JNI_callStaticObjectMethod(jclass clazz, jmethodID methodID, ...); extern jobject JNI_callStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern jobject JNI_callStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args); extern jshort JNI_callStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern void JNI_callStaticVoidMethod(jclass clazz, jmethodID methodID, ...); extern void JNI_callStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args); extern void JNI_callStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args); extern void JNI_callVoidMethod(jobject object, jmethodID methodID, ...); extern void JNI_callVoidMethodV(jobject object, jmethodID methodID, va_list args); extern jint JNI_createVM(JavaVM** javaVM, JavaVMInitArgs* vmArgs); extern void JNI_deleteGlobalRef(jobject object); extern void JNI_deleteLocalRef(jobject object); extern void JNI_deleteWeakGlobalRef(jweak object); extern jint JNI_destroyVM(JavaVM *vm); extern jboolean JNI_exceptionCheck(void); extern void JNI_exceptionClear(void); extern void JNI_exceptionDescribe(void); extern jthrowable JNI_exceptionOccurred(void); extern jclass JNI_findClass(const char* className); extern jsize JNI_getArrayLength(jarray array); extern jbyte* JNI_getByteArrayElements(jbyteArray array, jboolean* isCopy); extern void JNI_getByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf); extern jboolean* JNI_getBooleanArrayElements(jbooleanArray array, jboolean* isCopy); extern void JNI_getBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf); extern jfieldID JNI_getFieldID(jclass clazz, const char* name, const char* sig); extern jdouble* JNI_getDoubleArrayElements(jdoubleArray array, jboolean* isCopy); extern void JNI_getDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf); extern jfloat* JNI_getFloatArrayElements(jfloatArray array, jboolean* isCopy); extern void JNI_getFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf); extern jint* JNI_getIntArrayElements(jintArray array, jboolean* isCopy); extern void JNI_getIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf); extern jint JNI_getIntField(jobject object, jfieldID field); extern jlong* JNI_getLongArrayElements(jlongArray array, jboolean* isCopy); extern void JNI_getLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf); extern jlong JNI_getLongField(jobject object, jfieldID field); extern jmethodID JNI_getMethodID(jclass clazz, const char* name, const char* sig); extern jobject JNI_getObjectArrayElement(jobjectArray array, jsize index); extern jclass JNI_getObjectClass(jobject obj); extern jshort* JNI_getShortArrayElements(jshortArray array, jboolean* isCopy); extern void JNI_getShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf); extern jfieldID JNI_getStaticFieldID(jclass clazz, const char* name, const char* sig); extern jmethodID JNI_getStaticMethodID(jclass clazz, const char* name, const char* sig); extern jmethodID JNI_getStaticMethodIDOrNull(jclass clazz, const char* name, const char* sig); extern jobject JNI_getStaticObjectField(jclass clazz, jfieldID field); extern const char* JNI_getStringUTFChars(jstring string, jboolean* isCopy); extern jboolean JNI_hasNullArrayElement(jobjectArray array); extern jboolean JNI_isCallingJava(void); extern jboolean JNI_isInstanceOf(jobject obj, jclass clazz); extern jbyteArray JNI_newByteArray(jsize length); extern jbooleanArray JNI_newBooleanArray(jsize length); extern jobject JNI_newDirectByteBuffer(void* address, jlong capacity); extern jdoubleArray JNI_newDoubleArray(jsize length); extern jfloatArray JNI_newFloatArray(jsize length); extern jobject JNI_newGlobalRef(jobject object); extern jintArray JNI_newIntArray(jsize length); extern jobject JNI_newLocalRef(jobject object); extern jlongArray JNI_newLongArray(jsize length); extern jobject JNI_newObject(jclass clazz, jmethodID ctor, ...); extern jobject JNI_newObjectV(jclass clazz, jmethodID ctor, va_list args); extern jobjectArray JNI_newObjectArray(jsize length, jclass elementClass, jobject initialElement); extern jshortArray JNI_newShortArray(jsize length); extern jstring JNI_newStringUTF(const char* bytes); extern jobject JNI_newWeakGlobalRef(jobject object); extern jint JNI_pushLocalFrame(jint capacity); extern jobject JNI_popLocalFrame(jobject result); extern jint JNI_registerNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods); extern void JNI_releaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode); extern void JNI_releaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode); extern void JNI_releaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode); extern void JNI_releaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode); extern void JNI_releaseIntArrayElements(jintArray array, jint* elems, jint mode); extern void JNI_releaseLongArrayElements(jlongArray array, jlong* elems, jint mode); extern void JNI_releaseShortArrayElements(jshortArray array, jshort* elems, jint mode); extern void JNI_releaseStringUTFChars(jstring string, const char *utf); extern void JNI_setByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf); extern void JNI_setBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf); extern JNIEnv* JNI_setEnv(JNIEnv* env); extern void JNI_setDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf); extern void JNI_setFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf); extern void JNI_setIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf); extern void JNI_setLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf); extern void JNI_setShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf); extern void JNI_setLongField(jobject object, jfieldID field, jlong value); extern void JNI_setObjectArrayElement(jobjectArray array, jsize index, jobject value); extern void JNI_setThreadLock(jobject lockObject); extern jint JNI_throw(jthrowable obj); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/SQLOutputToChunk.h0000644000014500000120000000154711634451404021330 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_SQLOutputToChunk_h #define __pljava_SQLOutputToChunk_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * Provides mapping between java.sql.SQLOutput and a StringInfo buffer * allocated by the backend. * * @author Thomas Hallgren * ***********************************************************************/ #include jobject SQLOutputToChunk_create(StringInfo buffer); void SQLOutputToChunk_close(jobject output); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/HashMap.h0000644000014500000120000001214411634451404017470 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_HashMap_h #define __pljava_HashMap_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /************************************************************* * The HashMap class. Maintains mappings between HashKey instances * and values. The mappings are stored in hash bucket chains * containing Entry instances. * * All Entries and HashKeys will be allocated using the same * MemoryContext as the one used when creating the HashMap. * A HashKey used for storing (HashKey_put) is cloned so that * the HashMap maintains its own copy. * * @author Thomas Hallgren * *************************************************************/ struct HashKey_; typedef struct HashKey_* HashKey; struct Iterator_; typedef struct Iterator_* Iterator; struct Entry_; typedef struct Entry_* Entry; struct HashMap_; typedef struct HashMap_* HashMap; /* * Creates a new HashMap with an initial capacity. If ctx is NULL, CurrentMemoryContext * will be used. */ extern HashMap HashMap_create(uint32 initialCapacity, MemoryContext ctx); /* * Clears the HashMap. */ extern void HashMap_clear(HashMap self); /* * Returns an iterator that iterates over the entries of * this HashMap. */ extern Iterator HashMap_entries(HashMap self); /* * Returns the object stored using the given key or NULL if no * such object can be found. */ extern void* HashMap_get(HashMap self, HashKey key); /* * Returns the object stored using the given null * terminated string or NULL if no such object can be found. */ extern void* HashMap_getByString(HashMap self, const char* key); /* * Returns the object stored using the given Oid or NULL * if no such object can be found. */ extern void* HashMap_getByOid(HashMap self, Oid key); /* * Returns the object stored using the given Opaque pointer or NULL * if no such object can be found. */ extern void* HashMap_getByOpaque(HashMap self, void* key); /* * Stores the given value under the given key. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. * This method will make a private copy of the key argument. */ extern void* HashMap_put(HashMap self, HashKey key, void* value); /* * Stores the given value under the given null terminated string. If * an old value was stored using this key, the old value is returned. * Otherwise this method returns NULL. */ extern void* HashMap_putByString(HashMap self, const char* key, void* value); /* * Stores the given value under the given Oid. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOid(HashMap self, Oid key, void* value); /* * Stores the given value under the given Opaque pointer. If an old value * was stored using this key, the old value is returned. Otherwise * this method returns NULL. */ extern void* HashMap_putByOpaque(HashMap self, void* key, void* value); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_remove(HashMap self, HashKey key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. The key associated with the value is deleted. */ extern void* HashMap_removeByString(HashMap self, const char* key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOid(HashMap self, Oid key); /* * Removes the value stored under the given key. The the old value * (if any) is returned. */ extern void* HashMap_removeByOpaque(HashMap self, void* key); /* * Returns the number of entries currently in the HashMap */ extern uint32 HashMap_size(HashMap self); /************************************************************* * An instance of the Entry class holds a mapping between one * HashKey and its associated value. *************************************************************/ /* * Returns the value of the Entry. */ extern void* Entry_getValue(Entry self); /* * Assigns a new value to the Entry. Returns the old value. */ extern void* Entry_setValue(Entry self, void* value); /* * Returns the key of the Entry. Can be used for removal. */ extern HashKey Entry_getKey(Entry self); /************************************************************* * The HashKey is an abstract class. Currently, three different * implementations are used. Oid, Opaque (void*), and String. *************************************************************/ /* * Clone the key. The clone is allocated in the given context. */ extern HashKey HashKey_clone(HashKey self, MemoryContext ctx); /* * Return true if the key is equal to another key. */ extern bool HashKey_equals(HashKey self, HashKey other); /* * Return the hash code for the key */ extern uint32 HashKey_hashCode(HashKey self); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/Function.h0000644000014500000120000000462511634451404017741 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Function_h #define __pljava_Function_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /******************************************************************* * The Function instance is the most central class of the Pl/Java * system. Parsing of the "AS" (later to become "EXTERNAL NAME") * and class/method lookups is done here. * * A Function instance knows how to coerce all Datum parameters into * their Java equivalent, how to call its assocated Java class and * method and how to coerce the Java return value into a Datum. * * Functions are cached using their Oid. They live in TopMemoryContext. * * @author Thomas Hallgren * *******************************************************************/ /* * Clear all cached function to method entries. This is called after a * successful replace_jar operation. */ extern void Function_clearFunctionCache(void); /* * Get a Function using a function Oid. If the function is not found, one * will be created based on the class and method name denoted in the "AS" * clause, the parameter types, and the return value of the function * description. If "isTrigger" is set to true, the parameter type and * return value of the function will be fixed to: * * org.postgresql.pljava.Tuple (org.postgresql.pljava.TriggerData td) */ extern Function Function_getFunction(PG_FUNCTION_ARGS); /* * Invoke a function, i.e. coerce the parameters, call the java method, and * coerce the return value back to a Datum. */ extern Datum Function_invoke(Function self, PG_FUNCTION_ARGS); /* * Invoke a trigger. Wrap the TriggerData in org.postgresql.pljava.TriggerData * object, make the call, and unwrap the resulting Tuple. */ extern Datum Function_invokeTrigger(Function self, PG_FUNCTION_ARGS); /* * Returns the Type Map that is associated with the function */ extern jobject Function_getTypeMap(Function self); /* * Returns true if the currently executing function is non volatile, i.e. stable * or immutable. Such functions are not allowed to have side effects. */ extern bool Function_isCurrentReadOnly(void); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/pljava.h0000644000014500000120000000667011634451404017433 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_pljava_h #define __pljava_pljava_h #include #ifdef __cplusplus extern "C" { #endif /***************************************************************** * Misc stuff to tie Java to PostgreSQL. TRY/CATCH macros, thread * blocking, etc. resides here. * * @author Thomas Hallgren *****************************************************************/ #ifdef __STRICT_ANSI__ extern int vsnprintf(char* buf, size_t count, const char* format, va_list arg); #endif #include #include #include #include #include #include #include /* The errorOccured will be set when a call from Java into one of the * backend functions results in a elog that causes a longjmp (Levels >= ERROR) * that was trapped using the PLJAVA_TRY/PLJAVA_CATCH macros. * When this happens, all further calls from Java must be blocked since the * state of the current transaction is unknown. Further more, once the function * that initially called Java finally returns, the intended longjmp (the one * to the original value of Warn_restart) must be made. */ extern jlong mainThreadId; extern bool pljavaEntryFence(JNIEnv* env); extern JNIEnv* currentJNIEnv; extern MemoryContext JavaMemoryContext; #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) #define STACK_BASE_VARS #define STACK_BASE_PUSH(threadId) #define STACK_BASE_POP() #else #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3)) extern PGDLLIMPORT char* stack_base_ptr; #else extern DLLIMPORT char* stack_base_ptr; #endif #define STACK_BASE_VARS \ long saveMainThreadId = 0; \ char* saveStackBasePtr = 0; #define STACK_BASE_PUSH(threadId) \ if(threadId != mainThreadId) \ { \ saveStackBasePtr = stack_base_ptr; \ saveMainThreadId = mainThreadId; \ stack_base_ptr = (char*)&saveMainThreadId; \ mainThreadId = threadId; \ elog(DEBUG1, "Changed stack_base_ptr from %p to %p", saveStackBasePtr, stack_base_ptr); \ } #define STACK_BASE_POP() \ if(saveStackBasePtr != 0) \ { \ stack_base_ptr = saveStackBasePtr; \ mainThreadId = saveMainThreadId; \ elog(DEBUG1, "Restored stack_base_ptr to %p", saveStackBasePtr); \ } #endif /* NOTE! * When using the PG_TRY, PG_CATCH, PG_TRY_END family of macros, * it is an ABSOLUTE NECESSITY to use the PG_TRY_RETURN or * PG_TRY_RETURN_VOID in place of any return. */ #define PG_TRY_POP \ PG_exception_stack = save_exception_stack; \ error_context_stack = save_context_stack #define PG_TRY_RETURN(retVal) { PG_TRY_POP; return retVal; } #define PG_TRY_RETURN_VOID { PG_TRY_POP; return; } /* Some error codes missing from errcodes.h * * Class 07 - Dynamic SQL Exception */ #define ERRCODE_INVALID_DESCRIPTOR_INDEX MAKE_SQLSTATE('0','7', '0','0','9') /* * Union used when coercing void* to jlong and vice versa */ typedef union { void* ptrVal; jlong longVal; /* 64 bit quantity */ struct { /* Used when calculating pointer hash in systems where * a pointer is 64 bit */ uint32 intVal_1; uint32 intVal_2; } x64; } Ptr2Long; struct Invocation_; typedef struct Invocation_ Invocation; struct Function_; typedef struct Function_* Function; #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/Backend.h0000644000014500000120000000151011634451404017471 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Backend_h #define __pljava_Backend_h #include "pljava/Function.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Backend contains the call handler, initialization of the * PL/Java, access to config variables, and logging. * * @author Thomas Hallgren *****************************************************************/ extern bool integerDateTimes; void Backend_setJavaSecurity(bool trusted); int Backend_setJavaLogLevel(int logLevel); #ifdef __cplusplus } #endif #endif /* !__pljava_Backend_h */ pljava-1.4.3/src/C/include/pljava/Backend.h~0000644000014500000120000000151011634451404017667 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Backend_h #define __pljava_Backend_h #include "pljava/Function.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Backend contains the call handler, initialization of the * PL/Java, access to config variables, and logging. * * @author Thomas Hallgren *****************************************************************/ extern bool integerDateTimes; void Backend_setJavaSecurity(bool trusted); int Backend_setJavaLogLevel(int logLevel); #ifdef __cplusplus } #endif #endif /* !__pljava_Backend_h */ pljava-1.4.3/src/C/include/pljava/Exception.h0000644000014500000120000000502411634451404020104 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Exception_h #define __pljava_Exception_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************* * Java exception related things * * @author Thomas Hallgren * *******************************************************************/ /* * Trows an UnsupportedOperationException informing the caller that the * requested feature doesn't exist in the current version, it was introduced * starting with the intro version. */ extern void Exception_featureNotSupported(const char* requestedFeature, const char* introVersion); /* * Like ereport(ERROR, ...) but this method will raise a Java SQLException and * return. It will NOT do a longjmp. Suitable in native code that is called * from Java (such code must return to Java in order to have the real exception * thrown). */ extern void Exception_throw(int errCode, const char* errMessage, ...) /* This extension allows gcc to check the format string for consistency with the supplied arguments. */ __attribute__((format(printf, 2, 3))); /* * Like ereport(ERROR, ...) but this method will raise a Java IllegalArgumentException and * return. It will NOT do a longjmp. Suitable in native code that is called * from Java (such code must return to Java in order to have the real exception * thrown). */ extern void Exception_throwIllegalArgument(const char* errMessage, ...) /* This extension allows gcc to check the format string for consistency with the supplied arguments. */ __attribute__((format(printf, 1, 2))); /* * Like ereport(ERROR, ...) but this method will raise a Java SQLException and * return. It will NOT do a longjmp. */ extern void Exception_throwSPI(const char* function, int errCode); /* * This method will raise a Java ServerException based on an ErrorData obtained * by a call to CopyErrorData. It will NOT do a longjmp. It's intended use is * in PG_CATCH clauses. */ extern void Exception_throw_ERROR(const char* function); /* * Throw an exception indicating that wanted member could not be * found. This is an ereport(ERROR...) so theres' no return from * this function. */ extern void Exception_throwMemberError(const char* memberName, const char* signature, bool isMethod, bool isStatic); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/SQLInputFromChunk.h0000644000014500000120000000152611634451404021445 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_SQLInputFromChunk_h #define __pljava_SQLInputFromChunk_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * Provides mapping between java.sql.SQLInput and a chunk of memory * allocated by the backend. * * @author Thomas Hallgren * ***********************************************************************/ jobject SQLInputFromChunk_create(void* data, size_t dataSize); void SQLInputFromChunk_close(jobject input); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/SQLInputFromTuple.h0000644000014500000120000000146311634451404021466 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_SQLInputFromTuple_h #define __pljava_SQLInputFromTuple_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif #include /*********************************************************************** * Provides mapping between java.sql.SQLInput and a HeapTupleHeader * * @author Thomas Hallgren * ***********************************************************************/ extern jobject SQLInputFromTuple_create(HeapTupleHeader hth, TupleDesc td); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/backports.h0000644000014500000120000000275011634451404020141 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_backports_h #define __pljava_backports_h #include "pljava/pljava.h" #ifdef __cplusplus extern "C" { #endif #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) #include /*---------- * Support to ease writing functions returning composite types * * External declarations: * get_call_result_type: * Given a function's call info record, determine the kind of datatype * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId * receives the actual datatype OID (this is mainly useful for scalar * result types). If resultTupleDesc isn't NULL, *resultTupleDesc * receives a pointer to a TupleDesc when the result is of a composite * type, or NULL when it's a scalar result or the rowtype could not be * determined. NB: the tupledesc should be copied if it is to be * accessed over a long period. *---------- */ extern TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc); extern bool resolve_polymorphic_argtypes(int numargs, Oid *argtypes, Node *call_expr); #else #include #endif #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/type/0000755000014500000120000000000011634451404016755 5ustar johannstaffpljava-1.4.3/src/C/include/pljava/type/HeapTupleHeader.h0000644000014500000120000000173611634451404022135 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_HeapTupleHeader_h #define __pljava_type_HeapTupleHeader_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The HeapTupleHeader java class extends the NativeStruct and provides JNI * access to some of the attributes of the HeapTupleHeader structure. * * @author Thomas Hallgren *****************************************************************/ extern jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht); extern jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo); extern void HeapTupleHeader_free(JNIEnv* env, jlong hth); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Tuple.h0000644000014500000120000000216311634451404020221 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Tuple_h #define __pljava_Tuple_h #include "pljava/type/JavaWrapper.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The Tuple java class extends the NativeStruct and provides JNI * access to some of the attributes of the HeapTuple structure. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.Tuple instance */ extern jobject Tuple_create(HeapTuple tuple); extern jobject Tuple_internalCreate(HeapTuple tuple, bool mustCopy); extern jobjectArray Tuple_createArray(HeapTuple* tuples, jint size, bool mustCopy); /* * Return a java object at given index from a HeapTuple */ extern jobject Tuple_getObject(TupleDesc tupleDesc, HeapTuple tuple, int index); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Coerce.h0000644000014500000120000000236211634451404020331 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Coerce_h #define __pljava_type_Coerce_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * The Coerce class extends the Type and adds the members necessary to * perform standard Postgres input/output type coersion. * * @author Thomas Hallgren * **************************************************************************/ /* Create a type that will use the coerce function denoted by coerceFunctionID * to coerce Datums before they are passed to the object coercion function * of the originalType. */ extern Type Coerce_createIn(Type originalType, Type source, Oid coerceFunctionID); /* Create a type that will coerce an object into the type denoted by the * originalType and then coerce this datum using the coerce function denoted * by the coerceFunctionID. */ extern Type Coerce_createOut(Type originalType, Type dest, Oid coerceFunctionID); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/AclId.h0000644000014500000120000000147011634451404020104 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_AclId_h #define __pljava_type_AclId_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif #if (!(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0)) typedef Oid AclId; #endif /*********************************************************************** * ACL related stuff. * * @author Thomas Hallgren * ***********************************************************************/ extern jobject AclId_create(AclId aclId); extern AclId AclId_getAclId(jobject aclId); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/type/TupleTable.h0000644000014500000120000000174111634451404021172 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_TupleTable_h #define __pljava_TupleTable_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include #include /***************************************************************** * The TupleTable java class extends the NativeStruct and provides JNI * access to some of the attributes of the TupleTable structure. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.TupleTable instance */ extern jobject TupleTable_createFromSlot(TupleTableSlot* tupleTableSlot); extern jobject TupleTable_create(SPITupleTable* tupleTable, jobject knownTD); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/UDT.h0000644000014500000120000000225311634451404017564 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_UDT_h #define __pljava_type_UDT_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * The UDT class extends the Type and adds the members necessary to * perform standard Postgres input/output and send/receive conversion. * * @author Thomas Hallgren * **************************************************************************/ struct UDT_; typedef struct UDT_* UDT; extern Datum UDT_input(UDT udt, PG_FUNCTION_ARGS); extern Datum UDT_output(UDT udt, PG_FUNCTION_ARGS); extern Datum UDT_receive(UDT udt, PG_FUNCTION_ARGS); extern Datum UDT_send(UDT udt, PG_FUNCTION_ARGS); extern bool UDT_isScalar(UDT udt); extern UDT UDT_registerUDT(jclass clazz, Oid typeId, Form_pg_type pgType, TupleDesc td, bool isJavaBasedScalar); typedef Datum (*UDTFunction)(UDT udt, PG_FUNCTION_ARGS); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Timestamp.h0000644000014500000120000000207611634451404021076 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Timestamp_h #define __pljava_type_Timestamp_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Timestamp java class represents the native Timestamp. * * @author Thomas Hallgren *****************************************************************/ /* * Returns the current timezone. */ extern int Timestamp_getCurrentTimeZone(void); /* * Returns the timezone fo the given Timestamp. Comes in two variants. * The int64 variant will be used when PL/Java is used with a backend * compiled with integer datetimes. The double variant will be used when * this is not the case. */ extern int32 Timestamp_getTimeZone_id(int64 t); extern int32 Timestamp_getTimeZone_dd(double t); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Relation.h0000644000014500000120000000152111634451404020702 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Relation_h #define __pljava_Relation_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /******************************************************************* * The Relation java class extends the NativeStruct and provides JNI * access to some of the attributes of the Relation structure. * * @author Thomas Hallgren *******************************************************************/ /* * Create an instance of org.postgresql.pljava.Relation */ extern jobject Relation_create(Relation rel); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Type_priv.h0000644000014500000120000001322411634451404021111 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Type_priv_h #define __pljava_type_Type_priv_h #include "pljava/PgObject_priv.h" #include "pljava/HashMap.h" #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /* This is the "abstract" Type class. The Type is responsible for * value coercsions between Java types and PostGreSQL types. * * @author Thomas Hallgren */ struct TypeClass_ { struct PgObjectClass_ extendedClass; /* * Contains the JNI compliant signature for the type. */ const char* JNISignature; /* * Contains the Java type name. */ const char* javaTypeName; /* * The Java class that represents this type. Will be NULL for * primitive types. */ jclass javaClass; /* * Set to true if this type represents a dynamic type (anyelement or * collection/iterator of anyelement) */ bool dynamic; /* * Set to true if the invocation will create an out parameter (ResultSet typically) * to collect the return value. If so, the real return value will be a bool. */ bool outParameter; /* * Creates the array type for this type. */ Type (*createArrayType)(Type self, Oid arrayType); /* * Returns the real type for a dynamic type. A non dynamic type will * return itself. */ Type (*getRealType)(Type self, Oid realTypeID, jobject typeMap); /* * Returns true if this type uses the same postgres type the other type. * This is used when explicit java signatures are declared functions to * verify that the declared Java type is compatible with the SQL type. * * At present, the type argument must be either equal to self, or if * self is a Boolean, Character, or any Number, the primitive that * corresponds to that number (i.e. java.lang.Short == short). */ bool (*canReplaceType)(Type self, Type type); /* * Translate a given Datum into a jvalue accorging to the type represented * by this instance. */ DatumCoercer coerceDatum; /* * Translate a given Object into a Datum accorging to the type represented * by this instance. */ ObjectCoercer coerceObject; /* * Calls a java method using one of the CallMethodA routines where * corresponds to the type represented by this instance and * coerces the returned value into a Datum. * * The method will set the value pointed to by the wasNull parameter * to true if the Java method returned null. The method expects that * the wasNull parameter is set to false by the caller prior to the * call. */ Datum (*invoke)(Type self, jclass clazz, jmethodID method, jvalue* args, PG_FUNCTION_ARGS); jobject (*getSRFProducer)(Type self, jclass clazz, jmethodID method, jvalue* args); jobject (*getSRFCollector)(Type self, PG_FUNCTION_ARGS); bool (*hasNextSRF)(Type self, jobject producer, jobject collector, jint counter); Datum (*nextSRF)(Type self, jobject producer, jobject collector); void (*closeSRF)(Type self, jobject producer); const char* (*getJNISignature)(Type self); const char* (*getJNIReturnSignature)(Type self, bool forMultiCall, bool useAltRepr); /* * Returns the TupleDesc that corresponds to this type. */ TupleDesc (*getTupleDesc)(Type self, PG_FUNCTION_ARGS); }; struct Type_ { TypeClass typeClass; /* * The Oid that identifies this type. */ Oid typeId; /* * Points to the array type where this type is the element type. * If the type has no corresponding array type, this type will be NULL. */ Type arrayType; /* * If the type is an array type, this is the element type. */ Type elementType; /* * Points to the object type that corresponds to this type * if this type is a primitive. For non primitives, this attribute * will be NULL. */ Type objectType; /* * Oid keyed hash map of coercion routines that can front this type when doing * parameter input coercion. */ HashMap inCoercions; /* * Oid keyed hash map of coercion routines that can front this type when doing * coercion of output results. */ HashMap outCoercions; int16 length; bool byValue; char align; }; /* * Default version of canReplaceType. Returns true when * self and other are equal. */ extern bool _Type_canReplaceType(Type self, Type other); /* * Default version of invoke. Will make a JNI CallObjectMethod call and then * a call to self->coerceObject to create the Datum. */ extern Datum _Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS); /* * Return the m_oid member of the Type. This is the default version of * Type_getTupleDesc. */ extern TupleDesc _Type_getTupleDesc(Type self, PG_FUNCTION_ARGS); /* * Store a Type keyed by its Oid in the cache. */ extern void Type_cacheByOid(Oid typeId, Type type); /* * Create a TypeClass with default sizes for TypeClass and Type. */ extern TypeClass TypeClass_alloc(const char* className); /* * Create a TypeClass for a specific TypeClass size and a specific Type size. */ extern TypeClass TypeClass_alloc2(const char* className, Size classSize, Size instanceSize); /* * Types are always allocated in global context. */ extern Type TypeClass_allocInstance(TypeClass cls, Oid typeId); extern Type TypeClass_allocInstance2(TypeClass cls, Oid typeId, Form_pg_type pgType); #ifdef __cplusplus } #endif #endif /* Yet to implement LOG: Type name = 'abstime' LOG: Type name = 'box' LOG: Type name = 'cid' LOG: Type name = 'lseg' LOG: Type name = 'path' LOG: Type name = 'point' LOG: Type name = 'reltime' LOG: Type name = 'tid' LOG: Type name = 'tinterval' LOG: Type name = 'xid' */ pljava-1.4.3/src/C/include/pljava/type/Composite.h0000644000014500000120000000072111634451404021070 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Composite_h #define __pljava_type_Composite_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif extern Type Composite_obtain(Oid oid); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/JavaWrapper.h0000644000014500000120000000161211634451404021350 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_JavaWrapper_h #define __pljava_JavaWrapper_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * The JavaWrapper is a Java class that maintains a pointer to a * piece of memory allocated in the special JavaMemoryContext. * * @author Thomas Hallgren *************************************************************************/ /* * Allocates a new TypeClass and assigns a default coerceObject method used by * all JavaWrapper derivates. */ extern TypeClass JavaWrapperClass_alloc(const char* name); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/UDT_priv.h0000644000014500000120000000210111634451404020614 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_UDT_priv_h #define __pljava_type_UDT_priv_h #include "pljava/type/Type_priv.h" #include "pljava/type/UDT.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * @author Thomas Hallgren **************************************************************************/ struct UDT_ { /* * The UDT "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the String to a Type. */ struct Type_ Type_extension; jstring sqlTypeName; TupleDesc tupleDesc; jmethodID init; jmethodID parse; jmethodID toString; jmethodID readSQL; jmethodID writeSQL; }; extern Datum _UDT_coerceObject(Type self, jobject jstr); extern jvalue _UDT_coerceDatum(Type self, Datum value); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Type.h0000644000014500000120000001633411634451404020056 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Type_h #define __pljava_type_Type_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif #include /********************************************************************* * The Type class is responsible for data type conversions between the * Postgres Datum and the Java jvalue. A Type can also perform optimized * JNI calls that are type dependent (returning primitives) such as * CallIntMethod(...) or CallBooleanMethod(...). Consequently, the Type * of the return value of a function is responsible for its invocation. * * Types that are not mapped will default to a java.lang.String mapping * and use the Form_pg_type text conversion routines. * * @author Thomas Hallgren * *********************************************************************/ struct Type_; typedef struct Type_* Type; struct TypeClass_; typedef struct TypeClass_* TypeClass; /* * Returns the TypeClass */ extern TypeClass Type_getClass(Type self); /* * Returns true if the Type is primitive (i.e. not a real object in * the Java domain). */ extern bool Type_isPrimitive(Type self); /* * Returns true if this type uses the same postgres type the other type. * This is used when explicit java signatures are declared functions to * verify that the declared Java type is compatible with the SQL type. * * At present, the type argument must be either equal to self, or if * self is a Boolean, Character, or any Number, the primitive that * corresponds to that number (i.e. java.lang.Short == short). */ extern bool Type_canReplaceType(Type self, Type type); /* * Translate a given Datum into a jvalue accorging to the type represented * by this instance. */ extern jvalue Type_coerceDatum(Type self, Datum datum); /* * Translate a given Object into a Datum accorging to the type represented * by this instance. */ extern Datum Type_coerceObject(Type self, jobject object); /* * Return a Type based on a Postgres Oid. Creates a new type if * necessary. */ extern Type Type_fromOid(Oid typeId, jobject typeMap); /* * Return a Type from the Oid cache based on a Postgres Oid. This method * returns NULL if no such Type is cached. */ extern Type Type_fromOidCache(Oid typeId); /* * Return a coerce type that can front this type when doing parameter coercion */ extern Type Type_getCoerceIn(Type self, Type other); /* * Return a coerce type that this type can hand over to when doing result value * coercion */ extern Type Type_getCoerceOut(Type self, Type other); /* * Returns true if the type represents the dynamic (any) type. */ extern bool Type_isDynamic(Type self); /* * Returns the type alignment (i.e. pg_type->typalign). */ extern char Type_getAlign(Type self); /* * Returns the type length (i.e. pg_type->typlen). */ extern int16 Type_getLength(Type self); /* * Returns the type length (i.e. pg_type->typlen). */ extern jclass Type_getJavaClass(Type self); /* * Returns true if the type is passed by value (i.e. pg_type->typbyval). */ extern bool Type_isByValue(Type self); /* * Returns true if the invocation will create an out parameter (ResultSet typically) * to collect the return value. If so, the real return value will be a bool. */ extern bool Type_isOutParameter(Type self); /* * Returns the real type for a dynamic type. A non dynamic type will * return itself. */ extern Type Type_getRealType(Type self, Oid realTypeID, jobject typeMap); /* * Return a Type based on a PostgreSQL Oid. If the found * type is a primitive, return it's object corresponcance */ extern Type Type_objectTypeFromOid(Oid typeId, jobject typeMap); /* * Return a Type based on a default SQL type and a java type name. */ extern Type Type_fromJavaType(Oid dfltType, const char* javaTypeName); /* * Returns the Java type name for the Type. */ extern const char* Type_getJavaTypeName(Type self); /* * Returns the JNI signature for the Type. */ extern const char* Type_getJNISignature(Type self); /* * Returns the JNI signature used when returning instances * of this type. */ extern const char* Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr); /* * Returns the array Type. The type is created if it doesn't exist */ extern Type Type_getArrayType(Type self, Oid arrayTypeId); /* * Returns the element Type if this type is an array. */ extern Type Type_getElementType(Type self); /* * Returns the object Type if the type is primitive and NULL if not. */ extern Type Type_getObjectType(Type self); /* * Returns the Oid associated with this type. */ extern Oid Type_getOid(Type self); /* * Returns the TupleDesc associated with this type. */ extern TupleDesc Type_getTupleDesc(Type self, PG_FUNCTION_ARGS); /* * Calls a java method using one of the CallMethodA routines where * corresponds to the type represented by this instance and * coerces the returned value into a Datum. * * The method will set the value pointed to by the wasNull parameter * to true if the Java method returned null. The method expects that * the wasNull parameter is set to false by the caller prior to the * call. */ extern Datum Type_invoke(Type self, jclass clazz, jmethodID method, jvalue* args, PG_FUNCTION_ARGS); /* * Calls a Set Returning Function (SRF). */ extern Datum Type_invokeSRF(Type self, jclass clazz, jmethodID method, jvalue* args, PG_FUNCTION_ARGS); /* * Obtains the Java object that acts as the SRF producer. This instance will be * called once for each row that should be produced. */ extern jobject Type_getSRFProducer(Type self, jclass clazz, jmethodID method, jvalue* args); /* * Obtains the optional Java object that will act as the value collector for * the SRF producer. The collector typically manifest itself as an OUT * parameter of type java.sql.ResultSet in calls to the SRF producer. */ extern jobject Type_getSRFCollector(Type self, PG_FUNCTION_ARGS); /* * Called to determine if the producer will produce another row. */ extern bool Type_hasNextSRF(Type self, jobject producer, jobject collector, jint counter); /* * Converts the next row into a Datum of the expected type. */ extern Datum Type_nextSRF(Type self, jobject producer, jobject collector); /* * Called at the end of an SRF iteration. */ extern void Type_closeSRF(Type self, jobject producer); /* * Function used when obtaining a type based on an Oid * structure. In most cases, this function should return a * singleton. The only current exception from this is the * String since it makes use of functions stored in the * Form_pg_type structure. */ typedef Type (*TypeObtainer)(Oid typeId); /* * Function that can coerce a Datum into a jvalue */ typedef jvalue (*DatumCoercer)(Type, Datum); /* * Function that can coerce a jobject into a Datum */ typedef Datum (*ObjectCoercer)(Type, jobject); /* * Register this type as the default mapping for a postgres type. */ extern void Type_registerType(const char* javaTypeName, Type type); extern void Type_registerType2(Oid typeId, const char* javaTypeName, TypeObtainer obtainer); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/.#HeapTupleHeader.h.1.70000644000014500000120000000212511634451404022553 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_HeapTupleHeader_h #define __pljava_type_HeapTupleHeader_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The HeapTupleHeader java class extends the NativeStruct and provides JNI * access to some of the attributes of the HeapTupleHeader structure. * * @author Thomas Hallgren *****************************************************************/ extern jobject HeapTupleHeader_getStaticTupleDesc(HeapTupleHeader ht); extern jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht); extern jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo); extern void HeapTupleHeader_free(JNIEnv* env, jlong hth); extern void HeapTupleHeader_initialize(void); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/HeapTupleHeader.h~0000644000014500000120000000173611634451404022333 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_HeapTupleHeader_h #define __pljava_type_HeapTupleHeader_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The HeapTupleHeader java class extends the NativeStruct and provides JNI * access to some of the attributes of the HeapTupleHeader structure. * * @author Thomas Hallgren *****************************************************************/ extern jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht); extern jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo); extern void HeapTupleHeader_free(JNIEnv* env, jlong hth); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/TriggerData.h0000644000014500000120000000202311634451404021320 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_TriggerData_h #define __pljava_TriggerData_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /********************************************************************** * The TriggerData java class extends the NativeStruct and provides JNI * access to some of the attributes of the TriggerData structure. * * @author Thomas Hallgren **********************************************************************/ /* * Create the org.postgresql.pljava.TriggerData object. */ extern jobject TriggerData_create(TriggerData* triggerData); /* * Obtains the returned Tuple after trigger has been processed. */ extern HeapTuple TriggerData_getTriggerReturnTuple(jobject jtd, bool* wasNull); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Portal.h0000644000014500000120000000150411634451404020367 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Portal_h #define __pljava_Portal_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The Portal java class extends the NativeStruct and provides JNI * access to some of the attributes of the Portal structure. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.Portal instance */ extern jobject Portal_create(Portal portal); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/String_priv.h0000644000014500000120000000220411634451404021432 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_String_priv_h #define __pljava_type_String_priv_h #include "pljava/type/Type_priv.h" #include "pljava/type/String.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * @author Thomas Hallgren **************************************************************************/ struct String_ { /* * The String "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the String to a Type. */ struct Type_ Type_extension; /* * Transforms text into a Datum. */ FmgrInfo textInput; /* * Transforms datum into text. */ FmgrInfo textOutput; /* * Oid of elment type (if any) */ Oid elementType; }; extern Datum _String_coerceObject(Type self, jobject jstr); extern jvalue _String_coerceDatum(Type self, Datum value); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Array.h0000644000014500000120000000224611634451404020210 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html */ #ifndef __pljava_type_Array_h #define __pljava_type_Array_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /*********************************************************************** * Array related stuff. * * @author Thomas Hallgren * ***********************************************************************/ #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) extern ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType); #else extern ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType, bool withNulls); extern void arraySetNull(bits8* bitmap, int offset, bool flag); extern bool arrayIsNull(const bits8* bitmap, int offset); #endif extern Type Array_fromOid(Oid typeId, Type elementType); extern Type Array_fromOid2(Oid typeId, Type elementType, DatumCoercer coerceDatum, ObjectCoercer coerceObject); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/type/ErrorData.h0000644000014500000120000000167111634451404021016 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_ErrorData_h #define __pljava_ErrorData_h #include "pljava/type/JavaWrapper.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The ErrorData java class represents the native ErrorData. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.internal.ErrorData that represents * the current error obtaind from CopyErrorData(). */ extern jobject ErrorData_getCurrentError(void); /* * Extract the native ErrorData from a Java ErrorData. */ extern ErrorData* ErrorData_getErrorData(jobject jerrorData); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Oid.h0000644000014500000120000000157311634451404017647 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Oid_h #define __pljava_Oid_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Oid java class represents the native Oid. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.Oid instance */ extern jobject Oid_create(Oid oid); /* * Extract the native Oid from a Java Oid. */ extern Oid Oid_getOid(jobject joid); /* * Map a java.sql.Types SQL type to an Oid. */ extern Oid Oid_forSqlType(int sqlType); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/TupleDesc.h0000644000014500000120000000221411634451404021015 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_TupleDesc_h #define __pljava_TupleDesc_h #include "pljava/type/JavaWrapper.h" #ifdef __cplusplus extern "C" { #endif #include /******************************************************************** * The TupleDesc java class extends the NativeStruct and provides JNI * access to some of the attributes of the TupleDesc structure. * * @author Thomas Hallgren ********************************************************************/ /* * Returns the Type of the column at index. If the returned Type * is NULL a Java exception has been initiated and the caller * should return to Java ASAP. */ extern Type TupleDesc_getColumnType(TupleDesc tupleDesc, int index); /* * Create the org.postgresql.pljava.TupleDesc instance */ extern jobject TupleDesc_create(TupleDesc tDesc); extern jobject TupleDesc_internalCreate(TupleDesc tDesc); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/Time.h0000644000014500000120000000222211634451404020022 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_Time_h #define __pljava_type_Time_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * The Time java class represents the native Time or TimeTZ. * * @author Thomas Hallgren *****************************************************************/ #include /** * The PostgreSQL TimeTzADT when configured with --enable-integer-datetimes */ typedef struct { int64 time; /* all time units other than months and * years */ int32 zone; /* numeric time zone, in seconds */ } TimeTzADT_id; /** * The PostgreSQL TimeTzADT when configured without --enable-integer-datetimes */ typedef struct { double time; /* all time units other than months and * years */ int32 zone; /* numeric time zone, in seconds */ } TimeTzADT_dd; #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/String.h0000644000014500000120000000504611634451404020401 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_type_String_h #define __pljava_type_String_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif /************************************************************************** * The String class extends the Type and adds the members necessary to * perform standard Postgres textin/textout conversion. An instance of this * class will be used for all types that are not explicitly mapped. * * The class also has some convenience routings for Java String manipulation. * * @author Thomas Hallgren * **************************************************************************/ extern jclass s_Object_class; extern jclass s_String_class; struct String_; typedef struct String_* String; /* * Create a Java String object from a null terminated string. Conversion is * made from the encoding used by the database into UTF8 used when creating * the Java String. NULL Is accepted as a valid input and will yield * a NULL result. */ extern jstring String_createJavaStringFromNTS(const char* cp); /* * Create a Java String object from a text. Conversion is made from the * encoding used by the database into UTF8 used when creating the Java String. * NULL Is accepted as a valid input and will yield a NULL result. */ extern jstring String_createJavaString(text* cp); /* * Create a null terminated string from a Java String. The UTF8 encoded string * obtained from the Java string is first converted into the encoding used by * the database. * The returned string is allocated using palloc. It's the callers responsability * to free it. */ extern char* String_createNTS(jstring javaString); /* * The UTF8 encoded string obtained from the Java string is first converted into * the encoding used by the database and then appended to the StringInfoData * buffer. */ extern void String_appendJavaString(StringInfoData* buf, jstring javaString); /* * Create a text from a Java String. The UTF8 encoded string obtained from * the Java string is first converted into the encoding used by the * database. * The returned text is allocated using palloc. It's the callers responsability * to free it. */ extern text* String_createText(jstring javaString); extern Type String_obtain(Oid typeId); extern String StringClass_obtain(TypeClass self, Oid typeId); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/type/LargeObject.h0000644000014500000120000000156311634451404021314 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_LargeObject_h #define __pljava_LargeObject_h #include "pljava/type/Type.h" #ifdef __cplusplus extern "C" { #endif #include /***************************************************************** * The LargeObject java class extends the NativeStruct and provides JNI * access to some of the attributes of the LargeObjectDesc structure. * * @author Thomas Hallgren *****************************************************************/ /* * Create the org.postgresql.pljava.LargeObject instance */ extern jobject LargeObject_create(LargeObjectDesc* lo); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/include/pljava/Session.h0000644000014500000120000000121611634451404017570 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Session_h #define __pljava_Session_h #include "pljava/PgObject.h" #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * Session related stuff. * * @author Thomas Hallgren * ***********************************************************************/ #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/Iterator.h0000644000014500000120000000223511634451404017740 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_Iterator_h #define __pljava_Iterator_h #include "pljava/HashMap.h" #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * An Iterator that backed by the given HashMap. The * Iterator will indicate no more entries if the HashMap grows * so that it needs to rehash. * * The Iterator is allocated using the same MemoryContext * as the HashMap. * * @author Thomas Hallgren * ***********************************************************************/ /* * Creates an Iterator. */ extern Iterator Iterator_create(HashMap source); /* * Return true if the Iterator has more entries. */ extern bool Iterator_hasNext(Iterator self); /* * Return the next Entry from the backing HashMap or NULL when * no more entries exists. */ extern Entry Iterator_next(Iterator self); #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/HashMap_priv.h0000644000014500000120000000470311634451404020532 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_HashMap_priv_h #define __pljava_HashMap_priv_h #include "pljava/PgObject_priv.h" #include "pljava/Iterator.h" #include "pljava/HashMap.h" #ifdef __cplusplus extern "C" { #endif /***************************************************************** * Private section of the HashMap class * * @author Thomas Hallgren *****************************************************************/ struct HashKeyClass_; typedef struct HashKeyClass_* HashKeyClass; struct HashKeyClass_ { struct PgObjectClass_ extendedClass; /* * Return the hashCode of self. */ uint32 (*hashCode)(HashKey self); /* * Return true if self is equal to other. */ bool (*equals)(HashKey self, HashKey other); /* * Create a copy of self in MemoryContext ctx. */ HashKey (*clone)(HashKey self, MemoryContext ctx); }; struct HashKey_ { HashKeyClass m_class; }; /* * HashKey for Oid. */ struct OidKey_ { struct HashKey_ HashKey_extension; Oid key; }; typedef struct OidKey_* OidKey; /* * HashKey for an Opaque pointer, uses the pointer itself * as the hash value. */ struct OpaqueKey_ { struct HashKey_ HashKey_extension; void* key; }; typedef struct OpaqueKey_* OpaqueKey; /* * HashKey for strings. */ struct StringKey_ { struct HashKey_ HashKey_extension; /* We preserve the computed hashcode here. */ uint32 hash; const char* key; }; typedef struct StringKey_* StringKey; /* * Default clone method. Allocates a new instance in the given MemoryContext * and copies the orginial HashKey using memcpy and the size stated in the * class. */ extern HashKey _HashKey_clone(HashKey self, MemoryContext ctx); /* * Allocate a HashKeyClass for instances of a specific class. */ extern HashKeyClass HashKeyClass_alloc(const char* className, Size instanceSize, Finalizer finalizer); struct HashMap_ { struct PgObject_ PgObject_extension; Entry* table; uint32 tableSize; uint32 size; }; struct Entry_ { struct PgObject_ PgObject_extension; HashKey key; void* value; Entry next; }; #define Entry_create(ctx) ((Entry)PgObjectClass_allocInstance(s_EntryClass, ctx)) #define HASHSLOT(self, key) (HashKey_hashCode(key) % self->tableSize) #ifdef __cplusplus } /* end of extern "C" declaration */ #endif #endif pljava-1.4.3/src/C/include/pljava/PgObject.h0000644000014500000120000000644411634451404017652 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #ifndef __pljava_PgObject_h #define __pljava_PgObject_h #include "pljava/JNICalls.h" #ifdef __cplusplus extern "C" { #endif /*********************************************************************** * The PgObject class is the abstract base class for all classes in the * Pl/Java system. It provides the basic instance to class mapping, * a virtual destructor, and some convenience methods used when finding * Java classes and their members. * * @author Thomas Hallgren * ***********************************************************************/ struct PgObject_; typedef struct PgObject_* PgObject; struct PgObjectClass_; typedef struct PgObjectClass_* PgObjectClass; /* * The effectiveClassPath is set at initialization time (in Backend.c) */ extern const char* effectiveClassPath; /* * Calles the virtual finalizer and deallocates memory occupided by the * PgObject structure. */ extern void PgObject_free(PgObject object); /* * Misc JNIEnv mappings. */ extern jobject PgObject_callObjectMethod(jobject object, jmethodID field, ...); extern void PgObject_exceptionClear(void); extern void PgObject_exceptionDescribe(void); extern jint PgObject_getIntField(jobject object, jfieldID field); extern jobject PgObject_newGlobalRef(jobject object); /* * Obtains a java class. Calls elog(ERROR, ...) on failure so that * there is no return if the method fails. */ extern jclass PgObject_getJavaClass(const char* className); /* * Obtains a java method. Calls elog(ERROR, ...) on failure so that * there is no return if the method fails. */ extern jmethodID PgObject_getJavaMethod(jclass cls, const char* methodName, const char* signature); /* * Obtains a static java method. Calls elog(ERROR, ...) on failure so that * there is no return if the method fails. */ extern jmethodID PgObject_getStaticJavaMethod(jclass cls, const char* methodName, const char* signature); /* * Obtain a HeapTuple from the system cache and throw an excption * on failure. */ extern HeapTuple PgObject_getValidTuple(int cacheId, Oid tupleId, const char* tupleType); /* * Obtains a java field. Calls elog(ERROR, ...) on failure so that * there is no return if the method fails. */ extern jfieldID PgObject_getJavaField(jclass cls, const char* fieldName, const char* signature); extern jobject PgObject_newJavaObject(jclass cls, jmethodID ctor, ...); /* * Obtains a static java field. Calls elog(ERROR, ...) on failure so that * there is no return if the method fails. */ extern jfieldID PgObject_getStaticJavaField(jclass cls, const char* fieldName, const char* signature); /* * Register native methods with a class. Last entry in the methods array must * have all values set to NULL. */ extern void PgObject_registerNatives(const char* className, JNINativeMethod* methods); extern void PgObject_registerNatives2(jclass cls, JNINativeMethod* methods); /* * Returns the class for the instance. */ extern PgObjectClass PgObject_getClass(PgObject self); /* * Returns the name of the class */ extern const char* PgObjectClass_getName(PgObjectClass self); #ifdef __cplusplus } #endif #endif pljava-1.4.3/src/C/pljava/0000755000014500000120000000000011634451404014351 5ustar johannstaffpljava-1.4.3/src/C/pljava/Makefile0000644000014500000120000001520411634451404016013 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- MODULE_big := pljava SRCDIR := $(MODULEROOT)/$(MODULE_big) INCLDIR := -I$(MODULEROOT)/include -I$(JNIDIR) mkobjs = $(subst $(SRCDIR)/,,$(1:%.c=%.o)) SRCS = $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.c -print) OBJS = $(call mkobjs,$(SRCS)) DLLTOOL_DEFFLAGS := --add-stdcall-alias .PHONY: checkjavahome build pljava-plugin $(OBJS): %.o : $(SRCDIR)/%.c @-mkdir -p $(@D) $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ # Must include PGXS at this point since it defines PORTNAME and VERSION # include $(PGXS) SS_VERSION := $(subst ., ,$(subst devel,.99,$(subst beta,.99,$(subst alpha,.99,$(subst rc,.99,$(subst RC,.99,$(VERSION))))))) PGSQL_MAJOR_VER = $(word 1,$(SS_VERSION)) PGSQL_MINOR_VER = $(word 2,$(SS_VERSION)) PGSQL_PATCH_VER = $(word 3,$(SS_VERSION)) PLJAVA_CPPFLAGS = \ -DPKGLIBDIR=\"$(pkglibdir)\" $(INCLDIR) \ -DPGSQL_MAJOR_VER=$(PGSQL_MAJOR_VER) \ -DPGSQL_MINOR_VER=$(PGSQL_MINOR_VER) \ -DPGSQL_PATCH_VER=$(PGSQL_PATCH_VER) ifeq ($(PORTNAME), win32) DLL_BUILD := 1 else ifeq ($(PORTNAME), darwin) # # override with JAVAVM_FWX_ROOT=/... on command line # if something else is needed # JAVAVM_FWX_ROOT := /System/Library/Frameworks PLJAVA_CPPFLAGS += -I$(JAVAVM_FWX_ROOT)/JavaVM.framework/Headers/ else ifeq ($(PORTNAME), ibmos2) DLL_BUILD := 1 else JRE_INCL := $(PORTNAME) ifeq ($(host_cpu), i686) JRE_CPU := i386 else ifeq ($(host_cpu), i586) JRE_CPU := i386 else ifeq ($(host_cpu), i486) JRE_CPU := i386 else ifeq ($(host_cpu), x86_64) JRE_CPU := amd64 else JRE_CPU := $(host_cpu) endif endif endif endif ifeq ($(JRE_CPU), i386) ifeq ($(findstring -m64, $(CFLAGS)), -m64) JRE_CPU := amd64 endif endif endif endif endif ifdef USE_GCJ PLJAVA_CPPFLAGS += -DGCJ SHLIB_LINK = -lgcj $(PLJAVA_LDFLAGS) ifdef DLL_BUILD SHLIB_LINK += -lws2_32 endif OBJS += pljava_jar.o else pljava-jar = pljava.jar ifeq ($(PORTNAME), darwin) SHLIB_LINK += -L. -framework JavaVM $(PLJAVA_LDFLAGS) else ifdef JAVA_HOME ifeq ($(PORTNAME), win32) JDK_HOME := $(subst \,/,$(JAVA_HOME)) else JDK_HOME := $(JAVA_HOME) endif else ifeq ($(PORTNAME), ibmos2) JAVA_HOME_TEST_RESULT := $(echo "You must set JAVA_HOME") else JAVA_HOME_TEST_RESULT := $(error You must set JAVA_HOME) endif endif ifdef DLL_BUILD ifeq ($(PORTNAME),ibmos2) JRE_INCL := os2 else JRE_INCL := win32 endif ifeq ($(host_cpu), x86_64) JVM_LIB := $(JDK_HOME)/jre/bin/server else JVM_LIB := $(JDK_HOME)/jre/bin/client endif else JRE_LIB := $(JDK_HOME)/jre/lib/$(JRE_CPU) JVM_LIB := $(firstword $(shell /bin/ls -d \ $(JRE_LIB)/client \ $(JRE_LIB)/server \ $(JRE_LIB)/jrockit \ $(JDK_HOME)/jre/bin/j9vm \ 2> /dev/null)) endif PLJAVA_CPPFLAGS += -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/$(JRE_INCL)" SHLIB_LINK += -L. -L"$(JVM_LIB)" -ljvm $(PLJAVA_LDFLAGS) ifdef USE_LD_RPATH ifeq (${USE_LD_RPATH},1) SHLIB_LINK += -Wl,-rpath,${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -Wl,-rpath,$(JVM_LIB:/j9vm=) endif else ifeq (${USE_LD_RPATH},2) SHLIB_LINK += -rpath ${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -rpath $(JVM_LIB:/j9vm=) endif else ifeq (${USE_LD_RPATH},3) SHLIB_LINK += -R${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -R$(JVM_LIB:/j9vm=) endif else ifeq (${USE_LD_RPATH},4) SHLIB_LINK += -R ${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -R $(JVM_LIB:/j9vm=) endif else ifeq (${USE_LD_RPATH},5) SHLIB_LINK += -Wl,-R${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -Wl,-R$(JVM_LIB:/j9vm=) endif else ifeq (${USE_LD_RPATH},6) SHLIB_LINK += -Wl,-R,${JVM_LIB} ifneq ($(JVM_LIB), $(JVM_LIB:/j9vm=)) SHLIB_LINK += \ -Wl,-R,$(JVM_LIB:/j9vm=) endif endif endif endif endif endif endif endif endif endif ifeq ($(JRE_INCL), win32) # # Mingw is a bit special in that we use a "normal" windows # port of the Java Runtime Environment (unless we use gcj # of course). The headers etc. for the JRE is windows style # and contains __int64. GNU compiler doesn't know __int64, # instead it uses long long. # ifndef USE_GCJ PLJAVA_CPPFLAGS += -D__int64='long long' # Don't use higher standard then c89 since this rules out older compilers. We # must use 'long long' however, since JNI defines it. # PLJAVA_CFLAGS += -Wno-long-long endif endif override CPPFLAGS += $(PLJAVA_CPPFLAGS) override CFLAGS += $(PLJAVA_CFLAGS) # shlib naming convention for plugins (no 'lib') # plugin = $(NAME)$(DLSUFFIX) soname = $(plugin) shlib = $(plugin) ifndef USE_GCJ checkjavahome: $(JAVA_HOME_TEST_RESULT) endif ifneq ($(PORTNAME), win32) ifneq ($(PORTNAME), aix) # Normal case # $(plugin): $(OBJS) $(LINK.shared) $(LDFLAGS_SL) $(OBJS) $(SHLIB_LINK) -o $(plugin) else # PORTNAME == aix # AIX case # $(plugin): lib$(NAME).a $(MKLDEXPORT) lib$(NAME).a > $(NAME)$(EXPSUFF) $(COMPILER) $(LDFLAGS_NO_L) $(LDFLAGS_SL) -o $(plugin) $< -Wl,-bE:$(NAME)$(EXPSUFF) $(SHLIB_LINK) endif # PORTNAME == aix else # PORTNAME == win32 # win32 case # $(plugin): $(OBJS) ifndef USE_GCJ ifneq ($(host_cpu), x86_64) $(DLLTOOL) --input-def $(SRCDIR)/jvm.def --kill-at --dllname jvm.dll --output-lib libjvm.dll.a endif endif ifeq ($(host_cpu), x86_64) $(DLLTOOL) --export-all --output-def $(NAME).def $(OBJS) $(DLLTOOL) --dllname $(plugin) $(DLLTOOL_LIBFLAGS) \ --def $(NAME).def gendef - ${JVM_LIB}/jvm.dll > jvm.def ${DLLTOOL} --export-all-symbols --input-def jvm.def \ --output-lib libjvm.dll.a ${CC} -shared -o ${plugin} ${OBJS} ${BE_DLLLIBS} -L. -ljvm else $(DLLTOOL) --export-all $(DLLTOOL_DEFFLAGS) \ --output-def $(NAME).def $(OBJS) $(DLLWRAP) $(LDFLAGS_SL) -o $(plugin) --dllname $(plugin) \ $(DLLWRAP_FLAGS) --def $(NAME).def $(OBJS) \ $(SHLIB_LINK) $(DLLTOOL) --dllname $(plugin) $(DLLTOOL_LIBFLAGS) \ --def $(NAME).def endif endif # PORTNAME == win32 build_all: $(plugin) build_install: build_all installdirs $(INSTALL_SHLIB) $(plugin) $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ $(INSTALL_DATA) ../$(pljava-jar) $(DESTDIR)$(pkglibdir) endif build_uninstall: rm -f $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ rm -f $(DESTDIR)$(pkglibdir)/$(pljava-jar) endif pljava-1.4.3/src/C/pljava/HashMap.c0000644000014500000120000002374211634451404016046 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/HashMap_priv.h" HashKey HashKey_clone(HashKey self, MemoryContext ctx) { return self->m_class->clone(self, ctx); } bool HashKey_equals(HashKey self, HashKey other) { return self->m_class->equals(self, other); } uint32 HashKey_hashCode(HashKey self) { return self->m_class->hashCode(self); } HashKey _HashKey_clone(HashKey self, MemoryContext ctx) { Size sz = ((PgObjectClass)self->m_class)->instanceSize; HashKey clone = (HashKey)MemoryContextAlloc(ctx, sz); memcpy(clone, self, sz); return clone; } HashKeyClass HashKeyClass_alloc(const char* className, Size instanceSize, Finalizer finalizer) { HashKeyClass self = (HashKeyClass)MemoryContextAlloc(TopMemoryContext, sizeof(struct HashKeyClass_)); PgObjectClass_init((PgObjectClass)self, className, instanceSize, finalizer); self->clone = _HashKey_clone; return self; } static HashKeyClass s_OidKeyClass; static HashKeyClass s_StringKeyClass; static HashKeyClass s_OpaqueKeyClass; static PgObjectClass s_EntryClass; static PgObjectClass s_HashMapClass; /* * We use the Oid itself as the hashCode. */ static uint32 _OidKey_hashCode(HashKey self) { return (uint32)((OidKey)self)->key; } /* * Compare with another HashKey. */ static bool _OidKey_equals(HashKey self, HashKey other) { return other->m_class == self->m_class /* Same class */ && ((OidKey)self)->key == ((OidKey)other)->key; } static void OidKey_init(OidKey self, Oid keyVal) { ((HashKey)self)->m_class = s_OidKeyClass; self->key = keyVal; } /* * Create a copy of this StringKey */ static HashKey _StringKey_clone(HashKey self, MemoryContext ctx) { HashKey clone = _HashKey_clone(self, ctx); ((StringKey)clone)->key = MemoryContextStrdup(ctx, ((StringKey)self)->key); return clone; } /* * Compare with another HashKey. */ static bool _StringKey_equals(HashKey self, HashKey other) { return other->m_class == self->m_class /* Same class */ && strcmp(((StringKey)self)->key, ((StringKey)other)->key) == 0; } static void _StringKey_finalize(PgObject self) { pfree((char*)((StringKey)self)->key); } /* * Returns a hash code for this string. The hash code for a * string object is computed as * * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] * * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */ static uint32 _StringKey_hashCode(HashKey self) { const char* val = ((StringKey)self)->key; uint32 h = ((StringKey)self)->hash; if(h == 0) { uint32 c; while((c = *val++) != 0) h = 31 * h + c; ((StringKey)self)->hash = h; } return h; } static void StringKey_init(StringKey self, const char* keyVal) { ((HashKey)self)->m_class = s_StringKeyClass; self->key = keyVal; /* a private copy is made when the key is made permanent */ self->hash = 0; } /* * We use the Oid itself as the hashCode. */ static uint32 _OpaqueKey_hashCode(HashKey self) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = ((OpaqueKey)self)->key; return (uint32)(p2l.longVal >> 3); } /* * Compare with another HashKey. */ static bool _OpaqueKey_equals(HashKey self, HashKey other) { return other->m_class == self->m_class /* Same class */ && ((OpaqueKey)self)->key == ((OpaqueKey)other)->key; } static void OpaqueKey_init(OpaqueKey self, void* keyVal) { ((HashKey)self)->m_class = s_OpaqueKeyClass; self->key = keyVal; } /* * An Entry that holds the binding between the * key and an associated value. */ static PgObjectClass s_EntryClass; HashKey Entry_getKey(Entry self) { return self->key; } void* Entry_getValue(Entry self) { return self->value; } void* Entry_setValue(Entry self, void* value) { void* old = self->value; self->value = value; return old; } static void _Entry_finalize(PgObject self) { PgObject_free((PgObject)((Entry)self)->key); } static void HashMap_rehash(HashMap self, uint32 newCapacity) { Entry* oldTable = self->table; uint32 top = self->tableSize; uint32 idx; Entry* newTable = (Entry*)MemoryContextAlloc(GetMemoryChunkContext(self), newCapacity * sizeof(Entry)); memset(newTable, 0, newCapacity * sizeof(Entry)); self->table = newTable; self->tableSize = newCapacity; /* Move all old slots to the new table */ for(idx = 0; idx < top; ++idx) { Entry e = oldTable[idx]; while(e != 0) { Entry eNext = e->next; HashKey eKey = e->key; uint32 slotNo = HASHSLOT(self, eKey); e->next = newTable[slotNo]; newTable[slotNo] = e; e = eNext; } } pfree(oldTable); } void HashMap_clear(HashMap self) { /* Delete all Entries before deleting the table and * self. */ if(self->size > 0) { Entry* table = self->table; uint32 top = self->tableSize; uint32 idx; for(idx = 0; idx < top; ++idx) { Entry e = table[idx]; table[idx] = 0; while(e != 0) { Entry eNext = e->next; PgObject_free((PgObject)e); e = eNext; } } self->size = 0; } } static void _HashMap_finalize(PgObject self) { /* Delete all Entries before deleting the table and * self. */ HashMap_clear((HashMap)self); pfree(((HashMap)self)->table); } HashMap HashMap_create(uint32 initialCapacity, MemoryContext ctx) { HashMap self; if(ctx == 0) ctx = CurrentMemoryContext; self = (HashMap)PgObjectClass_allocInstance(s_HashMapClass, ctx); if(initialCapacity < 13) initialCapacity = 13; self->table = (Entry*)MemoryContextAlloc(ctx, initialCapacity * sizeof(Entry)); memset(self->table, 0, initialCapacity * sizeof(Entry)); self->tableSize = initialCapacity; self->size = 0; return self; } Iterator HashMap_entries(HashMap self) { return Iterator_create(self); } void* HashMap_get(HashMap self, HashKey key) { Entry slot; slot = self->table[HASHSLOT(self, key)]; while(slot != 0) { if(HashKey_equals(slot->key, key)) break; slot = slot->next; } return (slot == 0) ? 0 : slot->value; } void* HashMap_put(HashMap self, HashKey key, void* value) { void* old = 0; uint32 slotNo = HASHSLOT(self, key); Entry slot = self->table[slotNo]; while(slot != 0) { if(HashKey_equals(slot->key, key)) break; slot = slot->next; } if(slot == 0) { uint32 currSz = self->size; MemoryContext ctx = GetMemoryChunkContext(self); if((currSz + currSz / 2) > self->tableSize) { /* Always double the size. It gives predictable repositioning * of entries (a necessity if an Iteator is created some * time in the future. */ HashMap_rehash(self, self->tableSize * 2); slotNo = HASHSLOT(self, key); } slot = Entry_create(ctx); slot->key = HashKey_clone(key, ctx); /* Create a private copy of the key */ slot->value = value; slot->next = self->table[slotNo]; self->table[slotNo] = slot; self->size++; } else { old = slot->value; slot->value = value; } return old; } void* HashMap_remove(HashMap self, HashKey key) { PgObject old = 0; uint32 slotNo = HASHSLOT(self, key); Entry slot = self->table[slotNo]; while(slot != 0) { if(HashKey_equals(slot->key, key)) break; slot = slot->next; } if(slot != 0) { /* Find the preceding slot and create a bypass for the * found slot, i.e. disconnect it. */ Entry prev = self->table[slotNo]; if(slot == prev) self->table[slotNo] = slot->next; else { while(prev->next != slot) prev = prev->next; prev->next = slot->next; } old = slot->value; self->size--; PgObject_free((PgObject)slot); } return old; } void* HashMap_getByOid(HashMap self, Oid oid) { struct OidKey_ oidKey; OidKey_init(&oidKey, oid); return HashMap_get(self, (HashKey)&oidKey); } void* HashMap_getByOpaque(HashMap self, void* opaque) { struct OpaqueKey_ opaqueKey; OpaqueKey_init(&opaqueKey, opaque); return HashMap_get(self, (HashKey)&opaqueKey); } void* HashMap_getByString(HashMap self, const char* key) { struct StringKey_ stringKey; StringKey_init(&stringKey, key); return HashMap_get(self, (HashKey)&stringKey); } void* HashMap_putByOid(HashMap self, Oid oid, void* value) { struct OidKey_ oidKey; OidKey_init(&oidKey, oid); return HashMap_put(self, (HashKey)&oidKey, value); } void* HashMap_putByOpaque(HashMap self, void* opaque, void* value) { struct OpaqueKey_ opaqueKey; OpaqueKey_init(&opaqueKey, opaque); return HashMap_put(self, (HashKey)&opaqueKey, value); } void* HashMap_putByString(HashMap self, const char* key, void* value) { struct StringKey_ stringKey; StringKey_init(&stringKey, key); return HashMap_put(self, (HashKey)&stringKey, value); } void* HashMap_removeByOid(HashMap self, Oid oid) { struct OidKey_ oidKey; OidKey_init(&oidKey, oid); return HashMap_remove(self, (HashKey)&oidKey); } void* HashMap_removeByOpaque(HashMap self, void* opaque) { struct OpaqueKey_ opaqueKey; OpaqueKey_init(&opaqueKey, opaque); return HashMap_remove(self, (HashKey)&opaqueKey); } void* HashMap_removeByString(HashMap self, const char* key) { struct StringKey_ stringKey; StringKey_init(&stringKey, key); return HashMap_remove(self, (HashKey)&stringKey); } uint32 HashMap_size(HashMap self) { return self->size; } extern void Iterator_initialize(void); extern void HashMap_initialize(void); void HashMap_initialize(void) { Iterator_initialize(); s_EntryClass = PgObjectClass_create("Entry", sizeof(struct Entry_), _Entry_finalize); s_HashMapClass = PgObjectClass_create("HashMap", sizeof(struct HashMap_), _HashMap_finalize); s_OidKeyClass = HashKeyClass_alloc("OidKey", sizeof(struct OidKey_), 0); s_OidKeyClass->hashCode = _OidKey_hashCode; s_OidKeyClass->equals = _OidKey_equals; s_OpaqueKeyClass = HashKeyClass_alloc("OpaqueKey", sizeof(struct OpaqueKey_), 0); s_OpaqueKeyClass->hashCode = _OpaqueKey_hashCode; s_OpaqueKeyClass->equals = _OpaqueKey_equals; s_StringKeyClass = HashKeyClass_alloc("StringKey", sizeof(struct StringKey_), _StringKey_finalize); s_StringKeyClass->hashCode = _StringKey_hashCode; s_StringKeyClass->equals = _StringKey_equals; s_StringKeyClass->clone = _StringKey_clone; } pljava-1.4.3/src/C/pljava/Function.c0000644000014500000120000004770411634451404016316 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/PgObject_priv.h" #include "pljava/backports.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/Function.h" #include "pljava/HashMap.h" #include "pljava/Iterator.h" #include "pljava/type/Oid.h" #include "pljava/type/String.h" #include "pljava/type/TriggerData.h" #include "pljava/type/UDT.h" #include #include #include #include #include #include #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) # define PARAM_OIDS(procStruct) (procStruct)->proargtypes #else # define PARAM_OIDS(procStruct) (procStruct)->proargtypes.values #endif static jclass s_Loader_class; static jclass s_ClassLoader_class; static jmethodID s_Loader_getSchemaLoader; static jmethodID s_Loader_getTypeMap; static jmethodID s_ClassLoader_loadClass; static PgObjectClass s_FunctionClass; struct Function_ { struct PgObject_ PgObject_extension; /** * True if the function is not a volatile function (i.e. STABLE or * IMMUTABLE). This means that the function is not allowed to have * side effects. */ bool readOnly; /** * True if this is a UDT function (input/output/receive/send) */ bool isUDT; /** * Java class, i.e. the UDT class or the class where the static method * is defined. */ jclass clazz; union { struct { /* * True if the function is a multi-call function and hence, will * allocate a memory context of its own. */ bool isMultiCall; /* * The number of parameters */ int32 numParams; /* * Array containing one type for eeach parameter. */ Type* paramTypes; /* * The return type. */ Type returnType; /* * The type map used when mapping parameter and return types. We * need to store it here in order to cope with dynamic types (any * and anyarray) */ jobject typeMap; /* * The static method that should be called. */ jmethodID method; } nonudt; struct { /** * The UDT that this function is associated with */ UDT udt; /** * The UDT function to call */ UDTFunction udtFunction; } udt; } func; }; typedef struct ParseResultData { char* buffer; /* The buffer to pfree once we are done */ const char* returnType; const char* className; const char* methodName; const char* parameters; bool isUDT; } ParseResultData; typedef ParseResultData *ParseResult; static HashMap s_funcMap = 0; static jclass s_Loader_class; static jmethodID s_Loader_getSchemaLoader; static void _Function_finalize(PgObject func) { Function self = (Function)func; JNI_deleteGlobalRef(self->clazz); if(!self->isUDT) { if(self->func.nonudt.typeMap != 0) JNI_deleteGlobalRef(self->func.nonudt.typeMap); if(self->func.nonudt.paramTypes != 0) pfree(self->func.nonudt.paramTypes); } } extern void Function_initialize(void); void Function_initialize(void) { s_funcMap = HashMap_create(59, TopMemoryContext); s_Loader_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/sqlj/Loader")); s_Loader_getSchemaLoader = PgObject_getStaticJavaMethod(s_Loader_class, "getSchemaLoader", "(Ljava/lang/String;)Ljava/lang/ClassLoader;"); s_Loader_getTypeMap = PgObject_getStaticJavaMethod(s_Loader_class, "getTypeMap", "(Ljava/lang/String;)Ljava/util/Map;"); s_ClassLoader_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/ClassLoader")); s_ClassLoader_loadClass = PgObject_getJavaMethod(s_ClassLoader_class, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); s_FunctionClass = PgObjectClass_create("Function", sizeof(struct Function_), _Function_finalize); } static void buildSignature(Function self, StringInfo sign, Type retType, bool alt) { Type* tp = self->func.nonudt.paramTypes; Type* ep = tp + self->func.nonudt.numParams; appendStringInfoChar(sign, '('); while(tp < ep) appendStringInfoString(sign, Type_getJNISignature(*tp++)); if(!self->func.nonudt.isMultiCall && Type_isOutParameter(retType)) appendStringInfoString(sign, Type_getJNISignature(retType)); appendStringInfoChar(sign, ')'); appendStringInfoString(sign, Type_getJNIReturnSignature(retType, self->func.nonudt.isMultiCall, alt)); } static void parseParameters(Function self, Oid* dfltIds, const char* paramDecl) { char c; int idx = 0; int top = self->func.nonudt.numParams; bool lastIsOut = !self->func.nonudt.isMultiCall && Type_isOutParameter(self->func.nonudt.returnType); StringInfoData sign; initStringInfo(&sign); for(;;) { if(idx >= top) { if(!(lastIsOut && idx == top)) ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("To many parameters - expected %d ", top))); } c = *paramDecl++; if(c == 0 || c == ',') { Type deflt = (idx == top) ? self->func.nonudt.returnType : self->func.nonudt.paramTypes[idx]; const char* jtName = Type_getJavaTypeName(deflt); if(strcmp(jtName, sign.data) != 0) { Oid did; Type repl; if(idx == top) /* * Last parameter is the OUT parameter. It has no corresponding * entry in the dfltIds array. */ did = InvalidOid; else did = dfltIds[idx]; repl = Type_fromJavaType(did, sign.data); if(!Type_canReplaceType(repl, deflt)) repl = Type_getCoerceIn(repl, deflt); if(idx == top) self->func.nonudt.returnType = repl; else self->func.nonudt.paramTypes[idx] = repl; } pfree(sign.data); ++idx; if(c == 0) { /* * We are done. */ if(lastIsOut) ++top; if(idx != top) ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("To few parameters - expected %d ", top))); break; } /* * Initialize next parameter. */ initStringInfo(&sign); } else appendStringInfoChar(&sign, c); } } static char* getAS(HeapTuple procTup, char** epHolder) { char c; char* cp1; char* cp2; char* bp; bool atStart = true; bool passedFirst = false; bool isNull = false; Datum tmp = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isNull); if(isNull) { ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("'AS' clause of Java function cannot be NULL"))); } bp = pstrdup(DatumGetCString(DirectFunctionCall1(textout, tmp))); /* Strip all whitespace except the first one if it occures after * some alpha numeric characers and before some other alpha numeric * characters. We insert a '=' when that happens since it delimits * the return value from the method name. */ cp1 = cp2 = bp; while((c = *cp1++) != 0) { if(isspace(c)) { if(atStart || passedFirst) continue; while((c = *cp1++) != 0) if(!isspace(c)) break; if(c == 0) break; if(isalpha(c)) *cp2++ = '='; passedFirst = true; } atStart = false; if(!isalnum(c)) passedFirst = true; *cp2++ = c; } *cp2 = 0; *epHolder = cp2; return bp; } static void parseUDT(ParseResult info, char* bp, char* ep) { char* ip = ep - 1; while(ip > bp && *ip != ']') --ip; if(ip == bp) { ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("Missing ending ']' in UDT declaration"))); } *ip = 0; /* Terminate class name */ info->className = bp; info->methodName = ip + 1; info->isUDT = true; } static void parseFunction(ParseResult info, HeapTuple procTup) { /* The user's function definition must be the fully * qualified name of a java method short of parameter * signature. */ char* ip; char* ep; char* bp = getAS(procTup, &ep); info->buffer = bp; /* The AS clause can have two formats * * "." [ "(" ["," ... ] ")" ] * or * "UDT" "[" "]" * where is one of "input", "output", "receive" or "send" */ if(ep - bp >= 4 && strncasecmp(bp, "udt[", 4) == 0) { parseUDT(info, bp + 4, ep); return; } info->isUDT = false; /* Scan backwards from ep. */ ip = ep - 1; if(*ip == ')') { /* We have an explicit parameter type declaration */ *ip-- = 0; while(ip > bp && *ip != '(') --ip; if(ip == bp) { ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unbalanced parenthesis"))); } info->parameters = ip + 1; *ip-- = 0; } /* Find last '.' occurrence. */ while(ip > bp && *ip != '.') --ip; if(ip == bp) { ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("Did not find ."))); } info->methodName = ip + 1; *ip = 0; /* Check if we have a return type declaration */ while(--ip > bp) { if(*ip == '=') { info->className = ip + 1; *ip = 0; break; } } if(info->className != 0) info->returnType = bp; else info->className = bp; elog(DEBUG3, "className = '%s', methodName = '%s', parameters = '%s', returnType = '%s'", info->className == 0 ? "null" : info->className, info->methodName == 0 ? "null" : info->methodName, info->parameters == 0 ? "null" : info->parameters, info->returnType == 0 ? "null" : info->returnType); } static jstring getSchemaName(int namespaceOid) { HeapTuple nspTup = PgObject_getValidTuple(NAMESPACEOID, namespaceOid, "namespace"); Form_pg_namespace nspStruct = (Form_pg_namespace)GETSTRUCT(nspTup); jstring schemaName = String_createJavaStringFromNTS(NameStr(nspStruct->nspname)); ReleaseSysCache(nspTup); return schemaName; } static void setupTriggerParams(Function self, ParseResult info) { if(info->parameters != 0) ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("Triggers can not have a java parameter declaration"))); self->func.nonudt.returnType = Type_fromJavaType(InvalidOid, "void"); /* Parameters are not used when calling triggers. */ self->func.nonudt.numParams = 1; self->func.nonudt.paramTypes = (Type*)MemoryContextAlloc(GetMemoryChunkContext(self), sizeof(Type)); self->func.nonudt.paramTypes[0] = Type_fromJavaType(InvalidOid, "org.postgresql.pljava.TriggerData"); } static void setupUDT(Function self, ParseResult info, Form_pg_proc procStruct) { Oid udtId = 0; HeapTuple typeTup; Form_pg_type pgType; if(strcasecmp("input", info->methodName) == 0) { self->func.udt.udtFunction = UDT_input; udtId = procStruct->prorettype; } else if(strcasecmp("output", info->methodName) == 0) { self->func.udt.udtFunction = UDT_output; udtId = PARAM_OIDS(procStruct)[0]; } else if(strcasecmp("receive", info->methodName) == 0) { self->func.udt.udtFunction = UDT_receive; udtId = procStruct->prorettype; } else if(strcasecmp("send", info->methodName) == 0) { self->func.udt.udtFunction = UDT_send; udtId = PARAM_OIDS(procStruct)[0]; } else { ereport(ERROR, ( errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unknown UDT function %s", info->methodName))); } typeTup = PgObject_getValidTuple(TYPEOID, udtId, "type"); pgType = (Form_pg_type)GETSTRUCT(typeTup); self->func.udt.udt = UDT_registerUDT(self->clazz, udtId, pgType, 0, true); ReleaseSysCache(typeTup); } static void setupFunctionParams(Function self, ParseResult info, Form_pg_proc procStruct, PG_FUNCTION_ARGS) { Oid* paramOids; MemoryContext ctx = GetMemoryChunkContext(self); int32 top = (int32)procStruct->pronargs;; self->func.nonudt.numParams = top; self->func.nonudt.isMultiCall = procStruct->proretset; self->func.nonudt.returnType = Type_fromOid(procStruct->prorettype, self->func.nonudt.typeMap); if(top > 0) { int idx; paramOids = PARAM_OIDS(procStruct); self->func.nonudt.paramTypes = (Type*)MemoryContextAlloc(ctx, top * sizeof(Type)); for(idx = 0; idx < top; ++idx) self->func.nonudt.paramTypes[idx] = Type_fromOid(paramOids[idx], self->func.nonudt.typeMap); } else { self->func.nonudt.paramTypes = 0; paramOids = 0; } if(info->parameters != 0) parseParameters(self, paramOids, info->parameters); if(info->returnType != 0) { const char* jtName = Type_getJavaTypeName(self->func.nonudt.returnType); if(strcmp(jtName, info->returnType) != 0) { Type repl = Type_fromJavaType(Type_getOid(self->func.nonudt.returnType), info->returnType); if(!Type_canReplaceType(repl, self->func.nonudt.returnType)) repl = Type_getCoerceOut(repl, self->func.nonudt.returnType); self->func.nonudt.returnType = repl; } } } static void Function_init(Function self, ParseResult info, Form_pg_proc procStruct, PG_FUNCTION_ARGS) { StringInfoData sign; jobject loader; jstring className; /* Get the ClassLoader for the schema that this function belongs to */ jstring schemaName = getSchemaName(procStruct->pronamespace); /* Install the type map for the current schema. This must be done ASAP since * many other functions (including obtaining the loader) depends on it. */ jobject tmp = JNI_callStaticObjectMethod(s_Loader_class, s_Loader_getTypeMap, schemaName); self->func.nonudt.typeMap = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); self->readOnly = (procStruct->provolatile != PROVOLATILE_VOLATILE); self->isUDT = info->isUDT; currentInvocation->function = self; /* Get the ClassLoader for the schema that this function belongs to */ loader = JNI_callStaticObjectMethod(s_Loader_class, s_Loader_getSchemaLoader, schemaName); JNI_deleteLocalRef(schemaName); elog(DEBUG1, "Loading class %s", info->className); className = String_createJavaStringFromNTS(info->className); tmp = JNI_callObjectMethod(loader, s_ClassLoader_loadClass, className); JNI_deleteLocalRef(loader); JNI_deleteLocalRef(className); self->clazz = (jclass)JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); if(self->isUDT) { setupUDT(self, info, procStruct); return; } if(CALLED_AS_TRIGGER(fcinfo)) { self->func.nonudt.typeMap = 0; setupTriggerParams(self, info); } else { setupFunctionParams(self, info, procStruct, fcinfo); } initStringInfo(&sign); buildSignature(self, &sign, self->func.nonudt.returnType, false); elog(DEBUG1, "Obtaining method %s.%s %s", info->className, info->methodName, sign.data); self->func.nonudt.method = JNI_getStaticMethodIDOrNull(self->clazz, info->methodName, sign.data); if(self->func.nonudt.method == 0) { char* origSign = sign.data; Type altType = 0; Type realRetType = self->func.nonudt.returnType; elog(DEBUG1, "Method %s.%s %s not found", info->className, info->methodName, origSign); if(Type_isPrimitive(self->func.nonudt.returnType)) { /* * One valid reason for not finding the method is when * the return type used in the signature is a primitive and * the true return type of the method is the object class that * corresponds to that primitive. */ altType = Type_getObjectType(self->func.nonudt.returnType); realRetType = altType; } else if(strcmp(Type_getJavaTypeName(self->func.nonudt.returnType), "java.sql.ResultSet") == 0) { /* * Another reason might be that we expected a ResultSetProvider * but the implementation returns a ResultSetHandle that needs to be * wrapped. The wrapping is internal so we retain the original * return type anyway. */ altType = realRetType; } if(altType != 0) { JNI_exceptionClear(); initStringInfo(&sign); buildSignature(self, &sign, altType, true); elog(DEBUG1, "Obtaining method %s.%s %s", info->className, info->methodName, sign.data); self->func.nonudt.method = JNI_getStaticMethodIDOrNull(self->clazz, info->methodName, sign.data); if(self->func.nonudt.method != 0) self->func.nonudt.returnType = realRetType; } if(self->func.nonudt.method == 0) PgObject_throwMemberError(self->clazz, info->methodName, origSign, true, true); if(sign.data != origSign) pfree(origSign); } pfree(sign.data); } static Function Function_create(PG_FUNCTION_ARGS) { ParseResultData info; Function self = (Function)PgObjectClass_allocInstance(s_FunctionClass, TopMemoryContext); HeapTuple procTup = PgObject_getValidTuple(PROCOID, fcinfo->flinfo->fn_oid, "function"); memset(&info, 0, sizeof(ParseResultData)); parseFunction(&info, procTup); Function_init(self, &info, (Form_pg_proc)GETSTRUCT(procTup), fcinfo); pfree(info.buffer); ReleaseSysCache(procTup); return self; } Function Function_getFunction(PG_FUNCTION_ARGS) { Oid funcOid = fcinfo->flinfo->fn_oid; Function func = (Function)HashMap_getByOid(s_funcMap, funcOid); if(func == 0) { func = Function_create(fcinfo); HashMap_putByOid(s_funcMap, funcOid, func); } return func; } jobject Function_getTypeMap(Function self) { return self->func.nonudt.typeMap; } static bool Function_inUse(Function func) { Invocation* ic = currentInvocation; while(ic != 0) { if(ic->function == func) return true; ic = ic->previous; } return false; } void Function_clearFunctionCache(void) { Entry entry; HashMap oldMap = s_funcMap; Iterator itor = Iterator_create(oldMap); s_funcMap = HashMap_create(59, TopMemoryContext); while((entry = Iterator_next(itor)) != 0) { Function func = (Function)Entry_getValue(entry); if(func != 0) { if(Function_inUse(func)) { /* This is the replace_jar function or similar. Just * move it to the new map. */ HashMap_put(s_funcMap, Entry_getKey(entry), func); } else { Entry_setValue(entry, 0); PgObject_free((PgObject)func); } } } PgObject_free((PgObject)itor); PgObject_free((PgObject)oldMap); } Datum Function_invoke(Function self, PG_FUNCTION_ARGS) { Datum retVal; int32 top; jvalue* args; Type invokerType; fcinfo->isnull = false; currentInvocation->function = self; if(self->isUDT) return self->func.udt.udtFunction(self->func.udt.udt, fcinfo); if(self->func.nonudt.isMultiCall && SRF_IS_FIRSTCALL()) Invocation_assertDisconnect(); top = self->func.nonudt.numParams; /* Leave room for one extra parameter. Functions that returns unmapped * composite types must have a single row ResultSet as an OUT parameter. */ args = (jvalue*)palloc((top + 1) * sizeof(jvalue)); invokerType = self->func.nonudt.returnType; if(top > 0) { int32 idx; Type* types = self->func.nonudt.paramTypes; /* a class loader or other mechanism might have connected already. This * connection must be dropped since its parent context is wrong. */ if(Type_isDynamic(invokerType)) invokerType = Type_getRealType(invokerType, get_fn_expr_rettype(fcinfo->flinfo), self->func.nonudt.typeMap); for(idx = 0; idx < top; ++idx) { if(PG_ARGISNULL(idx)) /* * Set this argument to zero (or null in case of object) */ args[idx].j = 0L; else { Type paramType = types[idx]; if(Type_isDynamic(paramType)) paramType = Type_getRealType(paramType, get_fn_expr_argtype(fcinfo->flinfo, idx), self->func.nonudt.typeMap); args[idx] = Type_coerceDatum(paramType, PG_GETARG_DATUM(idx)); } } } retVal = self->func.nonudt.isMultiCall ? Type_invokeSRF(invokerType, self->clazz, self->func.nonudt.method, args, fcinfo) : Type_invoke(invokerType, self->clazz, self->func.nonudt.method, args, fcinfo); pfree(args); return retVal; } Datum Function_invokeTrigger(Function self, PG_FUNCTION_ARGS) { jvalue arg; Datum ret; arg.l = TriggerData_create((TriggerData*)fcinfo->context); if(arg.l == 0) return 0; currentInvocation->function = self; Type_invoke(self->func.nonudt.returnType, self->clazz, self->func.nonudt.method, &arg, fcinfo); fcinfo->isnull = false; if(JNI_exceptionCheck()) ret = 0; else { /* A new Tuple may or may not be created here. If it is, ensure that * it is created in the upper SPI context. */ MemoryContext currCtx = Invocation_switchToUpperContext(); ret = PointerGetDatum(TriggerData_getTriggerReturnTuple(arg.l, &fcinfo->isnull)); /* Triggers are not allowed to set the fcinfo->isnull, even when * they return null. */ fcinfo->isnull = false; MemoryContextSwitchTo(currCtx); } JNI_deleteLocalRef(arg.l); return ret; } bool Function_isCurrentReadOnly(void) { /* function will be 0 during resolve of class and java function. At * that time, no updates are allowed (or needed). */ return (currentInvocation->function == 0) ? true : currentInvocation->function->readOnly; } pljava-1.4.3/src/C/pljava/jvm.def0000644000014500000120000000012411634451404015622 0ustar johannstaffEXPORTS JNI_CreateJavaVM@12 JNI_GetDefaultJavaVMInitArgs@4 JNI_GetCreatedJavaVMs@12 pljava-1.4.3/src/C/pljava/SPI.c0000644000014500000120000001120511634451404015147 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "org_postgresql_pljava_internal_SPI.h" #include "pljava/backports.h" #include "pljava/SPI.h" #include "pljava/Invocation.h" #include "pljava/Exception.h" #include "pljava/type/String.h" #include "pljava/type/TupleTable.h" #include Savepoint* infant = 0; extern void SPI_initialize(void); void SPI_initialize(void) { JNINativeMethod methods[] = { { "_exec", "(JLjava/lang/String;I)I", Java_org_postgresql_pljava_internal_SPI__1exec }, { "_getProcessed", "()I", Java_org_postgresql_pljava_internal_SPI__1getProcessed }, { "_getResult", "()I", Java_org_postgresql_pljava_internal_SPI__1getResult }, { "_getTupTable", "(Lorg/postgresql/pljava/internal/TupleDesc;)Lorg/postgresql/pljava/internal/TupleTable;", Java_org_postgresql_pljava_internal_SPI__1getTupTable }, { "_freeTupTable", "()V", Java_org_postgresql_pljava_internal_SPI__1freeTupTable }, { 0, 0, 0 }}; PgObject_registerNatives("org/postgresql/pljava/internal/SPI", methods); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_SPI * Method: _exec * Signature: (JLjava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_SPI__1exec(JNIEnv* env, jclass cls, jlong threadId, jstring cmd, jint count) { jint result = 0; BEGIN_NATIVE char* command = String_createNTS(cmd); if(command != 0) { STACK_BASE_VARS STACK_BASE_PUSH(threadId) PG_TRY(); { Invocation_assertConnect(); result = (jint)SPI_exec(command, (int)count); if(result < 0) Exception_throwSPI("exec", result); } PG_CATCH(); { Exception_throw_ERROR("SPI_exec"); } PG_END_TRY(); pfree(command); STACK_BASE_POP() } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_SPI * Method: _getProcessed * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_SPI__1getProcessed(JNIEnv* env, jclass cls) { return (jint)SPI_processed; } /* * Class: org_postgresql_pljava_internal_SPI * Method: _getResult * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_SPI__1getResult(JNIEnv* env, jclass cls) { return (jint)SPI_result; } /* * Class: org_postgresql_pljava_internal_SPI * Method: _getTupTable * Signature: (Lorg/postgresql/pljava/internal/TupleDesc;)Lorg/postgresql/pljava/internal/TupleTable; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_SPI__1getTupTable(JNIEnv* env, jclass cls, jobject td) { jobject tupleTable = 0; if(SPI_tuptable != 0) { BEGIN_NATIVE tupleTable = TupleTable_create(SPI_tuptable, td); END_NATIVE } return tupleTable; } /* * Class: org_postgresql_pljava_internal_SPI * Method: _freeTupTable * Signature: ()V; */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_SPI__1freeTupTable(JNIEnv* env, jclass cls) { if(SPI_tuptable != 0) { BEGIN_NATIVE SPI_freetuptable(SPI_tuptable); SPI_tuptable = 0; END_NATIVE } } static void assertXid(SubTransactionId xid) { if(xid != GetCurrentSubTransactionId()) { /* Oops. Rollback to top level transaction. */ ereport(ERROR, ( errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION), errmsg("Subtransaction mismatch at txlevel %d", GetCurrentTransactionNestLevel()))); } } Savepoint* SPI_setSavepoint(const char* name) { Savepoint* sp = (Savepoint*)palloc(sizeof(Savepoint) + strlen(name)); Invocation_assertConnect(); sp->nestingLevel = GetCurrentTransactionNestLevel() + 1; strcpy(sp->name, name); infant = sp; BeginInternalSubTransaction(sp->name); infant = 0; sp->xid = GetCurrentSubTransactionId(); return sp; } void SPI_releaseSavepoint(Savepoint* sp) { while(sp->nestingLevel < GetCurrentTransactionNestLevel()) ReleaseCurrentSubTransaction(); if(sp->nestingLevel == GetCurrentTransactionNestLevel()) { assertXid(sp->xid); ReleaseCurrentSubTransaction(); } pfree(sp); } void SPI_rollbackSavepoint(Savepoint* sp) { while(sp->nestingLevel < GetCurrentTransactionNestLevel()) RollbackAndReleaseCurrentSubTransaction(); if(sp->nestingLevel == GetCurrentTransactionNestLevel()) { assertXid(sp->xid); RollbackAndReleaseCurrentSubTransaction(); } SPI_restore_connection(); pfree(sp); } #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 1) char* SPI_getnspname(Relation rel) { return get_namespace_name(RelationGetNamespace(rel)); } #endif pljava-1.4.3/src/C/pljava/ExecutionPlan.c0000644000014500000120000001677611634451404017314 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_ExecutionPlan.h" #include "pljava/Invocation.h" #include "pljava/Exception.h" #include "pljava/Function.h" #include "pljava/SPI.h" #include "pljava/type/Oid.h" #include "pljava/type/Portal.h" #include "pljava/type/String.h" /* Class 07 - Dynamic SQL Error */ #define ERRCODE_PARAMETER_COUNT_MISMATCH MAKE_SQLSTATE('0','7', '0','0','1') /* Make this datatype available to the postgres system. */ extern void ExecutionPlan_initialize(void); void ExecutionPlan_initialize(void) { JNINativeMethod methods[] = { { "_cursorOpen", "(JJLjava/lang/String;[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Portal;", Java_org_postgresql_pljava_internal_ExecutionPlan__1cursorOpen }, { "_isCursorPlan", "(J)Z", Java_org_postgresql_pljava_internal_ExecutionPlan__1isCursorPlan }, { "_execute", "(JJ[Ljava/lang/Object;I)I", Java_org_postgresql_pljava_internal_ExecutionPlan__1execute }, { "_prepare", "(JLjava/lang/String;[Lorg/postgresql/pljava/internal/Oid;)J", Java_org_postgresql_pljava_internal_ExecutionPlan__1prepare }, { "_invalidate", "(J)V", Java_org_postgresql_pljava_internal_ExecutionPlan__1invalidate }, { 0, 0, 0 } }; PgObject_registerNatives("org/postgresql/pljava/internal/ExecutionPlan", methods); } static bool coerceObjects(void* ePlan, jobjectArray jvalues, Datum** valuesPtr, char** nullsPtr) { char* nulls = 0; Datum* values = 0; int count = SPI_getargcount(ePlan); if((jvalues == 0 && count != 0) || (jvalues != 0 && count != JNI_getArrayLength(jvalues))) { Exception_throw(ERRCODE_PARAMETER_COUNT_MISMATCH, "Number of values does not match number of arguments for prepared plan"); return false; } if(count > 0) { int idx; jobject typeMap = Invocation_getTypeMap(); values = (Datum*)palloc(count * sizeof(Datum)); for(idx = 0; idx < count; ++idx) { Oid typeId = SPI_getargtypeid(ePlan, idx); Type type = Type_fromOid(typeId, typeMap); jobject value = JNI_getObjectArrayElement(jvalues, idx); if(value != 0) { values[idx] = Type_coerceObject(type, value); JNI_deleteLocalRef(value); } else { values[idx] = 0; if(nulls == 0) { nulls = (char*)palloc(count+1); memset(nulls, ' ', count); /* all values non-null initially */ nulls[count] = 0; *nullsPtr = nulls; } nulls[idx] = 'n'; } } } *valuesPtr = values; *nullsPtr = nulls; return true; } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_ExecutionPlan * Method: _cursorOpen * Signature: (JJLjava/lang/String;[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Portal; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_ExecutionPlan__1cursorOpen(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jstring cursorName, jobjectArray jvalues) { jobject jportal = 0; if(_this != 0) { BEGIN_NATIVE STACK_BASE_VARS STACK_BASE_PUSH(threadId) PG_TRY(); { Ptr2Long p2l; Datum* values = 0; char* nulls = 0; p2l.longVal = _this; if(coerceObjects(p2l.ptrVal, jvalues, &values, &nulls)) { Portal portal; char* name = 0; if(cursorName != 0) name = String_createNTS(cursorName); Invocation_assertConnect(); portal = SPI_cursor_open( name, p2l.ptrVal, values, nulls, Function_isCurrentReadOnly()); if(name != 0) pfree(name); if(values != 0) pfree(values); if(nulls != 0) pfree(nulls); jportal = Portal_create(portal); } } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_open"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return jportal; } /* * Class: org_postgresql_pljava_internal_ExecutionPlan * Method: _isCursorPlan * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_ExecutionPlan__1isCursorPlan(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; p2l.longVal = _this; Invocation_assertConnect(); result = (jboolean)SPI_is_cursor_plan(p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("SPI_is_cursor_plan"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_ExecutionPlan * Method: _execute * Signature: (JJ[Ljava/lang/Object;I)V */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ExecutionPlan__1execute(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jobjectArray jvalues, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE STACK_BASE_VARS STACK_BASE_PUSH(threadId) PG_TRY(); { Ptr2Long p2l; Datum* values = 0; char* nulls = 0; p2l.longVal = _this; if(coerceObjects(p2l.ptrVal, jvalues, &values, &nulls)) { Invocation_assertConnect(); result = (jint)SPI_execute_plan( p2l.ptrVal, values, nulls, Function_isCurrentReadOnly(), (int)count); if(result < 0) Exception_throwSPI("execute_plan", result); if(values != 0) pfree(values); if(nulls != 0) pfree(nulls); } } PG_CATCH(); { Exception_throw_ERROR("SPI_execute_plan"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_ExecutionPlan * Method: _prepare * Signature: (JLjava/lang/String;[Lorg/postgresql/pljava/internal/Oid;)J; */ JNIEXPORT jlong JNICALL Java_org_postgresql_pljava_internal_ExecutionPlan__1prepare(JNIEnv* env, jclass clazz, jlong threadId, jstring jcmd, jobjectArray paramTypes) { jlong result = 0; BEGIN_NATIVE STACK_BASE_VARS STACK_BASE_PUSH(threadId) PG_TRY(); { char* cmd; void* ePlan; int paramCount = 0; Oid* paramOids = 0; if(paramTypes != 0) { paramCount = JNI_getArrayLength(paramTypes); if(paramCount > 0) { int idx; paramOids = (Oid*)palloc(paramCount * sizeof(Oid)); for(idx = 0; idx < paramCount; ++idx) { jobject joid = JNI_getObjectArrayElement(paramTypes, idx); paramOids[idx] = Oid_getOid(joid); JNI_deleteLocalRef(joid); } } } cmd = String_createNTS(jcmd); Invocation_assertConnect(); ePlan = SPI_prepare(cmd, paramCount, paramOids); pfree(cmd); if(ePlan == 0) Exception_throwSPI("prepare", SPI_result); else { Ptr2Long p2l; /* Make the plan durable */ p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = SPI_saveplan(ePlan); result = p2l.longVal; SPI_freeplan(ePlan); /* Get rid of the original, nobody can see it anymore */ } } PG_CATCH(); { Exception_throw_ERROR("SPI_prepare"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ExecutionPlan * Method: _invalidate * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_ExecutionPlan__1invalidate(JNIEnv* env, jclass clazz, jlong _this) { /* The plan is not cached as a normal JavaHandle since its made * persistent. */ if(_this != 0) { BEGIN_NATIVE_NO_ERRCHECK PG_TRY(); { Ptr2Long p2l; p2l.longVal = _this; SPI_freeplan(p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("SPI_freeplan"); } PG_END_TRY(); END_NATIVE } } pljava-1.4.3/src/C/pljava/SubXactListener.c0000644000014500000120000000630211634451404017575 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/SPI.h" #include "org_postgresql_pljava_internal_SubXactListener.h" #include static jclass s_SubXactListener_class; static jmethodID s_SubXactListener_onStart; static jmethodID s_SubXactListener_onCommit; static jmethodID s_SubXactListener_onAbort; static void subXactCB(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void* arg) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = arg; switch(event) { case SUBXACT_EVENT_START_SUB: { Ptr2Long infant2l; infant->xid = mySubid; infant2l.longVal = 0L; /* ensure that the rest is zeroed out */ infant2l.ptrVal = infant; JNI_callStaticVoidMethod(s_SubXactListener_class, s_SubXactListener_onStart, p2l.longVal, infant2l.longVal, parentSubid); } break; case SUBXACT_EVENT_COMMIT_SUB: JNI_callStaticVoidMethod(s_SubXactListener_class, s_SubXactListener_onCommit, p2l.longVal, mySubid, parentSubid); break; case SUBXACT_EVENT_ABORT_SUB: JNI_callStaticVoidMethod(s_SubXactListener_class, s_SubXactListener_onAbort, p2l.longVal, mySubid, parentSubid); } } extern void SubXactListener_initialize(void); void SubXactListener_initialize(void) { JNINativeMethod methods[] = { { "_register", "(J)V", Java_org_postgresql_pljava_internal_SubXactListener__1register }, { "_unregister", "(J)V", Java_org_postgresql_pljava_internal_SubXactListener__1unregister }, { 0, 0, 0 }}; PgObject_registerNatives("org/postgresql/pljava/internal/SubXactListener", methods); s_SubXactListener_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/SubXactListener")); s_SubXactListener_onAbort = PgObject_getStaticJavaMethod(s_SubXactListener_class, "onAbort", "(JII)V"); s_SubXactListener_onCommit = PgObject_getStaticJavaMethod(s_SubXactListener_class, "onCommit", "(JII)V"); s_SubXactListener_onStart = PgObject_getStaticJavaMethod(s_SubXactListener_class, "onStart", "(JJI)V"); } /* * Class: org_postgresql_pljava_internal_SubXactListener * Method: _register * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_SubXactListener__1register(JNIEnv* env, jclass cls, jlong listenerId) { BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; p2l.longVal = listenerId; RegisterSubXactCallback(subXactCB, p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("RegisterSubXactCallback"); } PG_END_TRY(); END_NATIVE } /* * Class: org_postgresql_pljava_internal_SubXactListener * Method: _unregister * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_SubXactListener__1unregister(JNIEnv* env, jclass cls, jlong listenerId) { BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; p2l.longVal = listenerId; UnregisterSubXactCallback(subXactCB, p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("UnregisterSubXactCallback"); } PG_END_TRY(); END_NATIVE } pljava-1.4.3/src/C/pljava/Invocation.c0000644000014500000120000001510411634451404016627 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "org_postgresql_pljava_jdbc_Invocation.h" #include "pljava/Invocation.h" #include "pljava/Function.h" #include "pljava/PgObject.h" #include "pljava/JNICalls.h" #include "pljava/Backend.h" #define LOCAL_FRAME_SIZE 128 struct CallLocal_ { /** * Pointer to the call local structure. */ void* pointer; /** * The invocation where this CallLocal was allocated */ Invocation* invocation; /** * Next CallLocal in a double linked list */ CallLocal* next; /** * Previous CallLocal in a double linked list */ CallLocal* prev; }; static jmethodID s_Invocation_onExit; static unsigned int s_callLevel = 0; Invocation* currentInvocation; extern void Invocation_initialize(void); void Invocation_initialize(void) { jclass cls; JNINativeMethod invocationMethods[] = { { "_getCurrent", "()Lorg/postgresql/pljava/jdbc/Invocation;", Java_org_postgresql_pljava_jdbc_Invocation__1getCurrent }, { "_getNestingLevel", "()I", Java_org_postgresql_pljava_jdbc_Invocation__1getNestingLevel }, { "_clearErrorCondition", "()V", Java_org_postgresql_pljava_jdbc_Invocation__1clearErrorCondition }, { "_register", "()V", Java_org_postgresql_pljava_jdbc_Invocation__1register }, { 0, 0, 0 } }; cls = PgObject_getJavaClass("org/postgresql/pljava/jdbc/Invocation"); PgObject_registerNatives2(cls, invocationMethods); s_Invocation_onExit = PgObject_getJavaMethod(cls, "onExit", "()V"); JNI_deleteLocalRef(cls); } void Invocation_assertConnect(void) { if(!currentInvocation->hasConnected) { SPI_connect(); currentInvocation->hasConnected = true; } } void Invocation_assertDisconnect(void) { if(currentInvocation->hasConnected) { SPI_finish(); currentInvocation->hasConnected = false; } } jobject Invocation_getTypeMap(void) { Function f = currentInvocation->function; return f == 0 ? 0 : Function_getTypeMap(f); } void Invocation_pushBootContext(Invocation* ctx) { ctx->invocation = 0; ctx->function = 0; ctx->trusted = false; ctx->hasConnected = false; ctx->upperContext = CurrentMemoryContext; ctx->errorOccured = false; ctx->inExprContextCB = false; ctx->previous = 0; ctx->callLocals = 0; currentInvocation = ctx; ++s_callLevel; } void Invocation_popBootContext(void) { currentInvocation = 0; --s_callLevel; } void Invocation_pushInvocation(Invocation* ctx, bool trusted) { JNI_pushLocalFrame(LOCAL_FRAME_SIZE); ctx->invocation = 0; ctx->function = 0; ctx->trusted = trusted; ctx->hasConnected = false; ctx->upperContext = CurrentMemoryContext; ctx->errorOccured = false; ctx->inExprContextCB = false; ctx->previous = currentInvocation; ctx->callLocals = 0; currentInvocation = ctx; Backend_setJavaSecurity(trusted); ++s_callLevel; } void Invocation_popInvocation(bool wasException) { CallLocal* cl; Invocation* ctx = currentInvocation->previous; if(currentInvocation->invocation != 0) { if(!wasException) JNI_callVoidMethod(currentInvocation->invocation, s_Invocation_onExit); JNI_deleteGlobalRef(currentInvocation->invocation); } if(currentInvocation->hasConnected) SPI_finish(); JNI_popLocalFrame(0); if(ctx != 0) { PG_TRY(); { Backend_setJavaSecurity(ctx->trusted); } PG_CATCH(); { elog(FATAL, "Failed to reinstate untrusted security after a trusted call or vice versa"); } PG_END_TRY(); MemoryContextSwitchTo(ctx->upperContext); } /** * Reset all local wrappers that has been allocated during this call. Yank them * from the double linked list but do *not* remove them. */ cl = currentInvocation->callLocals; if(cl != 0) { CallLocal* first = cl; do { cl->pointer = 0; cl->invocation = 0; cl = cl->next; } while(cl != first); } currentInvocation = ctx; --s_callLevel; } void Invocation_freeLocalWrapper(jlong wrapper) { Ptr2Long p2l; Invocation* ctx; CallLocal* cl; CallLocal* prev; p2l.longVal = wrapper; cl = (CallLocal*)p2l.ptrVal; prev = cl->prev; if(prev != cl) { /* Disconnect */ CallLocal* next = cl->next; prev->next = next; next->prev = prev; } /* If this CallLocal is freed before its owning invocation was * popped then there's a risk that this is the first CallLocal * in the list. */ ctx = cl->invocation; if(ctx != 0 && ctx->callLocals == cl) { if(prev == cl) prev = 0; ctx->callLocals = prev; } pfree(cl); } void* Invocation_getWrappedPointer(jlong wrapper) { Ptr2Long p2l; p2l.longVal = wrapper; return ((CallLocal*)p2l.ptrVal)->pointer; } jlong Invocation_createLocalWrapper(void* pointer) { /* Create a local wrapper for the pointer */ Ptr2Long p2l; CallLocal* cl = (CallLocal*)MemoryContextAlloc(JavaMemoryContext, sizeof(CallLocal)); CallLocal* prev = currentInvocation->callLocals; if(prev == 0) { currentInvocation->callLocals = cl; cl->prev = cl; cl->next = cl; } else { CallLocal* next = prev->next; cl->prev = prev; cl->next = next; prev->next = cl; next->prev = cl; } cl->pointer = pointer; cl->invocation = currentInvocation; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = cl; return p2l.longVal; } MemoryContext Invocation_switchToUpperContext(void) { return MemoryContextSwitchTo(currentInvocation->upperContext); } /* * Class: org_postgresql_pljava_jdbc_Invocation * Method: _getNestingLevel * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_jdbc_Invocation__1getNestingLevel(JNIEnv* env, jclass cls) { return s_callLevel; } /* * Class: org_postgresql_pljava_jdbc_Invocation * Method: _getCurrent * Signature: ()Lorg/postgresql/pljava/jdbc/Invocation; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_jdbc_Invocation__1getCurrent(JNIEnv* env, jclass cls) { return currentInvocation->invocation; } /* * Class: org_postgresql_pljava_jdbc_Invocation * Method: _clearErrorCondition * Signature: ()V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_Invocation__1clearErrorCondition(JNIEnv* env, jclass cls) { currentInvocation->errorOccured = false; } /* * Class: org_postgresql_pljava_jdbc_Invocation * Method: _register * Signature: ()V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_Invocation__1register(JNIEnv* env, jobject _this) { currentInvocation->invocation = (*env)->NewGlobalRef(env, _this); } pljava-1.4.3/src/C/pljava/SQLOutputToTuple.c0000644000014500000120000000303011634451404017706 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include "pljava/type/TupleDesc.h" #include "pljava/type/JavaWrapper.h" #include "pljava/SQLOutputToTuple.h" #include "org_postgresql_pljava_jdbc_SQLOutputToTuple.h" static jclass s_SQLOutputToTuple_class; static jmethodID s_SQLOutputToTuple_init; static jmethodID s_SQLOutputToTuple_getTuple; jobject SQLOutputToTuple_create(TupleDesc td) { jobject tupleDesc = TupleDesc_create(td); jobject result = JNI_newObject(s_SQLOutputToTuple_class, s_SQLOutputToTuple_init, tupleDesc); JNI_deleteLocalRef(tupleDesc); return result; } HeapTuple SQLOutputToTuple_getTuple(jobject sqlOutput) { Ptr2Long p2l; if(sqlOutput == 0) return 0; p2l.longVal = JNI_callLongMethod(sqlOutput, s_SQLOutputToTuple_getTuple); if(p2l.longVal == 0) return 0; return (HeapTuple)p2l.ptrVal; } /* Make this datatype available to the postgres system. */ extern void SQLOutputToTuple_initialize(void); void SQLOutputToTuple_initialize(void) { s_SQLOutputToTuple_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SQLOutputToTuple")); s_SQLOutputToTuple_init = PgObject_getJavaMethod(s_SQLOutputToTuple_class, "", "(Lorg/postgresql/pljava/internal/TupleDesc;)V"); s_SQLOutputToTuple_getTuple = PgObject_getJavaMethod(s_SQLOutputToTuple_class, "getTuple", "()J"); } pljava-1.4.3/src/C/pljava/JNICalls.c0000644000014500000120000005715111634451404016125 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/JNICalls.h" #include "pljava/Backend.h" #include "pljava/Invocation.h" #include "pljava/Exception.h" #include "pljava/type/ErrorData.h" #include "pljava/type/String.h" JNIEnv* jniEnv; #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3)) extern PGDLLIMPORT int log_min_messages; extern PGDLLIMPORT int client_min_messages; #else extern DLLIMPORT int log_min_messages; extern DLLIMPORT int client_min_messages; #endif static jobject s_threadLock; #define BEGIN_JAVA { JNIEnv* env = jniEnv; jniEnv = 0; #define END_JAVA jniEnv = env; } #define BEGIN_CALL \ BEGIN_JAVA \ if((*env)->MonitorExit(env, s_threadLock) < 0) \ elog(ERROR, "Java exit monitor failure"); #define END_CALL endCall(env); } static void elogExceptionMessage(JNIEnv* env, jthrowable exh, int logLevel) { StringInfoData buf; int sqlState = ERRCODE_INTERNAL_ERROR; jclass exhClass = (*env)->GetObjectClass(env, exh); jstring jtmp = (jstring)(*env)->CallObjectMethod(env, exhClass, Class_getName); JNIEnv* saveEnv = jniEnv; initStringInfo(&buf); jniEnv = env; /* Used by the String operations */ String_appendJavaString(&buf, jtmp); (*env)->DeleteLocalRef(env, exhClass); (*env)->DeleteLocalRef(env, jtmp); jtmp = (jstring)(*env)->CallObjectMethod(env, exh, Throwable_getMessage); if(jtmp != 0) { appendStringInfoString(&buf, ": "); String_appendJavaString(&buf, jtmp); (*env)->DeleteLocalRef(env, jtmp); } if((*env)->IsInstanceOf(env, exh, SQLException_class)) { jtmp = (*env)->CallObjectMethod(env, exh, SQLException_getSQLState); if(jtmp != 0) { char* s = String_createNTS(jtmp); (*env)->DeleteLocalRef(env, jtmp); if(strlen(s) >= 5) sqlState = MAKE_SQLSTATE(s[0], s[1], s[2], s[3], s[4]); pfree(s); } } jniEnv = saveEnv; ereport(logLevel, (errcode(sqlState), errmsg(buf.data))); } static void printStacktrace(JNIEnv* env, jobject exh) { if(DEBUG1 >= log_min_messages || DEBUG1 >= client_min_messages) { int currLevel = Backend_setJavaLogLevel(DEBUG1); (*env)->CallVoidMethod(env, exh, Throwable_printStackTrace); Backend_setJavaLogLevel(currLevel); } } static void endCall(JNIEnv* env) { jobject exh = (*env)->ExceptionOccurred(env); if(exh != 0) (*env)->ExceptionClear(env); if((*env)->MonitorEnter(env, s_threadLock) < 0) elog(ERROR, "Java enter monitor failure"); jniEnv = env; if(exh != 0) { printStacktrace(env, exh); if((*env)->IsInstanceOf(env, exh, ServerException_class)) { /* Rethrow the server error. */ jobject jed = (*env)->CallObjectMethod(env, exh, ServerException_getErrorData); if(jed != 0) ReThrowError(ErrorData_getErrorData(jed)); } /* There's no return from this call. */ elogExceptionMessage(env, exh, ERROR); } } bool beginNativeNoErrCheck(JNIEnv* env) { if((env = JNI_setEnv(env)) != 0) { /* The backend is *not* awaiting the return of a call to the JVM * so there's no way the JVM can be allowed to call out at this * point. */ Exception_throw(ERRCODE_INTERNAL_ERROR, "An attempt was made to call a PostgreSQL backend function while main thread was not in the JVM"); JNI_setEnv(env); return false; } return true; } bool beginNative(JNIEnv* env) { if (!currentInvocation) { env = JNI_setEnv(env); Exception_throw(ERRCODE_INTERNAL_ERROR, "An attempt was made to call a PostgreSQL backend function in a transaction callback. At the end of a transaction you may not access the database any longer."); JNI_setEnv(env); return false; } if(currentInvocation->errorOccured) { /* An elog with level higher than ERROR was issued. The transaction * state is unknown. There's no way the JVM is allowed to enter the * backend at this point. */ env = JNI_setEnv(env); Exception_throw(ERRCODE_INTERNAL_ERROR, "An attempt was made to call a PostgreSQL backend function after an elog(ERROR) had been issued"); JNI_setEnv(env); return false; } return beginNativeNoErrCheck(env); } jboolean JNI_callBooleanMethod(jobject object, jmethodID methodID, ...) { jboolean result; va_list args; va_start(args, methodID); result = JNI_callBooleanMethodV(object, methodID, args); va_end(args); return result; } jboolean JNI_callBooleanMethodV(jobject object, jmethodID methodID, va_list args) { jboolean result; BEGIN_CALL result = (*env)->CallBooleanMethodV(env, object, methodID, args); END_CALL return result; } jbyte JNI_callByteMethod(jobject object, jmethodID methodID, ...) { jbyte result; va_list args; va_start(args, methodID); result = JNI_callByteMethodV(object, methodID, args); va_end(args); return result; } jbyte JNI_callByteMethodV(jobject object, jmethodID methodID, va_list args) { jbyte result; BEGIN_CALL result = (*env)->CallByteMethodV(env, object, methodID, args); END_CALL return result; } jdouble JNI_callDoubleMethod(jobject object, jmethodID methodID, ...) { jdouble result; va_list args; va_start(args, methodID); result = JNI_callDoubleMethodV(object, methodID, args); va_end(args); return result; } jdouble JNI_callDoubleMethodV(jobject object, jmethodID methodID, va_list args) { jdouble result; BEGIN_CALL result = (*env)->CallDoubleMethodV(env, object, methodID, args); END_CALL return result; } jfloat JNI_callFloatMethod(jobject object, jmethodID methodID, ...) { jfloat result; va_list args; va_start(args, methodID); result = JNI_callFloatMethodV(object, methodID, args); va_end(args); return result; } jfloat JNI_callFloatMethodV(jobject object, jmethodID methodID, va_list args) { jfloat result; BEGIN_CALL result = (*env)->CallFloatMethodV(env, object, methodID, args); END_CALL return result; } jint JNI_callIntMethod(jobject object, jmethodID methodID, ...) { jint result; va_list args; va_start(args, methodID); result = JNI_callIntMethodV(object, methodID, args); va_end(args); return result; } jint JNI_callIntMethodV(jobject object, jmethodID methodID, va_list args) { jint result; BEGIN_CALL result = (*env)->CallIntMethodV(env, object, methodID, args); END_CALL return result; } jlong JNI_callLongMethod(jobject object, jmethodID methodID, ...) { jlong result; va_list args; va_start(args, methodID); result = JNI_callLongMethodV(object, methodID, args); va_end(args); return result; } jlong JNI_callLongMethodV(jobject object, jmethodID methodID, va_list args) { jlong result; BEGIN_CALL result = (*env)->CallLongMethodV(env, object, methodID, args); END_CALL return result; } jshort JNI_callShortMethod(jobject object, jmethodID methodID, ...) { jshort result; va_list args; va_start(args, methodID); result = JNI_callShortMethodV(object, methodID, args); va_end(args); return result; } jshort JNI_callShortMethodV(jobject object, jmethodID methodID, va_list args) { jshort result; BEGIN_CALL result = (*env)->CallShortMethodV(env, object, methodID, args); END_CALL return result; } jobject JNI_callObjectMethod(jobject object, jmethodID methodID, ...) { jobject result; va_list args; va_start(args, methodID); result = JNI_callObjectMethodV(object, methodID, args); va_end(args); return result; } jobject JNI_callObjectMethodV(jobject object, jmethodID methodID, va_list args) { jobject result; BEGIN_CALL result = (*env)->CallObjectMethodV(env, object, methodID, args); END_CALL return result; } jboolean JNI_callStaticBooleanMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jboolean result; BEGIN_CALL result = (*env)->CallStaticBooleanMethodA(env, clazz, methodID, args); END_CALL return result; } jbyte JNI_callStaticByteMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jbyte result; BEGIN_CALL result = (*env)->CallStaticByteMethodA(env, clazz, methodID, args); END_CALL return result; } jdouble JNI_callStaticDoubleMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jdouble result; BEGIN_CALL result = (*env)->CallStaticDoubleMethodA(env, clazz, methodID, args); END_CALL return result; } jfloat JNI_callStaticFloatMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jfloat result; BEGIN_CALL result = (*env)->CallStaticFloatMethodA(env, clazz, methodID, args); END_CALL return result; } jint JNI_callStaticIntMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jint result; BEGIN_CALL result = (*env)->CallStaticIntMethodA(env, clazz, methodID, args); END_CALL return result; } jlong JNI_callStaticLongMethod(jclass clazz, jmethodID methodID, ...) { jlong result; va_list args; va_start(args, methodID); result = JNI_callStaticLongMethodV(clazz, methodID, args); va_end(args); return result; } jlong JNI_callStaticLongMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jlong result; BEGIN_CALL result = (*env)->CallStaticLongMethodA(env, clazz, methodID, args); END_CALL return result; } jlong JNI_callStaticLongMethodV(jclass clazz, jmethodID methodID, va_list args) { jlong result; BEGIN_CALL result = (*env)->CallStaticLongMethodV(env, clazz, methodID, args); END_CALL return result; } jobject JNI_callStaticObjectMethod(jclass clazz, jmethodID methodID, ...) { jobject result; va_list args; va_start(args, methodID); result = JNI_callStaticObjectMethodV(clazz, methodID, args); va_end(args); return result; } jobject JNI_callStaticObjectMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jobject result; BEGIN_CALL result = (*env)->CallStaticObjectMethodA(env, clazz, methodID, args); END_CALL return result; } jobject JNI_callStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args) { jobject result; BEGIN_CALL result = (*env)->CallStaticObjectMethodV(env, clazz, methodID, args); END_CALL return result; } jshort JNI_callStaticShortMethodA(jclass clazz, jmethodID methodID, jvalue* args) { jshort result; BEGIN_CALL result = (*env)->CallStaticShortMethodA(env, clazz, methodID, args); END_CALL return result; } void JNI_callStaticVoidMethod(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); JNI_callStaticVoidMethodV(clazz, methodID, args); va_end(args); } void JNI_callStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args) { BEGIN_CALL (*env)->CallStaticVoidMethodA(env, clazz, methodID, args); END_CALL } void JNI_callStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args) { BEGIN_CALL (*env)->CallStaticVoidMethodV(env, clazz, methodID, args); END_CALL } void JNI_callVoidMethod(jobject object, jmethodID methodID, ...) { va_list args; va_start(args, methodID); JNI_callVoidMethodV(object, methodID, args); va_end(args); } void JNI_callVoidMethodV(jobject object, jmethodID methodID, va_list args) { BEGIN_CALL (*env)->CallVoidMethodV(env, object, methodID, args); END_CALL } jint JNI_createVM(JavaVM** javaVM, JavaVMInitArgs* vmArgs) { JNIEnv* env = 0; jint jstat = JNI_CreateJavaVM(javaVM, (void **)&env, vmArgs); if(jstat == JNI_OK) jniEnv = env; return jstat; } void JNI_deleteGlobalRef(jobject object) { BEGIN_JAVA (*env)->DeleteGlobalRef(env, object); END_JAVA } void JNI_deleteLocalRef(jobject object) { BEGIN_JAVA (*env)->DeleteLocalRef(env, object); END_JAVA } void JNI_deleteWeakGlobalRef(jweak object) { BEGIN_JAVA (*env)->DeleteWeakGlobalRef(env, object); END_JAVA } jint JNI_destroyVM(JavaVM *vm) { jint result; BEGIN_JAVA result = (*vm)->DestroyJavaVM(vm); END_JAVA jniEnv = 0; s_threadLock = 0; return result; } jboolean JNI_exceptionCheck(void) { jboolean result; BEGIN_JAVA result = (*env)->ExceptionCheck(env); END_JAVA return result; } void JNI_exceptionClear(void) { BEGIN_JAVA (*env)->ExceptionClear(env); END_JAVA } void JNI_exceptionDescribe(void) { /* The ExceptionDescribe will print on stderr. Not a good idea * since the exception itself might have been caused by an unwriteable * stdout/stderr (happens when running as a windows service * * (*env)->ExceptionDescribe(env); */ jthrowable exh; BEGIN_JAVA exh = (*env)->ExceptionOccurred(env); if(exh != 0) { (*env)->ExceptionClear(env); printStacktrace(env, exh); elogExceptionMessage(env, exh, WARNING); } END_JAVA } jthrowable JNI_exceptionOccurred(void) { jthrowable result; BEGIN_JAVA result = (*env)->ExceptionOccurred(env); END_JAVA return result; } jclass JNI_findClass(const char* className) { jclass result; BEGIN_JAVA result = (*env)->FindClass(env, className); END_JAVA return result; } jsize JNI_getArrayLength(jarray array) { jsize result; BEGIN_JAVA result = (*env)->GetArrayLength(env, array); END_JAVA return result; } jbyte* JNI_getByteArrayElements(jbyteArray array, jboolean* isCopy) { jbyte* result; BEGIN_JAVA result = (*env)->GetByteArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) { BEGIN_JAVA (*env)->GetByteArrayRegion(env, array, start, len, buf); END_JAVA } jboolean* JNI_getBooleanArrayElements(jbooleanArray array, jboolean* isCopy) { jboolean* result; BEGIN_JAVA result = (*env)->GetBooleanArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) { BEGIN_JAVA (*env)->GetBooleanArrayRegion(env, array, start, len, buf); END_JAVA } jdouble* JNI_getDoubleArrayElements(jdoubleArray array, jboolean* isCopy) { jdouble* result; BEGIN_JAVA result = (*env)->GetDoubleArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf) { BEGIN_JAVA (*env)->GetDoubleArrayRegion(env, array, start, len, buf); END_JAVA } jfieldID JNI_getFieldID(jclass clazz, const char* name, const char* sig) { jfieldID result; BEGIN_JAVA result = (*env)->GetFieldID(env, clazz, name, sig); END_JAVA return result; } jfloat* JNI_getFloatArrayElements(jfloatArray array, jboolean* isCopy) { jfloat* result; BEGIN_JAVA result = (*env)->GetFloatArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) { BEGIN_JAVA (*env)->GetFloatArrayRegion(env, array, start, len, buf); END_JAVA } jint* JNI_getIntArrayElements(jintArray array, jboolean* isCopy) { jint* result; BEGIN_JAVA result = (*env)->GetIntArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) { BEGIN_JAVA (*env)->GetIntArrayRegion(env, array, start, len, buf); END_JAVA } jint JNI_getIntField(jobject object, jfieldID field) { jint result; BEGIN_JAVA result = (*env)->GetIntField(env, object, field); END_JAVA return result; } jlong* JNI_getLongArrayElements(jlongArray array, jboolean* isCopy) { jlong* result; BEGIN_JAVA result = (*env)->GetLongArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) { BEGIN_JAVA (*env)->GetLongArrayRegion(env, array, start, len, buf); END_JAVA } jlong JNI_getLongField(jobject object, jfieldID field) { jlong result; BEGIN_JAVA result = (*env)->GetLongField(env, object, field); END_JAVA return result; } jmethodID JNI_getMethodID(jclass clazz, const char* name, const char* sig) { jmethodID result; BEGIN_JAVA result = (*env)->GetMethodID(env, clazz, name, sig); END_JAVA return result; } jobject JNI_getObjectArrayElement(jobjectArray array, jsize index) { jobject result; BEGIN_JAVA result = (*env)->GetObjectArrayElement(env, array, index); END_JAVA return result; } jclass JNI_getObjectClass(jobject obj) { jclass result; BEGIN_JAVA result = (*env)->GetObjectClass(env, obj); END_JAVA return result; } jshort* JNI_getShortArrayElements(jshortArray array, jboolean* isCopy) { jshort* result; BEGIN_JAVA result = (*env)->GetShortArrayElements(env, array, isCopy); END_JAVA return result; } void JNI_getShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) { BEGIN_JAVA (*env)->GetShortArrayRegion(env, array, start, len, buf); END_JAVA } jfieldID JNI_getStaticFieldID(jclass clazz, const char* name, const char* sig) { jfieldID result; BEGIN_JAVA result = (*env)->GetStaticFieldID(env, clazz, name, sig); END_JAVA return result; } jmethodID JNI_getStaticMethodID(jclass clazz, const char* name, const char* sig) { jmethodID result; BEGIN_CALL result = (*env)->GetStaticMethodID(env, clazz, name, sig); END_CALL return result; } jmethodID JNI_getStaticMethodIDOrNull(jclass clazz, const char* name, const char* sig) { jmethodID result; BEGIN_CALL result = (*env)->GetStaticMethodID(env, clazz, name, sig); if(result == 0) (*env)->ExceptionClear(env); END_CALL return result; } jobject JNI_getStaticObjectField(jclass clazz, jfieldID field) { jobject result; BEGIN_JAVA result = (*env)->GetStaticObjectField(env, clazz, field); END_JAVA return result; } const char* JNI_getStringUTFChars(jstring string, jboolean* isCopy) { const char* result; BEGIN_JAVA result = (*env)->GetStringUTFChars(env, string, isCopy); END_JAVA return result; } jboolean JNI_hasNullArrayElement(jobjectArray array) { jsize idx; jboolean foundNull = JNI_FALSE; BEGIN_JAVA idx = (*env)->GetArrayLength(env, array); while(--idx >= 0) { if((*env)->GetObjectArrayElement(env, array, idx) != 0) continue; foundNull = JNI_TRUE; break; } END_JAVA return foundNull; } jboolean JNI_isCallingJava(void) { return jniEnv == 0; } jboolean JNI_isInstanceOf(jobject obj, jclass clazz) { jboolean result; BEGIN_JAVA result = (*env)->IsInstanceOf(env, obj, clazz); END_JAVA return result; } jbyteArray JNI_newByteArray(jsize length) { jbyteArray result; BEGIN_JAVA result = (*env)->NewByteArray(env, length); END_JAVA return result; } jbooleanArray JNI_newBooleanArray(jsize length) { jbooleanArray result; BEGIN_JAVA result = (*env)->NewBooleanArray(env, length); END_JAVA return result; } jobjectArray JNI_newObjectArray(jsize length, jclass elementClass, jobject initialElement) { jobjectArray result; BEGIN_JAVA result = (*env)->NewObjectArray(env, length, elementClass, initialElement); END_JAVA return result; } jobject JNI_newDirectByteBuffer(void* address, jlong capacity) { jobject result; BEGIN_JAVA result = (*env)->NewDirectByteBuffer(env, address, capacity); END_JAVA return result; } jdoubleArray JNI_newDoubleArray(jsize length) { jdoubleArray result; BEGIN_JAVA result = (*env)->NewDoubleArray(env, length); END_JAVA return result; } jfloatArray JNI_newFloatArray(jsize length) { jfloatArray result; BEGIN_JAVA result = (*env)->NewFloatArray(env, length); END_JAVA return result; } jobject JNI_newGlobalRef(jobject object) { jobject result; BEGIN_JAVA result = (*env)->NewGlobalRef(env, object); END_JAVA return result; } jintArray JNI_newIntArray(jsize length) { jintArray result; BEGIN_JAVA result = (*env)->NewIntArray(env, length); END_JAVA return result; } jobject JNI_newLocalRef(jobject object) { jobject result; BEGIN_JAVA result = (*env)->NewLocalRef(env, object); END_JAVA return result; } jlongArray JNI_newLongArray(jsize length) { jlongArray result; BEGIN_JAVA result = (*env)->NewLongArray(env, length); END_JAVA return result; } jshortArray JNI_newShortArray(jsize length) { jshortArray result; BEGIN_JAVA result = (*env)->NewShortArray(env, length); END_JAVA return result; } jstring JNI_newStringUTF(const char* bytes) { jstring result; BEGIN_JAVA result = (*env)->NewStringUTF(env, bytes); END_JAVA return result; } jint JNI_pushLocalFrame(jint capacity) { jint result; BEGIN_JAVA result = (*env)->PushLocalFrame(env, capacity); END_JAVA return result; } jobject JNI_popLocalFrame(jobject resultObj) { jobject result; BEGIN_JAVA result = (*env)->PopLocalFrame(env, resultObj); END_JAVA return result; } jweak JNI_newWeakGlobalRef(jobject object) { jweak result; BEGIN_JAVA result = (*env)->NewWeakGlobalRef(env, object); END_JAVA return result; } jobject JNI_newObject(jclass clazz, jmethodID ctor, ...) { jobject result; va_list args; va_start(args, ctor); result = JNI_newObjectV(clazz, ctor, args); va_end(args); return result; } jobject JNI_newObjectV(jclass clazz, jmethodID ctor, va_list args) { jobject result; BEGIN_CALL result = (*env)->NewObjectV(env, clazz, ctor, args); END_CALL return result; } void JNI_releaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseByteArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseBooleanArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseDoubleArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseFloatArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseIntArrayElements(jintArray array, jint* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseIntArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseLongArrayElements(jlongArray array, jlong* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseLongArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseShortArrayElements(jshortArray array, jshort* elems, jint mode) { BEGIN_JAVA (*env)->ReleaseShortArrayElements(env, array, elems, mode); END_JAVA } void JNI_releaseStringUTFChars(jstring string, const char *utf) { BEGIN_JAVA (*env)->ReleaseStringUTFChars(env, string, utf); END_JAVA } jint JNI_registerNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { jint result; BEGIN_JAVA result = (*env)->RegisterNatives(env, clazz, methods, nMethods); END_JAVA return result; } void JNI_setByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) { BEGIN_JAVA (*env)->SetByteArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) { BEGIN_JAVA (*env)->SetBooleanArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf) { BEGIN_JAVA (*env)->SetDoubleArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) { BEGIN_JAVA (*env)->SetFloatArrayRegion(env, array, start, len, buf); END_JAVA } JNIEnv* JNI_setEnv(JNIEnv* env) { JNIEnv* oldEnv = jniEnv; jniEnv = env; return oldEnv; } void JNI_setIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) { BEGIN_JAVA (*env)->SetIntArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) { BEGIN_JAVA (*env)->SetLongArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setLongField(jobject object, jfieldID field, jlong value) { BEGIN_JAVA (*env)->SetLongField(env, object, field, value); END_JAVA } void JNI_setObjectArrayElement(jobjectArray array, jsize index, jobject value) { BEGIN_JAVA (*env)->SetObjectArrayElement(env, array, index, value); END_JAVA } void JNI_setShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) { BEGIN_JAVA (*env)->SetShortArrayRegion(env, array, start, len, buf); END_JAVA } void JNI_setThreadLock(jobject lockObject) { BEGIN_JAVA s_threadLock = (*env)->NewGlobalRef(env, lockObject); if((*env)->MonitorEnter(env, s_threadLock) < 0) elog(ERROR, "Java enter monitor failure (initial)"); END_JAVA } jint JNI_throw(jthrowable obj) { jint result; BEGIN_JAVA result = (*env)->Throw(env, obj); END_JAVA return result; } pljava-1.4.3/src/C/pljava/SQLOutputToChunk.c0000644000014500000120000000540111634451404017671 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include "pljava/SQLOutputToChunk.h" #include "org_postgresql_pljava_jdbc_SQLOutputToChunk.h" static jclass s_SQLOutputToChunk_class; static jmethodID s_SQLOutputToChunk_init; static jmethodID s_SQLOutputToChunk_close; jobject SQLOutputToChunk_create(StringInfo data) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = data; return JNI_newObject(s_SQLOutputToChunk_class, s_SQLOutputToChunk_init, p2l.longVal); } void SQLOutputToChunk_close(jobject stream) { JNI_callVoidMethod(stream, s_SQLOutputToChunk_close); } /* Make this datatype available to the postgres system. */ extern void SQLOutputToChunk_initialize(void); void SQLOutputToChunk_initialize(void) { JNINativeMethod methods[] = { { "_writeByte", "(JI)V", Java_org_postgresql_pljava_jdbc_SQLOutputToChunk__1writeByte }, { "_writeBytes", "(J[BI)V", Java_org_postgresql_pljava_jdbc_SQLOutputToChunk__1writeBytes }, { 0, 0, 0 }}; s_SQLOutputToChunk_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SQLOutputToChunk")); PgObject_registerNatives2(s_SQLOutputToChunk_class, methods); s_SQLOutputToChunk_init = PgObject_getJavaMethod(s_SQLOutputToChunk_class, "", "(J)V"); s_SQLOutputToChunk_close = PgObject_getJavaMethod(s_SQLOutputToChunk_class, "close", "()V"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SQLOutputToChunk * Method: _writeByte * Signature: (JI)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SQLOutputToChunk__1writeByte(JNIEnv* env, jclass cls, jlong _this, jint b) { Ptr2Long p2l; unsigned char byte = (unsigned char)b; p2l.longVal = _this; BEGIN_NATIVE appendBinaryStringInfo((StringInfo)p2l.ptrVal, (char*)&byte, 1); END_NATIVE } /* * Class: org_postgresql_pljava_internal_MemoryChunkInputStream * Method: _readBytes * Signature: (JI[BII)V */ #define BYTE_BUF_SIZE 1024 JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SQLOutputToChunk__1writeBytes(JNIEnv* env, jclass cls, jlong _this, jbyteArray ba, jint len) { Ptr2Long p2l; jbyte buffer[BYTE_BUF_SIZE]; int off = 0; p2l.longVal = _this; BEGIN_NATIVE while(len > 0) { int copySize = len; if(copySize > BYTE_BUF_SIZE) copySize = BYTE_BUF_SIZE; JNI_getByteArrayRegion(ba, off, copySize, buffer); appendBinaryStringInfo((StringInfo)p2l.ptrVal, (const char*)buffer, copySize); off += copySize; len -= copySize; } END_NATIVE } pljava-1.4.3/src/C/pljava/PgSavepoint.c0000644000014500000120000000731311634451404016760 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_PgSavepoint.h" #include "pljava/Exception.h" #include "pljava/type/String.h" #include "pljava/SPI.h" extern void PgSavepoint_initialize(void); void PgSavepoint_initialize(void) { JNINativeMethod methods[] = { { "_set", "(Ljava/lang/String;)J", Java_org_postgresql_pljava_internal_PgSavepoint__1set }, { "_release", "(J)V", Java_org_postgresql_pljava_internal_PgSavepoint__1release }, { "_rollback", "(J)V", Java_org_postgresql_pljava_internal_PgSavepoint__1rollback }, { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_PgSavepoint__1getName }, { "_getId", "(J)I", Java_org_postgresql_pljava_internal_PgSavepoint__1getId }, { 0, 0, 0 } }; PgObject_registerNatives("org/postgresql/pljava/internal/PgSavepoint", methods); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_PgSavepoint * Method: _set * Signature: (Ljava/lang/String;)J; */ JNIEXPORT jlong JNICALL Java_org_postgresql_pljava_internal_PgSavepoint__1set(JNIEnv* env, jclass cls, jstring jname) { jlong result = 0; BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; char* name = String_createNTS(jname); MemoryContext currCtx = MemoryContextSwitchTo(JavaMemoryContext); p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = SPI_setSavepoint(name); result = p2l.longVal; MemoryContextSwitchTo(currCtx); pfree(name); } PG_CATCH(); { Exception_throw_ERROR("SPI_setSavepoint"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_PgSavepoint * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_PgSavepoint__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((Savepoint*)p2l.ptrVal)->name); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_PgSavepoint * Method: _getId * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_PgSavepoint__1getId(JNIEnv* env, jclass clazz, jlong _this) { jint result = (jint)InvalidSubTransactionId; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jint)((Savepoint*)p2l.ptrVal)->xid; } return result; } /* * Class: org_postgresql_pljava_internal_PgSavepoint * Method: _release * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_PgSavepoint__1release(JNIEnv* env, jclass clazz, jlong _this) { if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { SPI_releaseSavepoint((Savepoint*)p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("SPI_releaseSavepoint"); } PG_END_TRY(); END_NATIVE } } /* * Class: org_postgresql_pljava_internal_PgSavepoint * Method: _rollback * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_PgSavepoint__1rollback(JNIEnv* env, jclass clazz, jlong _this) { if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { SPI_rollbackSavepoint((Savepoint*)p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("SPI_rollbackSavepoint"); } PG_END_TRY(); END_NATIVE } } pljava-1.4.3/src/C/pljava/.#Makefile.1.410000644000014500000120000001172011634451404016515 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- MODULE_big := pljava SRCDIR := $(MODULEROOT)/$(MODULE_big) INCLDIR := -I$(MODULEROOT)/include -I$(JNIDIR) mkobjs = $(subst $(SRCDIR)/,,$(1:%.c=%.o)) SRCS = $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.c -print) OBJS = $(call mkobjs,$(SRCS)) DLLTOOL_DEFFLAGS := --add-stdcall-alias .PHONY: checkjavahome build pljava-plugin $(OBJS): %.o : $(SRCDIR)/%.c @-mkdir -p $(@D) $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ # Must include PGXS at this point since it defines PORTNAME and VERSION # include $(PGXS) SS_VERSION := $(subst ., ,$(subst devel,.99,$(subst beta,.99,$(subst alpha,.99,$(subst rc,.99,$(subst RC,.99,$(VERSION))))))) PGSQL_MAJOR_VER = $(word 1,$(SS_VERSION)) PGSQL_MINOR_VER = $(word 2,$(SS_VERSION)) PGSQL_PATCH_VER = $(word 3,$(SS_VERSION)) PLJAVA_CPPFLAGS = \ -DPKGLIBDIR=\"$(pkglibdir)\" $(INCLDIR) \ -DPGSQL_MAJOR_VER=$(PGSQL_MAJOR_VER) \ -DPGSQL_MINOR_VER=$(PGSQL_MINOR_VER) \ -DPGSQL_PATCH_VER=$(PGSQL_PATCH_VER) ifeq ($(PORTNAME), win32) DLL_BUILD := 1 else ifeq ($(PORTNAME), darwin) # # override with JAVAVM_FWX_ROOT=/... on command line # if something else is needed # JAVAVM_FWX_ROOT := /System/Library/Frameworks PLJAVA_CPPFLAGS += -I$(JAVAVM_FWX_ROOT)/JavaVM.framework/Headers/ else ifeq ($(PORTNAME), ibmos2) DLL_BUILD := 1 else JRE_INCL := $(PORTNAME) ifeq ($(findstring -m64,$(CFLAGS)),-m64) JRE_CPU := amd64 else ifeq ($(host_cpu), i686) JRE_CPU := i386 else ifeq ($(host_cpu), i586) JRE_CPU := i386 else ifeq ($(host_cpu), i486) JRE_CPU := i386 else ifeq ($(host_cpu), x86_64) JRE_CPU := amd64 else JRE_CPU := $(host_cpu) endif endif endif endif endif endif endif endif ifdef USE_GCJ PLJAVA_CPPFLAGS += -DGCJ SHLIB_LINK = -lgcj ifdef DLL_BUILD SHLIB_LINK += -lws2_32 endif OBJS += pljava_jar.o else pljava-jar = pljava.jar ifeq ($(PORTNAME), darwin) SHLIB_LINK += -L. -framework JavaVM else ifdef JAVA_HOME ifeq ($(PORTNAME), win32) JDK_HOME := $(subst \,/,$(JAVA_HOME)) else JDK_HOME := $(JAVA_HOME) endif else ifeq ($(PORTNAME), ibmos2) JAVA_HOME_TEST_RESULT := $(echo "You must set JAVA_HOME") else JAVA_HOME_TEST_RESULT := $(error You must set JAVA_HOME) endif endif ifdef DLL_BUILD ifeq ($(PORTNAME),ibmos2) JRE_INCL := os2 else JRE_INCL := win32 endif JVM_LIB := $(JDK_HOME)/jre/bin/client else JRE_LIB := $(JDK_HOME)/jre/lib/$(JRE_CPU) JVM_LIB := $(firstword $(shell /bin/ls -d \ $(JRE_LIB)/client \ $(JRE_LIB)/server \ $(JRE_LIB)/jrockit \ 2> /dev/null)) endif PLJAVA_CPPFLAGS += -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/$(JRE_INCL)" SHLIB_LINK += -L. -L"$(JVM_LIB)" -R"$(JVM_LIB)" -ljvm endif endif ifeq ($(JRE_INCL), win32) # # Mingw is a bit special in that we use a "normal" windows # port of the Java Runtime Environment (unless we use gcj # of course). The headers etc. for the JRE is windows style # and contains __int64. GNU compiler doesn't know __int64, # instead it uses long long. # ifndef USE_GCJ PLJAVA_CPPFLAGS += -D__int64='long long' # Don't use higher standard then c89 since this rules out older compilers. We # must use 'long long' however, since JNI defines it. # PLJAVA_CFLAGS += -Wno-long-long endif endif override CPPFLAGS += $(PLJAVA_CPPFLAGS) override CFLAGS += $(PLJAVA_CFLAGS) # shlib naming convention for plugins (no 'lib') # plugin = $(NAME)$(DLSUFFIX) soname = $(plugin) shlib = $(plugin) ifndef USE_GCJ checkjavahome: $(JAVA_HOME_TEST_RESULT) endif ifneq ($(PORTNAME), win32) ifneq ($(PORTNAME), aix) # Normal case # $(plugin): $(OBJS) $(LINK.shared) $(LDFLAGS_SL) $(OBJS) $(SHLIB_LINK) -o $(plugin) else # PORTNAME == aix # AIX case # $(plugin): lib$(NAME).a $(MKLDEXPORT) lib$(NAME).a > $(NAME)$(EXPSUFF) $(COMPILER) $(LDFLAGS_NO_L) $(LDFLAGS_SL) -o $(plugin) $< -Wl,-bE:$(NAME)$(EXPSUFF) $(SHLIB_LINK) endif # PORTNAME == aix else # PORTNAME == win32 # win32 case # $(plugin): $(OBJS) ifndef USE_GCJ $(DLLTOOL) --input-def $(SRCDIR)/jvm.def --kill-at --dllname jvm.dll --output-lib libjvm.dll.a endif $(DLLTOOL) --export-all $(DLLTOOL_DEFFLAGS) --output-def $(NAME).def $(OBJS) $(DLLWRAP) $(LDFLAGS_SL) -o $(plugin) --dllname $(plugin) $(DLLWRAP_FLAGS) --def $(NAME).def $(OBJS) $(SHLIB_LINK) $(DLLTOOL) --dllname $(plugin) $(DLLTOOL_LIBFLAGS) --def $(NAME).def endif # PORTNAME == win32 build_all: $(plugin) build_install: build_all installdirs $(INSTALL_SHLIB) $(plugin) $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ $(INSTALL_DATA) ../$(pljava-jar) $(DESTDIR)$(pkglibdir) endif build_uninstall: rm -f $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ rm -f $(DESTDIR)$(pkglibdir)/$(pljava-jar) endif pljava-1.4.3/src/C/pljava/Session.c0000644000014500000120000000322311634451404016140 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "org_postgresql_pljava_internal_Session.h" #include "pljava/Session.h" #include "pljava/type/AclId.h" extern void Session_initialize(void); void Session_initialize(void) { JNINativeMethod methods[] = { { "_setUser", "(Lorg/postgresql/pljava/internal/AclId;)V", Java_org_postgresql_pljava_internal_Session__1setUser }, { 0, 0, 0 }}; PgObject_registerNatives("org/postgresql/pljava/internal/Session", methods); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Session * Method: _setUser * Signature: (Lorg/postgresql/pljava/internal/AclId;)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Session__1setUser(JNIEnv* env, jclass cls, jobject aclId) { /* No error checking since this might be a restore of user in * a finally block after an exception. */ BEGIN_NATIVE_NO_ERRCHECK #if ( \ (PGSQL_MAJOR_VER > 8) \ || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3) \ || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 2 && PGSQL_PATCH_VER >= 6) \ || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 1 && PGSQL_PATCH_VER >= 11) \ || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0 && PGSQL_PATCH_VER >= 15) \ ) SetUserIdAndContext(AclId_getAclId(aclId), true); #else SetUserId(AclId_getAclId(aclId)); #endif END_NATIVE } pljava-1.4.3/src/C/pljava/type/0000755000014500000120000000000011634451404015332 5ustar johannstaffpljava-1.4.3/src/C/pljava/type/.#Type.c.1.390000644000014500000120000005237011634451404017140 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include #include #include #include "pljava/type/String_priv.h" #include "pljava/type/Array.h" #include "pljava/type/Coerce.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" #include "pljava/type/UDT.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/SPI.h" static HashMap s_typeByOid; static HashMap s_obtainerByOid; static HashMap s_obtainerByJavaName; static jclass s_Map_class; static jmethodID s_Map_get; typedef struct CacheEntryData { Type type; TypeObtainer obtainer; Oid typeId; } CacheEntryData; typedef CacheEntryData* CacheEntry; static jclass s_Iterator_class; static jmethodID s_Iterator_hasNext; static jmethodID s_Iterator_next; /* Structure used in multi function calls (calls returning * SETOF ) */ typedef struct { Type elemType; jobject rowProducer; jobject rowCollector; jobject invocation; MemoryContext rowContext; MemoryContext spiContext; bool hasConnected; bool trusted; } CallContextData; static void _closeIteration(CallContextData* ctxData) { currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; Type_closeSRF(ctxData->elemType, ctxData->rowProducer); JNI_deleteGlobalRef(ctxData->rowProducer); if(ctxData->rowCollector != 0) JNI_deleteGlobalRef(ctxData->rowCollector); MemoryContextDelete(ctxData->rowContext); if(ctxData->hasConnected && ctxData->spiContext != 0) { /* Connect during SRF_IS_FIRSTCALL(). Switch context back to what * it was at that time and disconnect. */ MemoryContext currCtx = MemoryContextSwitchTo(ctxData->spiContext); Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); } } static void _endOfSetCB(Datum arg) { Invocation topCall; bool saveInExprCtxCB; CallContextData* ctxData = (CallContextData*)DatumGetPointer(arg); if(currentInvocation == 0) Invocation_pushInvocation(&topCall, ctxData->trusted); saveInExprCtxCB = currentInvocation->inExprContextCB; currentInvocation->inExprContextCB = true; _closeIteration(ctxData); currentInvocation->inExprContextCB = saveInExprCtxCB; } Type Type_getCoerceIn(Type self, Type other) { Oid funcId; Type coerce; Oid fromOid = other->typeId; Oid toOid = self->typeId; if(self->inCoercions != 0) { coerce = HashMap_getByOid(self->inCoercions, fromOid); if(coerce != 0) return coerce; } if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if(self->inCoercions == 0) self->inCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coerce = Coerce_createIn(self, other, funcId); HashMap_putByOid(self->inCoercions, fromOid, coerce); return coerce; } Type Type_getCoerceOut(Type self, Type other) { Oid funcId; Type coercer; Oid fromOid = self->typeId; Oid toOid = other->typeId; if(self->outCoercions != 0) { coercer = HashMap_getByOid(self->outCoercions, toOid); if(coercer != 0) return coercer; } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(self->outCoercions == 0) self->outCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coercer = Coerce_createOut(self, other, funcId); HashMap_putByOid(self->outCoercions, toOid, coercer); return coercer; } bool Type_canReplaceType(Type self, Type other) { return self->typeClass->canReplaceType(self, other); } bool Type_isDynamic(Type self) { return self->typeClass->dynamic; } bool Type_isOutParameter(Type self) { return self->typeClass->outParameter; } jvalue Type_coerceDatum(Type self, Datum value) { return self->typeClass->coerceDatum(self, value); } Datum Type_coerceObject(Type self, jobject object) { return self->typeClass->coerceObject(self, object); } char Type_getAlign(Type self) { return self->align; } TypeClass Type_getClass(Type self) { return self->typeClass; } int16 Type_getLength(Type self) { return self->length; } bool Type_isByValue(Type self) { return self->byValue; } jclass Type_getJavaClass(Type self) { TypeClass typeClass = self->typeClass; if(typeClass->javaClass == 0) { jclass cls; const char* cp = typeClass->JNISignature; if(cp == 0 || *cp == 0) ereport(ERROR, ( errmsg("Type '%s' has no corresponding java class", PgObjectClass_getName((PgObjectClass)typeClass)))); if(*cp == 'L') { /* L; should be just . Strange * since the L and ; are retained if its an array. */ int len = strlen(cp) - 2; char* bp = palloc(len + 1); memcpy(bp, cp + 1, len); bp[len] = 0; cls = PgObject_getJavaClass(bp); pfree(bp); } else cls = PgObject_getJavaClass(cp); typeClass->javaClass = JNI_newGlobalRef(cls); JNI_deleteLocalRef(cls); } return typeClass->javaClass; } const char* Type_getJavaTypeName(Type self) { return self->typeClass->javaTypeName; } const char* Type_getJNISignature(Type self) { return self->typeClass->getJNISignature(self); } const char* Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return self->typeClass->getJNIReturnSignature(self, forMultiCall, useAltRepr); } Type Type_getArrayType(Type self, Oid arrayTypeId) { Type arrayType = self->arrayType; if(arrayType != 0) { if(arrayType->typeId == arrayTypeId) return arrayType; if(arrayType->typeId == InvalidOid) { arrayType->typeId = arrayTypeId; return arrayType; } } arrayType = self->typeClass->createArrayType(self, arrayTypeId); self->arrayType = arrayType; return arrayType; } Type Type_getElementType(Type self) { return self->elementType; } Type Type_getObjectType(Type self) { return self->objectType; } Type Type_getRealType(Type self, Oid realTypeId, jobject typeMap) { return self->typeClass->getRealType(self, realTypeId, typeMap); } Oid Type_getOid(Type self) { return self->typeId; } TupleDesc Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getTupleDesc(self, fcinfo); } Datum Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return self->typeClass->invoke(self, cls, method, args, fcinfo); } Datum Type_invokeSRF(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; CallContextData* ctxData; FuncCallContext* context; MemoryContext currCtx; /* stuff done only on the first call of the function */ if(SRF_IS_FIRSTCALL()) { jobject tmp; /* create a function context for cross-call persistence */ context = SRF_FIRSTCALL_INIT(); currCtx = MemoryContextSwitchTo(context->multi_call_memory_ctx); /* Call the declared Java function. It returns an instance that can produce * the rows. */ tmp = Type_getSRFProducer(self, cls, method, args); if(tmp == 0) { Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); fcinfo->isnull = true; SRF_RETURN_DONE(context); } ctxData = (CallContextData*)palloc(sizeof(CallContextData)); context->user_fctx = ctxData; ctxData->elemType = self; ctxData->rowProducer = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); /* Some row producers will need a writable result set in order * to produce the row. If one is needed, it's created here. */ tmp = Type_getSRFCollector(self, fcinfo); if(tmp == 0) ctxData->rowCollector = 0; else { ctxData->rowCollector = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); } ctxData->trusted = currentInvocation->trusted; ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; if(ctxData->hasConnected) ctxData->spiContext = CurrentMemoryContext; else ctxData->spiContext = 0; ctxData->rowContext = AllocSetContextCreate(context->multi_call_memory_ctx, "PL/Java row context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Register callback to be called when the function ends */ RegisterExprContextCallback(((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); MemoryContextSwitchTo(currCtx); } context = SRF_PERCALL_SETUP(); ctxData = (CallContextData*)context->user_fctx; MemoryContextReset(ctxData->rowContext); currCtx = MemoryContextSwitchTo(ctxData->rowContext); currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; hasRow = Type_hasNextSRF(self, ctxData->rowProducer, ctxData->rowCollector, (jint)context->call_cntr); ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; currentInvocation->hasConnected = false; currentInvocation->invocation = 0; if(hasRow) { Datum result = Type_nextSRF(self, ctxData->rowProducer, ctxData->rowCollector); MemoryContextSwitchTo(currCtx); SRF_RETURN_NEXT(context, result); } MemoryContextSwitchTo(currCtx); /* Unregister this callback and call it manually. We do this because * otherwise it will be called when the backend is in progress of * cleaning up Portals. If we close cursors (i.e. drop portals) in * the close, then that mechanism fails since attempts are made to * delete portals more then once. */ UnregisterExprContextCallback( ((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); _closeIteration(ctxData); /* This is the end of the set. */ SRF_RETURN_DONE(context); } bool Type_isPrimitive(Type self) { return self->objectType != 0; } Type Type_fromJavaType(Oid typeId, const char* javaTypeName) { CacheEntry ce = (CacheEntry)HashMap_getByString(s_obtainerByJavaName, javaTypeName); if(ce == 0) { int jtlen = strlen(javaTypeName) - 2; if(jtlen > 0 && strcmp("[]", javaTypeName + jtlen) == 0) { Type type; char* elemName = palloc(jtlen+1); memcpy(elemName, javaTypeName, jtlen); elemName[jtlen] = 0; type = Type_getArrayType(Type_fromJavaType(InvalidOid, elemName), typeId); pfree(elemName); return type; } ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("No java type mapping installed for \"%s\"", javaTypeName))); } return ce->type == 0 ? ce->obtainer(typeId == InvalidOid ? ce->typeId : typeId) : ce->type; } void Type_cacheByOid(Oid typeId, Type type) { HashMap_putByOid(s_typeByOid, typeId, type); } Type Type_fromOidCache(Oid typeId) { return (Type)HashMap_getByOid(s_typeByOid, typeId); } Type Type_fromOid(Oid typeId, jobject typeMap) { CacheEntry ce; HeapTuple typeTup; Form_pg_type typeStruct; Type type = Type_fromOidCache(typeId); if(type != 0) return type; typeTup = PgObject_getValidTuple(TYPEOID, typeId, "type"); typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if(typeStruct->typelem != 0 && typeStruct->typlen == -1) { type = Type_getArrayType(Type_fromOid(typeStruct->typelem, typeMap), typeId); goto finally; } /* For some reason, the anyarray is *not* an array with anyelement as the * element type. We'd like to see it that way though. */ if(typeId == ANYARRAYOID) { type = Type_getArrayType(Type_fromOid(ANYELEMENTOID, typeMap), typeId); goto finally; } if(typeStruct->typbasetype != 0) { /* Domain type, recurse using the base type (which in turn may * also be a domain) */ type = Type_fromOid(typeStruct->typbasetype, typeMap); goto finally; } if(typeMap != 0) { jobject joid = Oid_create(typeId); jclass typeClass = (jclass)JNI_callObjectMethod(typeMap, s_Map_get, joid); JNI_deleteLocalRef(joid); if(typeClass != 0) { TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); type = (Type)UDT_registerUDT(typeClass, typeId, typeStruct, tupleDesc, false); JNI_deleteLocalRef(typeClass); goto finally; } } /* Composite and record types will not have a TypeObtainer registered */ if(typeStruct->typtype == 'c' || (typeStruct->typtype == 'p' && typeId == RECORDOID)) { type = Composite_obtain(typeId); goto finally; } ce = (CacheEntry)HashMap_getByOid(s_obtainerByOid, typeId); if(ce == 0) /* * Default to String and standard textin/textout coersion. */ type = String_obtain(typeId); else { type = ce->type; if(type == 0) type = ce->obtainer(typeId); } finally: ReleaseSysCache(typeTup); Type_cacheByOid(typeId, type); return type; } Type Type_objectTypeFromOid(Oid typeId, jobject typeMap) { Type type = Type_fromOid(typeId, typeMap); Type objectType = type->objectType; return (objectType == 0) ? type : objectType; } bool _Type_canReplaceType(Type self, Type other) { return self->typeClass == other->typeClass; } Datum _Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { MemoryContext currCtx; Datum ret; jobject value = JNI_callStaticObjectMethodA(cls, method, args); if(value == 0) { fcinfo->isnull = true; return 0; } /* The return value cannot be created in the current context since it * goes out of scope when SPI_finish is called. */ currCtx = Invocation_switchToUpperContext(); ret = self->typeClass->coerceObject(self, value); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); return ret; } static Type _Type_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid(arrayTypeId, self); } static jobject _Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return JNI_callStaticObjectMethodA(cls, method, args); } static jobject _Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return 0; } static bool _Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return (JNI_callBooleanMethod(rowProducer, s_Iterator_hasNext) == JNI_TRUE); } static Datum _Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { jobject tmp = JNI_callObjectMethod(rowProducer, s_Iterator_next); Datum result = Type_coerceObject(self, tmp); JNI_deleteLocalRef(tmp); return result; } static void _Type_closeSRF(Type self, jobject rowProducer) { } jobject Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return self->typeClass->getSRFProducer(self, cls, method, args); } jobject Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getSRFCollector(self, fcinfo); } bool Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return self->typeClass->hasNextSRF(self, rowProducer, rowCollector, callCounter); } Datum Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { return self->typeClass->nextSRF(self, rowProducer, rowCollector); } void Type_closeSRF(Type self, jobject rowProducer) { self->typeClass->closeSRF(self, rowProducer); } static Type _Type_getRealType(Type self, Oid realId, jobject typeMap) { return self; } static const char* _Type_getJNISignature(Type self) { return self->typeClass->JNISignature; } static const char* _Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? "Ljava/util/Iterator;" : Type_getJNISignature(self); } TupleDesc _Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Type is not associated with a record"))); return 0; /* Keep compiler happy */ } /* * Shortcuts to initializers of known types */ extern void Any_initialize(void); extern void Coerce_initialize(void); extern void Void_initialize(void); extern void Boolean_initialize(void); extern void Byte_initialize(void); extern void Short_initialize(void); extern void Integer_initialize(void); extern void Long_initialize(void); extern void Float_initialize(void); extern void Double_initialize(void); extern void BigDecimal_initialize(void); extern void Date_initialize(void); extern void Time_initialize(void); extern void Timestamp_initialize(void); extern void Oid_initialize(void); extern void AclId_initialize(void); extern void ErrorData_initialize(void); extern void LargeObject_initialize(void); extern void String_initialize(void); extern void byte_array_initialize(void); extern void JavaWrapper_initialize(void); extern void ExecutionPlan_initialize(void); extern void Portal_initialize(void); extern void Relation_initialize(void); extern void TriggerData_initialize(void); extern void Tuple_initialize(void); extern void TupleDesc_initialize(void); extern void TupleTable_initialize(void); extern void HeapTupleHeader_initialize(void); extern void Composite_initialize(void); extern void Type_initialize(void); void Type_initialize(void) { s_typeByOid = HashMap_create(59, TopMemoryContext); s_obtainerByOid = HashMap_create(59, TopMemoryContext); s_obtainerByJavaName = HashMap_create(59, TopMemoryContext); String_initialize(); Any_initialize(); Coerce_initialize(); Void_initialize(); Boolean_initialize(); Byte_initialize(); Short_initialize(); Integer_initialize(); Long_initialize(); Float_initialize(); Double_initialize(); BigDecimal_initialize(); Date_initialize(); Time_initialize(); Timestamp_initialize(); Oid_initialize(); AclId_initialize(); ErrorData_initialize(); LargeObject_initialize(); byte_array_initialize(); JavaWrapper_initialize(); ExecutionPlan_initialize(); Portal_initialize(); TriggerData_initialize(); Relation_initialize(); TupleDesc_initialize(); Tuple_initialize(); TupleTable_initialize(); HeapTupleHeader_initialize(); Composite_initialize(); s_Map_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Map")); s_Map_get = PgObject_getJavaMethod(s_Map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); s_Iterator_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Iterator")); s_Iterator_hasNext = PgObject_getJavaMethod(s_Iterator_class, "hasNext", "()Z"); s_Iterator_next = PgObject_getJavaMethod(s_Iterator_class, "next", "()Ljava/lang/Object;"); } /* * Abstract Type constructor */ TypeClass TypeClass_alloc(const char* typeName) { return TypeClass_alloc2(typeName, sizeof(struct TypeClass_), sizeof(struct Type_)); } TypeClass TypeClass_alloc2(const char* typeName, Size classSize, Size instanceSize) { TypeClass self = (TypeClass)MemoryContextAlloc(TopMemoryContext, classSize); PgObjectClass_init((PgObjectClass)self, typeName, instanceSize, 0); self->JNISignature = ""; self->javaTypeName = ""; self->javaClass = 0; self->canReplaceType = _Type_canReplaceType; self->coerceDatum = (DatumCoercer)_PgObject_pureVirtualCalled; self->coerceObject = (ObjectCoercer)_PgObject_pureVirtualCalled; self->createArrayType = _Type_createArrayType; self->invoke = _Type_invoke; self->getSRFProducer = _Type_getSRFProducer; self->getSRFCollector = _Type_getSRFCollector; self->hasNextSRF = _Type_hasNextSRF; self->nextSRF = _Type_nextSRF; self->closeSRF = _Type_closeSRF; self->getTupleDesc = _Type_getTupleDesc; self->getJNISignature = _Type_getJNISignature; self->getJNIReturnSignature = _Type_getJNIReturnSignature; self->dynamic = false; self->outParameter = false; self->getRealType = _Type_getRealType; return self; } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance(TypeClass cls, Oid typeId) { return TypeClass_allocInstance2(cls, typeId, 0); } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance2(TypeClass cls, Oid typeId, Form_pg_type pgType) { Type t = (Type)PgObjectClass_allocInstance((PgObjectClass)(cls), TopMemoryContext); t->typeId = typeId; t->arrayType = 0; t->elementType = 0; t->objectType = 0; t->inCoercions = 0; t->outCoercions = 0; if(pgType != 0) { t->length = pgType->typlen; t->byValue = pgType->typbyval; t->align = pgType->typalign; } else if(typeId != InvalidOid) { get_typlenbyvalalign(typeId, &t->length, &t->byValue, &t->align); } else { t->length = 0; t->byValue = true; t->align = 'i'; } return t; } /* * Register this type. */ static void _registerType(Oid typeId, const char* javaTypeName, Type type, TypeObtainer obtainer) { CacheEntry ce = (CacheEntry)MemoryContextAlloc(TopMemoryContext, sizeof(CacheEntryData)); ce->typeId = typeId; ce->type = type; ce->obtainer = obtainer; if(javaTypeName != 0) HashMap_putByString(s_obtainerByJavaName, javaTypeName, ce); if(typeId != InvalidOid && HashMap_getByOid(s_obtainerByOid, typeId) == 0) HashMap_putByOid(s_obtainerByOid, typeId, ce); } void Type_registerType(const char* javaTypeName, Type type) { _registerType(type->typeId, javaTypeName, type, (TypeObtainer)_PgObject_pureVirtualCalled); } void Type_registerType2(Oid typeId, const char* javaTypeName, TypeObtainer obtainer) { _registerType(typeId, javaTypeName, 0, obtainer); } pljava-1.4.3/src/C/pljava/type/Float.c~0000644000014500000120000001023711634451404016744 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_floatClass; static jclass s_Float_class; static jmethodID s_Float_init; static jmethodID s_Float_floatValue; /* * float primitive type. */ static Datum _asDatum(jfloat v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Float4GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _float_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticFloatMethodA(cls, method, args)); } static jvalue _float_coerceDatum(Type self, Datum arg) { jvalue result; result.f = DatumGetFloat4(arg); return result; } static jvalue _floatArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jfloatArray floatArray = JNI_newFloatArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setFloatArrayRegion(floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jfloat* values = (jfloat*)ARR_DATA_PTR(v); jfloat* elems = JNI_getFloatArrayElements(floatArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseFloatArrayElements(floatArray, elems, JNI_COMMIT); } else JNI_setFloatArrayRegion(floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); #endif result.l = (jobject)floatArray; return result; } static Datum _floatArray_coerceObject(Type self, jobject floatArray) { ArrayType* v; jsize nElems; if(floatArray == 0) return 0; nElems = JNI_getArrayLength((jarray)floatArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jfloat), FLOAT4OID); #else v = createArrayType(nElems, sizeof(jfloat), FLOAT4OID, false); #endif JNI_getFloatArrayRegion((jfloatArray)floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Float type. */ static bool _Float_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_floatClass; } static jvalue _Float_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Float_class, s_Float_init, DatumGetFloat4(arg)); return result; } static Datum _Float_coerceObject(Type self, jobject floatObj) { return _asDatum(floatObj == 0 ? 0.0 : JNI_callFloatMethod(floatObj, s_Float_floatValue)); } static Type _float_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _floatArray_coerceDatum, _floatArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Float_initialize(void); void Float_initialize(void) { Type t_float; Type t_Float; TypeClass cls; s_Float_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Float")); s_Float_init = PgObject_getJavaMethod(s_Float_class, "", "(F)V"); s_Float_floatValue = PgObject_getJavaMethod(s_Float_class, "floatValue", "()F"); cls = TypeClass_alloc("type.Float"); cls->canReplaceType = _Float_canReplace; cls->JNISignature = "Ljava/lang/Float;"; cls->javaTypeName = "java.lang.Float"; cls->coerceDatum = _Float_coerceDatum; cls->coerceObject = _Float_coerceObject; t_Float = TypeClass_allocInstance(cls, FLOAT4OID); cls = TypeClass_alloc("type.float"); cls->JNISignature = "F"; cls->javaTypeName = "float"; cls->invoke = _float_invoke; cls->coerceDatum = _float_coerceDatum; cls->coerceObject = _Float_coerceObject; cls->createArrayType = _float_createArrayType; s_floatClass = cls; t_float = TypeClass_allocInstance(cls, FLOAT4OID); t_float->objectType = t_Float; Type_registerType("float", t_float); Type_registerType("java.lang.Float", t_Float); } pljava-1.4.3/src/C/pljava/type/Portal.c~0000644000014500000120000002012211634451404017132 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include "org_postgresql_pljava_internal_Portal.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/type/Type_priv.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Portal.h" #include "pljava/type/String.h" static jclass s_Portal_class; static jmethodID s_Portal_init; typedef void (*PortalCleanupProc)(Portal portal); static HashMap s_portalMap = 0; static PortalCleanupProc s_originalCleanupProc = 0; static void _pljavaPortalCleanup(Portal portal) { /* * Remove this object from the cache and clear its * handle. */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_deleteGlobalRef(jportal); } portal->cleanup = s_originalCleanupProc; if(s_originalCleanupProc != 0) { (*s_originalCleanupProc)(portal); } } /* * org.postgresql.pljava.type.Tuple type. */ jobject Portal_create(Portal portal) { jobject jportal = 0; if(portal != 0) { jportal = (jobject)HashMap_getByOpaque(s_portalMap, portal); if(jportal == 0) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = portal; /* We need to know when a portal is dropped so that we * don't attempt to drop it twice. */ if(s_originalCleanupProc == 0) s_originalCleanupProc = portal->cleanup; jportal = JNI_newObject(s_Portal_class, s_Portal_init, p2l.longVal); HashMap_putByOpaque(s_portalMap, portal, JNI_newGlobalRef(jportal)); /* * Fail the day the backend decides to utilize the pointer for multiple * purposes. */ Assert(portal->cleanup == s_originalCleanupProc); portal->cleanup = _pljavaPortalCleanup; } } return jportal; } /* Make this datatype available to the postgres system. */ extern void Portal_initialize(void); void Portal_initialize(void) { JNINativeMethod methods[] = { { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Portal__1getName }, { "_getPortalPos", "(J)I", Java_org_postgresql_pljava_internal_Portal__1getPortalPos }, { "_getTupleDesc", "(J)Lorg/postgresql/pljava/internal/TupleDesc;", Java_org_postgresql_pljava_internal_Portal__1getTupleDesc }, { "_fetch", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1fetch }, { "_close", "(J)V", Java_org_postgresql_pljava_internal_Portal__1close }, { "_isAtEnd", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtEnd }, { "_isAtStart", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtStart }, { "_isPosOverflow", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isPosOverflow }, { "_move", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1move }, { 0, 0, 0 } }; s_Portal_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Portal")); PgObject_registerNatives2(s_Portal_class, methods); s_Portal_init = PgObject_getJavaMethod(s_Portal_class, "", "(J)V"); s_portalMap = HashMap_create(13, TopMemoryContext); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Portal * Method: _getPortalPos * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1getPortalPos(JNIEnv* env, jclass clazz, jlong _this) { jint result = 0; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jint)((Portal)p2l.ptrVal)->portalPos; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _fetch * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1fetch(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_fetch((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_fetch"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Portal__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((Portal)p2l.ptrVal)->name); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getTupleDesc * Signature: (J)Lorg/postgresql/pljava/internal/TupleDesc; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Portal__1getTupleDesc(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = TupleDesc_create(((Portal)p2l.ptrVal)->tupDesc); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _invalidate * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Portal__1close(JNIEnv* env, jclass clazz, jlong _this) { /* We don't use error checking here since we don't want an exception * caused by another exception when we attempt to close. */ if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; BEGIN_NATIVE_NO_ERRCHECK Portal portal = (Portal)p2l.ptrVal; /* Reset our own cleanup callback if needed. No need to come in * the backway */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_deleteGlobalRef(jportal); } if(portal->cleanup == _pljavaPortalCleanup) portal->cleanup = s_originalCleanupProc; if(!(currentInvocation->errorOccured || currentInvocation->inExprContextCB)) SPI_cursor_close(portal); END_NATIVE } } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtStart * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtStart(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atStart; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtEnd * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtEnd(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atEnd; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isPosOverflow * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isPosOverflow(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->posOverflow; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _move * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1move(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_move((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_move"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } pljava-1.4.3/src/C/pljava/type/Void.c0000644000014500000120000000223311634451404016377 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" /* * void primitive type. */ static Datum _void_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { JNI_callStaticVoidMethodA(cls, method, args); fcinfo->isnull = true; return 0; } static jvalue _void_coerceDatum(Type self, Datum nothing) { jvalue result; result.j = 0L; return result; } static Datum _void_coerceObject(Type self, jobject nothing) { return 0; } /* Make this datatype available to the postgres system. */ extern void Void_initialize(void); void Void_initialize(void) { TypeClass cls = TypeClass_alloc("type.void"); cls->JNISignature = "V"; cls->javaTypeName = "void"; cls->invoke = _void_invoke; cls->coerceDatum = _void_coerceDatum; cls->coerceObject = _void_coerceObject; Type_registerType("void", TypeClass_allocInstance(cls, VOIDOID)); } pljava-1.4.3/src/C/pljava/type/byte_array.c0000644000014500000120000000540111634451404017637 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/Exception.h" #include "pljava/type/Type_priv.h" static jclass s_byteArray_class; static jclass s_BlobValue_class; static jmethodID s_BlobValue_length; static jmethodID s_BlobValue_getContents; /* * byte[] type. Copies data to/from a bytea struct. */ static jvalue _byte_array_coerceDatum(Type self, Datum arg) { jvalue result; bytea* bytes = DatumGetByteaP(arg); jsize length = VARSIZE(bytes) - VARHDRSZ; jbyteArray ba = JNI_newByteArray(length); JNI_setByteArrayRegion(ba, 0, length, (jbyte*)VARDATA(bytes)); result.l = ba; return result; } static Datum _byte_array_coerceObject(Type self, jobject byteArray) { bytea* bytes = 0; if(byteArray == 0) return 0; if(JNI_isInstanceOf(byteArray, s_byteArray_class)) { jsize length = JNI_getArrayLength((jarray)byteArray); int32 byteaSize = length + VARHDRSZ; bytes = (bytea*)palloc(byteaSize); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(bytes) = byteaSize; #else SET_VARSIZE(bytes, byteaSize); #endif JNI_getByteArrayRegion((jbyteArray)byteArray, 0, length, (jbyte*)VARDATA(bytes)); } else if(JNI_isInstanceOf(byteArray, s_BlobValue_class)) { jobject byteBuffer; int32 byteaSize; jlong length = JNI_callLongMethod(byteArray, s_BlobValue_length); byteaSize = (int32)(length + VARHDRSZ); bytes = (bytea*)palloc(byteaSize); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(bytes) = byteaSize; #else SET_VARSIZE(bytes, byteaSize); #endif byteBuffer = JNI_newDirectByteBuffer((void*)VARDATA(bytes), length); if(byteBuffer != 0) JNI_callVoidMethod(byteArray, s_BlobValue_getContents, byteBuffer); JNI_deleteLocalRef(byteBuffer); } else { Exception_throwIllegalArgument("Not coercable to bytea"); } PG_RETURN_BYTEA_P(bytes); } /* Make this datatype available to the postgres system. */ extern void byte_array_initialize(void); void byte_array_initialize(void) { TypeClass cls = TypeClass_alloc("type.byte[]"); cls->JNISignature = "[B"; cls->javaTypeName = "byte[]"; cls->coerceDatum = _byte_array_coerceDatum; cls->coerceObject = _byte_array_coerceObject; Type_registerType("byte[]", TypeClass_allocInstance(cls, BYTEAOID)); s_byteArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[B")); s_BlobValue_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/BlobValue")); s_BlobValue_length = PgObject_getJavaMethod(s_BlobValue_class, "length", "()J"); s_BlobValue_getContents = PgObject_getJavaMethod(s_BlobValue_class, "getContents", "(Ljava/nio/ByteBuffer;)V"); } pljava-1.4.3/src/C/pljava/type/Coerce.c0000644000014500000120000000622211634451404016700 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Coerce.h" #include "pljava/HashMap.h" #include "pljava/Invocation.h" static TypeClass s_coerceInClass; static TypeClass s_coerceOutClass; struct Coerce_ { /* * The UDT "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the String to a Type. */ struct Type_ Type_extension; Type innerType; Type outerType; FmgrInfo coerceFunction; }; typedef struct Coerce_* Coerce; static Datum _Coerce_invoke(Type type, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { Coerce self = (Coerce)type; Datum arg = Type_invoke(self->innerType, cls, method, args, fcinfo); if(arg != 0) { MemoryContext currCtx = Invocation_switchToUpperContext(); arg = FunctionCall1(&self->coerceFunction, arg); MemoryContextSwitchTo(currCtx); } return arg; } static jvalue _Coerce_coerceDatum(Type type, Datum arg) { jvalue result; Coerce self = (Coerce)type; if(arg == 0) result.j = 0; else result = Type_coerceDatum(self->innerType, FunctionCall1(&self->coerceFunction, arg)); return result; } static Datum _Coerce_coerceObject(Type type, jobject jval) { Coerce self = (Coerce)type; Datum arg = Type_coerceObject(self->innerType, jval); if(arg != 0) { MemoryContext currCtx = Invocation_switchToUpperContext(); arg = FunctionCall1(&self->coerceFunction, arg); MemoryContextSwitchTo(currCtx); } return arg; } static const char* _Coerce_getJNISignature(Type self) { return Type_getJNISignature(((Coerce)self)->innerType); } static Type _Coerce_create(TypeClass coerceClass, Type innerType, Type outerType, Oid coerceFunctionID) { Coerce self = (Coerce)TypeClass_allocInstance(coerceClass, Type_getOid(outerType)); fmgr_info_cxt(coerceFunctionID, &self->coerceFunction, GetMemoryChunkContext(self)); self->innerType = innerType; self->outerType = outerType; if(Type_isPrimitive(self->innerType)) ((Type)self)->objectType = _Coerce_create(coerceClass, Type_getObjectType(self->innerType), outerType, coerceFunctionID); return (Type)self; } Type Coerce_createIn(Type innerType, Type outerType, Oid coerceFunctionID) { return _Coerce_create(s_coerceInClass, innerType, outerType, coerceFunctionID); } Type Coerce_createOut(Type innerType, Type outerType, Oid coerceFunctionID) { return _Coerce_create(s_coerceOutClass, innerType, outerType, coerceFunctionID); } extern void Coerce_initialize(void); void Coerce_initialize(void) { s_coerceInClass = TypeClass_alloc2("type.CoerceIn", sizeof(struct TypeClass_), sizeof(struct Coerce_)); s_coerceInClass->getJNISignature = _Coerce_getJNISignature; s_coerceInClass->coerceDatum = _Coerce_coerceDatum; s_coerceOutClass = TypeClass_alloc2("type.CoerceOut", sizeof(struct TypeClass_), sizeof(struct Coerce_)); s_coerceOutClass->getJNISignature = _Coerce_getJNISignature; s_coerceOutClass->invoke = _Coerce_invoke; s_coerceOutClass->coerceObject = _Coerce_coerceObject; } pljava-1.4.3/src/C/pljava/type/Date.c0000644000014500000120000000376211634451404016363 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/Timestamp.h" #define EPOCH_DIFF (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) static jclass s_Date_class; static jmethodID s_Date_init; static jmethodID s_Date_getTime; /* * Date data type. Postgres will pass and expect number of days since * Jan 01 2000. Java uses number of millisecs since midnight Jan 01 1970. */ static jvalue _Date_coerceDatum(Type self, Datum arg) { DateADT pgDate = DatumGetDateADT(arg); int64 ts = (int64)pgDate * INT64CONST(86400000000); int tz = Timestamp_getTimeZone_id(ts); jlong date = (jlong)(pgDate + EPOCH_DIFF); jvalue result; date *= 86400L; // Convert to seconds date += tz; // Add local timezone result.l = JNI_newObject(s_Date_class, s_Date_init, date * 1000); return result; } static Datum _Date_coerceObject(Type self, jobject date) { jlong milliSecs = JNI_callLongMethod(date, s_Date_getTime) - INT64CONST(86400000) * EPOCH_DIFF; jlong secs = milliSecs / 1000 - Timestamp_getTimeZone_id(milliSecs * 1000); return DateADTGetDatum((DateADT)(secs / 86400)); } /* Make this datatype available to the postgres system. */ extern void Date_initialize(void); void Date_initialize(void) { TypeClass cls = TypeClass_alloc("type.Date"); cls->JNISignature = "Ljava/sql/Date;"; cls->javaTypeName = "java.sql.Date"; cls->coerceDatum = _Date_coerceDatum; cls->coerceObject = _Date_coerceObject; Type_registerType("java.sql.Date", TypeClass_allocInstance(cls, DATEOID)); s_Date_class = JNI_newGlobalRef(PgObject_getJavaClass("java/sql/Date")); s_Date_init = PgObject_getJavaMethod(s_Date_class, "", "(J)V"); s_Date_getTime = PgObject_getJavaMethod(s_Date_class, "getTime", "()J"); } pljava-1.4.3/src/C/pljava/type/JavaWrapper.c0000644000014500000120000000404511634451404017723 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "org_postgresql_pljava_internal_JavaWrapper.h" #include "pljava/type/Type_priv.h" #include "pljava/type/JavaWrapper.h" #include "pljava/Backend.h" #include "pljava/Exception.h" static jclass s_JavaWrapper_class; static jfieldID s_JavaWrapper_m_pointer; MemoryContext JavaMemoryContext; static jlong _getPointer(jobject managed) { if(managed == 0) { Exception_throw(ERRCODE_INTERNAL_ERROR, "Null JavaWrapper object"); return 0; } return JNI_getLongField(managed, s_JavaWrapper_m_pointer); } static Datum _JavaWrapper_coerceObject(Type self, jobject nStruct) { Ptr2Long p2l; p2l.longVal = _getPointer(nStruct); return PointerGetDatum(p2l.ptrVal); } TypeClass JavaWrapperClass_alloc(const char* name) { TypeClass self = TypeClass_alloc(name); self->coerceObject = _JavaWrapper_coerceObject; return self; } extern void JavaWrapper_initialize(void); void JavaWrapper_initialize(void) { JNINativeMethod methods[] = { { "_free", "(J)V", Java_org_postgresql_pljava_internal_JavaWrapper__1free }, { 0, 0, 0 } }; s_JavaWrapper_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/JavaWrapper")); PgObject_registerNatives2(s_JavaWrapper_class, methods); s_JavaWrapper_m_pointer = PgObject_getJavaField(s_JavaWrapper_class, "m_pointer", "J"); JavaMemoryContext = AllocSetContextCreate(TopMemoryContext, "PL/Java", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } /* * Class: org_postgresql_pljava_internal_JavaWrapper * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_JavaWrapper__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; pfree(p2l.ptrVal); END_NATIVE } pljava-1.4.3/src/C/pljava/type/Long.c~0000644000014500000120000001007311634451404016574 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_longClass; static jclass s_Long_class; static jmethodID s_Long_init; static jmethodID s_Long_longValue; /* * long primitive type. */ static Datum _asDatum(jlong v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Int64GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _long_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticLongMethodA(cls, method, args)); } static jvalue _long_coerceDatum(Type self, Datum arg) { jvalue result; result.j = DatumGetInt64(arg); return result; } static jvalue _longArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jlongArray longArray = JNI_newLongArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setLongArrayRegion(longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jlong* values = (jlong*)ARR_DATA_PTR(v); jlong* elems = JNI_getLongArrayElements(longArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseLongArrayElements(longArray, elems, JNI_COMMIT); } else JNI_setLongArrayRegion(longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); #endif result.l = (jobject)longArray; return result; } static Datum _longArray_coerceObject(Type self, jobject longArray) { ArrayType* v; jsize nElems; if(longArray == 0) return 0; nElems = JNI_getArrayLength((jarray)longArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jlong), INT8OID); #else v = createArrayType(nElems, sizeof(jlong), INT8OID, false); #endif JNI_getLongArrayRegion((jlongArray)longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Long type. */ static bool _Long_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_longClass; } static jvalue _Long_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Long_class, s_Long_init, DatumGetInt64(arg)); return result; } static Datum _Long_coerceObject(Type self, jobject longObj) { return _asDatum(longObj == 0 ? 0 : JNI_callLongMethod(longObj, s_Long_longValue)); } static Type _long_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _longArray_coerceDatum, _longArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Long_initialize(void); void Long_initialize(void) { Type t_long; Type t_Long; TypeClass cls; s_Long_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Long")); s_Long_init = PgObject_getJavaMethod(s_Long_class, "", "(J)V"); s_Long_longValue = PgObject_getJavaMethod(s_Long_class, "longValue", "()J"); cls = TypeClass_alloc("type.Long"); cls->canReplaceType = _Long_canReplace; cls->JNISignature = "Ljava/lang/Long;"; cls->javaTypeName = "java.lang.Long"; cls->coerceDatum = _Long_coerceDatum; cls->coerceObject = _Long_coerceObject; t_Long = TypeClass_allocInstance(cls, INT8OID); cls = TypeClass_alloc("type.long"); cls->JNISignature = "J"; cls->javaTypeName = "long"; cls->invoke = _long_invoke; cls->coerceDatum = _long_coerceDatum; cls->coerceObject = _Long_coerceObject; cls->createArrayType = _long_createArrayType; s_longClass = cls; t_long = TypeClass_allocInstance(cls, INT8OID); t_long->objectType = t_Long; Type_registerType("long", t_long); Type_registerType("java.lang.Long", t_Long); } pljava-1.4.3/src/C/pljava/type/ErrorData.c0000644000014500000120000002420711634451404017366 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "org_postgresql_pljava_internal_ErrorData.h" #include "pljava/Exception.h" #include "pljava/type/Type_priv.h" #include "pljava/type/ErrorData.h" #include "pljava/type/String.h" static jclass s_ErrorData_class; static jmethodID s_ErrorData_init; static jmethodID s_ErrorData_getNativePointer; jobject ErrorData_getCurrentError(void) { Ptr2Long p2l; jobject jed; MemoryContext curr = MemoryContextSwitchTo(JavaMemoryContext); ErrorData* errorData = CopyErrorData(); MemoryContextSwitchTo(curr); p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = errorData; jed = JNI_newObject(s_ErrorData_class, s_ErrorData_init, p2l.longVal); return jed; } ErrorData* ErrorData_getErrorData(jobject jed) { Ptr2Long p2l; p2l.longVal = JNI_callLongMethod(jed, s_ErrorData_getNativePointer); return (ErrorData*)p2l.ptrVal; } /* Make this datatype available to the postgres system. */ extern void ErrorData_initialize(void); void ErrorData_initialize(void) { JNINativeMethod methods[] = { { "_getErrorLevel", "(J)I", Java_org_postgresql_pljava_internal_ErrorData__1getErrorLevel }, { "_isOutputToServer", "(J)Z", Java_org_postgresql_pljava_internal_ErrorData__1isOutputToServer }, { "_isOutputToClient", "(J)Z", Java_org_postgresql_pljava_internal_ErrorData__1isOutputToClient }, { "_isShowFuncname", "(J)Z", Java_org_postgresql_pljava_internal_ErrorData__1isShowFuncname }, { "_getFilename", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getFilename }, { "_getLineno", "(J)I", Java_org_postgresql_pljava_internal_ErrorData__1getLineno }, { "_getFuncname", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getFuncname }, { "_getSqlState", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getSqlState }, { "_getMessage", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getMessage }, { "_getDetail", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getDetail }, { "_getHint", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getHint }, { "_getContextMessage", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getContextMessage }, { "_getCursorPos", "(J)I", Java_org_postgresql_pljava_internal_ErrorData__1getCursorPos }, { "_getInternalPos", "(J)I", Java_org_postgresql_pljava_internal_ErrorData__1getInternalPos }, { "_getInternalQuery", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_ErrorData__1getInternalQuery }, { "_getSavedErrno", "(J)I", Java_org_postgresql_pljava_internal_ErrorData__1getSavedErrno }, { "_free", "(J)V", Java_org_postgresql_pljava_internal_ErrorData__1free }, { 0, 0, 0 } }; s_ErrorData_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/ErrorData")); PgObject_registerNatives2(s_ErrorData_class, methods); s_ErrorData_init = PgObject_getJavaMethod(s_ErrorData_class, "", "(J)V"); s_ErrorData_getNativePointer = PgObject_getJavaMethod(s_ErrorData_class, "getNativePointer", "()J"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getErrorLevel * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getErrorLevel(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return ((ErrorData*)p2l.ptrVal)->elevel; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getMessage * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getMessage(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->message); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getSqlState * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getSqlState(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK char buf[6]; int errCode; int idx; Ptr2Long p2l; p2l.longVal = _this; /* unpack MAKE_SQLSTATE code */ errCode = ((ErrorData*)p2l.ptrVal)->sqlerrcode; for (idx = 0; idx < 5; ++idx) { buf[idx] = PGUNSIXBIT(errCode); errCode >>= 6; } buf[idx] = 0; result = String_createJavaStringFromNTS(buf); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: isOutputToServer * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_ErrorData__1isOutputToServer(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jboolean)((ErrorData*)p2l.ptrVal)->output_to_server; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: isOutputToClient * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_ErrorData__1isOutputToClient(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jboolean)((ErrorData*)p2l.ptrVal)->output_to_client; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: isShowFuncname * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_ErrorData__1isShowFuncname(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jboolean)((ErrorData*)p2l.ptrVal)->show_funcname; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getFilename * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getFilename(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->filename); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getLineno * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getLineno(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jint)((ErrorData*)p2l.ptrVal)->lineno; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getFuncname * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getFuncname(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->funcname); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getDetail * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getDetail(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->detail); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getHint * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getHint(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->hint); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getContextMessage * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getContextMessage(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->context); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getCursorPos * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getCursorPos(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jint)((ErrorData*)p2l.ptrVal)->cursorpos; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getInternalPos * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getInternalPos(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jint)((ErrorData*)p2l.ptrVal)->internalpos; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getInternalQuery * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getInternalQuery(JNIEnv* env, jclass cls, jlong _this) { jstring result = 0; BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((ErrorData*)p2l.ptrVal)->internalquery); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: getSavedErrno * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_ErrorData__1getSavedErrno(JNIEnv* env, jclass cls, jlong _this) { Ptr2Long p2l; p2l.longVal = _this; return (jint)((ErrorData*)p2l.ptrVal)->saved_errno; } /* * Class: org_postgresql_pljava_internal_ErrorData * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_ErrorData__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; FreeErrorData(p2l.ptrVal); END_NATIVE } pljava-1.4.3/src/C/pljava/type/Type.c~0000644000014500000120000005225111634451404016622 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include #include #include #include "pljava/type/String_priv.h" #include "pljava/type/Array.h" #include "pljava/type/Coerce.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" #include "pljava/type/UDT.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/SPI.h" static HashMap s_typeByOid; static HashMap s_obtainerByOid; static HashMap s_obtainerByJavaName; static jclass s_Map_class; static jmethodID s_Map_get; typedef struct CacheEntryData { Type type; TypeObtainer obtainer; Oid typeId; } CacheEntryData; typedef CacheEntryData* CacheEntry; static jclass s_Iterator_class; static jmethodID s_Iterator_hasNext; static jmethodID s_Iterator_next; /* Structure used in multi function calls (calls returning * SETOF ) */ typedef struct { Type elemType; jobject rowProducer; jobject rowCollector; jobject invocation; MemoryContext rowContext; MemoryContext spiContext; bool hasConnected; bool trusted; } CallContextData; static void _closeIteration(CallContextData* ctxData) { currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; Type_closeSRF(ctxData->elemType, ctxData->rowProducer); JNI_deleteGlobalRef(ctxData->rowProducer); if(ctxData->rowCollector != 0) JNI_deleteGlobalRef(ctxData->rowCollector); MemoryContextDelete(ctxData->rowContext); if(ctxData->hasConnected && ctxData->spiContext != 0) { /* Connect during SRF_IS_FIRSTCALL(). Switch context back to what * it was at that time and disconnect. */ MemoryContext currCtx = MemoryContextSwitchTo(ctxData->spiContext); Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); } } static void _endOfSetCB(Datum arg) { Invocation topCall; bool saveInExprCtxCB; CallContextData* ctxData = (CallContextData*)DatumGetPointer(arg); if(currentInvocation == 0) Invocation_pushInvocation(&topCall, ctxData->trusted); saveInExprCtxCB = currentInvocation->inExprContextCB; currentInvocation->inExprContextCB = true; _closeIteration(ctxData); currentInvocation->inExprContextCB = saveInExprCtxCB; } Type Type_getCoerceIn(Type self, Type other) { Oid funcId; Type coerce; Oid fromOid = other->typeId; Oid toOid = self->typeId; if(self->inCoercions != 0) { coerce = HashMap_getByOid(self->inCoercions, fromOid); if(coerce != 0) return coerce; } if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if(self->inCoercions == 0) self->inCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coerce = Coerce_createIn(self, other, funcId); HashMap_putByOid(self->inCoercions, fromOid, coerce); return coerce; } Type Type_getCoerceOut(Type self, Type other) { Oid funcId; Type coercer; Oid fromOid = self->typeId; Oid toOid = other->typeId; if(self->outCoercions != 0) { coercer = HashMap_getByOid(self->outCoercions, toOid); if(coercer != 0) return coercer; } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(self->outCoercions == 0) self->outCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coercer = Coerce_createOut(self, other, funcId); HashMap_putByOid(self->outCoercions, toOid, coercer); return coercer; } bool Type_canReplaceType(Type self, Type other) { return self->typeClass->canReplaceType(self, other); } bool Type_isDynamic(Type self) { return self->typeClass->dynamic; } bool Type_isOutParameter(Type self) { return self->typeClass->outParameter; } jvalue Type_coerceDatum(Type self, Datum value) { return self->typeClass->coerceDatum(self, value); } Datum Type_coerceObject(Type self, jobject object) { return self->typeClass->coerceObject(self, object); } char Type_getAlign(Type self) { return self->align; } TypeClass Type_getClass(Type self) { return self->typeClass; } int16 Type_getLength(Type self) { return self->length; } bool Type_isByValue(Type self) { return self->byValue; } jclass Type_getJavaClass(Type self) { TypeClass typeClass = self->typeClass; if(typeClass->javaClass == 0) { jclass cls; const char* cp = typeClass->JNISignature; if(cp == 0 || *cp == 0) ereport(ERROR, ( errmsg("Type '%s' has no corresponding java class", PgObjectClass_getName((PgObjectClass)typeClass)))); if(*cp == 'L') { /* L; should be just . Strange * since the L and ; are retained if its an array. */ int len = strlen(cp) - 2; char* bp = palloc(len + 1); memcpy(bp, cp + 1, len); bp[len] = 0; cls = PgObject_getJavaClass(bp); pfree(bp); } else cls = PgObject_getJavaClass(cp); typeClass->javaClass = JNI_newGlobalRef(cls); JNI_deleteLocalRef(cls); } return typeClass->javaClass; } const char* Type_getJavaTypeName(Type self) { return self->typeClass->javaTypeName; } const char* Type_getJNISignature(Type self) { return self->typeClass->getJNISignature(self); } const char* Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return self->typeClass->getJNIReturnSignature(self, forMultiCall, useAltRepr); } Type Type_getArrayType(Type self, Oid arrayTypeId) { Type arrayType = self->arrayType; if(arrayType != 0) { if(arrayType->typeId == arrayTypeId) return arrayType; if(arrayType->typeId == InvalidOid) { arrayType->typeId = arrayTypeId; return arrayType; } } arrayType = self->typeClass->createArrayType(self, arrayTypeId); self->arrayType = arrayType; return arrayType; } Type Type_getElementType(Type self) { return self->elementType; } Type Type_getObjectType(Type self) { return self->objectType; } Type Type_getRealType(Type self, Oid realTypeId, jobject typeMap) { return self->typeClass->getRealType(self, realTypeId, typeMap); } Oid Type_getOid(Type self) { return self->typeId; } TupleDesc Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getTupleDesc(self, fcinfo); } Datum Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return self->typeClass->invoke(self, cls, method, args, fcinfo); } Datum Type_invokeSRF(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; CallContextData* ctxData; FuncCallContext* context; MemoryContext currCtx; /* stuff done only on the first call of the function */ if(SRF_IS_FIRSTCALL()) { jobject tmp; /* create a function context for cross-call persistence */ context = SRF_FIRSTCALL_INIT(); currCtx = MemoryContextSwitchTo(context->multi_call_memory_ctx); /* Call the declared Java function. It returns an instance that can produce * the rows. */ tmp = Type_getSRFProducer(self, cls, method, args); if(tmp == 0) { Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); fcinfo->isnull = true; SRF_RETURN_DONE(context); } ctxData = (CallContextData*)palloc(sizeof(CallContextData)); context->user_fctx = ctxData; ctxData->elemType = self; ctxData->rowProducer = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); /* Some row producers will need a writable result set in order * to produce the row. If one is needed, it's created here. */ tmp = Type_getSRFCollector(self, fcinfo); if(tmp == 0) ctxData->rowCollector = 0; else { ctxData->rowCollector = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); } ctxData->trusted = currentInvocation->trusted; ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; if(ctxData->hasConnected) ctxData->spiContext = CurrentMemoryContext; else ctxData->spiContext = 0; ctxData->rowContext = AllocSetContextCreate(context->multi_call_memory_ctx, "PL/Java row context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Register callback to be called when the function ends */ RegisterExprContextCallback(((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); MemoryContextSwitchTo(currCtx); } context = SRF_PERCALL_SETUP(); ctxData = (CallContextData*)context->user_fctx; MemoryContextReset(ctxData->rowContext); currCtx = MemoryContextSwitchTo(ctxData->rowContext); currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; hasRow = Type_hasNextSRF(self, ctxData->rowProducer, ctxData->rowCollector, (jint)context->call_cntr); ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; currentInvocation->hasConnected = false; currentInvocation->invocation = 0; if(hasRow) { Datum result = Type_nextSRF(self, ctxData->rowProducer, ctxData->rowCollector); MemoryContextSwitchTo(currCtx); SRF_RETURN_NEXT(context, result); } MemoryContextSwitchTo(currCtx); /* Unregister this callback and call it manually. We do this because * otherwise it will be called when the backend is in progress of * cleaning up Portals. If we close cursors (i.e. drop portals) in * the close, then that mechanism fails since attempts are made to * delete portals more then once. */ UnregisterExprContextCallback( ((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); _closeIteration(ctxData); /* This is the end of the set. */ SRF_RETURN_DONE(context); } bool Type_isPrimitive(Type self) { return self->objectType != 0; } Type Type_fromJavaType(Oid typeId, const char* javaTypeName) { CacheEntry ce = (CacheEntry)HashMap_getByString(s_obtainerByJavaName, javaTypeName); if(ce == 0) { int jtlen = strlen(javaTypeName) - 2; if(jtlen > 0 && strcmp("[]", javaTypeName + jtlen) == 0) { Type type; char* elemName = palloc(jtlen+1); memcpy(elemName, javaTypeName, jtlen); elemName[jtlen] = 0; type = Type_getArrayType(Type_fromJavaType(InvalidOid, elemName), typeId); pfree(elemName); return type; } ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("No java type mapping installed for \"%s\"", javaTypeName))); } return ce->type == 0 ? ce->obtainer(typeId == InvalidOid ? ce->typeId : typeId) : ce->type; } void Type_cacheByOid(Oid typeId, Type type) { HashMap_putByOid(s_typeByOid, typeId, type); } Type Type_fromOidCache(Oid typeId) { return (Type)HashMap_getByOid(s_typeByOid, typeId); } Type Type_fromOid(Oid typeId, jobject typeMap) { CacheEntry ce; HeapTuple typeTup; Form_pg_type typeStruct; Type type = Type_fromOidCache(typeId); if(type != 0) return type; typeTup = PgObject_getValidTuple(TYPEOID, typeId, "type"); typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if(typeStruct->typelem != 0 && typeStruct->typlen == -1) { type = Type_getArrayType(Type_fromOid(typeStruct->typelem, typeMap), typeId); goto finally; } /* For some reason, the anyarray is *not* an array with anyelement as the * element type. We'd like to see it that way though. */ if(typeId == ANYARRAYOID) { type = Type_getArrayType(Type_fromOid(ANYELEMENTOID, typeMap), typeId); goto finally; } if(typeStruct->typbasetype != 0) { /* Domain type, recurse using the base type (which in turn may * also be a domain) */ type = Type_fromOid(typeStruct->typbasetype, typeMap); goto finally; } if(typeMap != 0) { jobject joid = Oid_create(typeId); jclass typeClass = (jclass)JNI_callObjectMethod(typeMap, s_Map_get, joid); JNI_deleteLocalRef(joid); if(typeClass != 0) { TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); type = (Type)UDT_registerUDT(typeClass, typeId, typeStruct, tupleDesc, false); JNI_deleteLocalRef(typeClass); goto finally; } } /* Composite and record types will not have a TypeObtainer registered */ if(typeStruct->typtype == 'c' || (typeStruct->typtype == 'p' && typeId == RECORDOID)) { type = Composite_obtain(typeId); goto finally; } ce = (CacheEntry)HashMap_getByOid(s_obtainerByOid, typeId); if(ce == 0) /* * Default to String and standard textin/textout coersion. */ type = String_obtain(typeId); else { type = ce->type; if(type == 0) type = ce->obtainer(typeId); } finally: ReleaseSysCache(typeTup); Type_cacheByOid(typeId, type); return type; } Type Type_objectTypeFromOid(Oid typeId, jobject typeMap) { Type type = Type_fromOid(typeId, typeMap); Type objectType = type->objectType; return (objectType == 0) ? type : objectType; } bool _Type_canReplaceType(Type self, Type other) { return self->typeClass == other->typeClass; } Datum _Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { MemoryContext currCtx; Datum ret; jobject value = JNI_callStaticObjectMethodA(cls, method, args); if(value == 0) { fcinfo->isnull = true; return 0; } /* The return value cannot be created in the current context since it * goes out of scope when SPI_finish is called. */ currCtx = Invocation_switchToUpperContext(); ret = self->typeClass->coerceObject(self, value); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); return ret; } static Type _Type_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid(arrayTypeId, self); } static jobject _Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return JNI_callStaticObjectMethodA(cls, method, args); } static jobject _Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return 0; } static bool _Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return (JNI_callBooleanMethod(rowProducer, s_Iterator_hasNext) == JNI_TRUE); } static Datum _Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { jobject tmp = JNI_callObjectMethod(rowProducer, s_Iterator_next); Datum result = Type_coerceObject(self, tmp); JNI_deleteLocalRef(tmp); return result; } static void _Type_closeSRF(Type self, jobject rowProducer) { } jobject Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return self->typeClass->getSRFProducer(self, cls, method, args); } jobject Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getSRFCollector(self, fcinfo); } bool Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return self->typeClass->hasNextSRF(self, rowProducer, rowCollector, callCounter); } Datum Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { return self->typeClass->nextSRF(self, rowProducer, rowCollector); } void Type_closeSRF(Type self, jobject rowProducer) { self->typeClass->closeSRF(self, rowProducer); } static Type _Type_getRealType(Type self, Oid realId, jobject typeMap) { return self; } static const char* _Type_getJNISignature(Type self) { return self->typeClass->JNISignature; } static const char* _Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? "Ljava/util/Iterator;" : Type_getJNISignature(self); } TupleDesc _Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Type is not associated with a record"))); return 0; /* Keep compiler happy */ } /* * Shortcuts to initializers of known types */ extern void Any_initialize(void); extern void Coerce_initialize(void); extern void Void_initialize(void); extern void Boolean_initialize(void); extern void Byte_initialize(void); extern void Short_initialize(void); extern void Integer_initialize(void); extern void Long_initialize(void); extern void Float_initialize(void); extern void Double_initialize(void); extern void BigDecimal_initialize(void); extern void Date_initialize(void); extern void Time_initialize(void); extern void Timestamp_initialize(void); extern void Oid_initialize(void); extern void AclId_initialize(void); extern void ErrorData_initialize(void); extern void LargeObject_initialize(void); extern void String_initialize(void); extern void byte_array_initialize(void); extern void JavaWrapper_initialize(void); extern void ExecutionPlan_initialize(void); extern void Portal_initialize(void); extern void Relation_initialize(void); extern void TriggerData_initialize(void); extern void Tuple_initialize(void); extern void TupleDesc_initialize(void); extern void TupleTable_initialize(void); extern void Composite_initialize(void); extern void Type_initialize(void); void Type_initialize(void) { s_typeByOid = HashMap_create(59, TopMemoryContext); s_obtainerByOid = HashMap_create(59, TopMemoryContext); s_obtainerByJavaName = HashMap_create(59, TopMemoryContext); String_initialize(); Any_initialize(); Coerce_initialize(); Void_initialize(); Boolean_initialize(); Byte_initialize(); Short_initialize(); Integer_initialize(); Long_initialize(); Float_initialize(); Double_initialize(); BigDecimal_initialize(); Date_initialize(); Time_initialize(); Timestamp_initialize(); Oid_initialize(); AclId_initialize(); ErrorData_initialize(); LargeObject_initialize(); byte_array_initialize(); JavaWrapper_initialize(); ExecutionPlan_initialize(); Portal_initialize(); TriggerData_initialize(); Relation_initialize(); TupleDesc_initialize(); Tuple_initialize(); TupleTable_initialize(); Composite_initialize(); s_Map_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Map")); s_Map_get = PgObject_getJavaMethod(s_Map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); s_Iterator_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Iterator")); s_Iterator_hasNext = PgObject_getJavaMethod(s_Iterator_class, "hasNext", "()Z"); s_Iterator_next = PgObject_getJavaMethod(s_Iterator_class, "next", "()Ljava/lang/Object;"); } /* * Abstract Type constructor */ TypeClass TypeClass_alloc(const char* typeName) { return TypeClass_alloc2(typeName, sizeof(struct TypeClass_), sizeof(struct Type_)); } TypeClass TypeClass_alloc2(const char* typeName, Size classSize, Size instanceSize) { TypeClass self = (TypeClass)MemoryContextAlloc(TopMemoryContext, classSize); PgObjectClass_init((PgObjectClass)self, typeName, instanceSize, 0); self->JNISignature = ""; self->javaTypeName = ""; self->javaClass = 0; self->canReplaceType = _Type_canReplaceType; self->coerceDatum = (DatumCoercer)_PgObject_pureVirtualCalled; self->coerceObject = (ObjectCoercer)_PgObject_pureVirtualCalled; self->createArrayType = _Type_createArrayType; self->invoke = _Type_invoke; self->getSRFProducer = _Type_getSRFProducer; self->getSRFCollector = _Type_getSRFCollector; self->hasNextSRF = _Type_hasNextSRF; self->nextSRF = _Type_nextSRF; self->closeSRF = _Type_closeSRF; self->getTupleDesc = _Type_getTupleDesc; self->getJNISignature = _Type_getJNISignature; self->getJNIReturnSignature = _Type_getJNIReturnSignature; self->dynamic = false; self->outParameter = false; self->getRealType = _Type_getRealType; return self; } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance(TypeClass cls, Oid typeId) { return TypeClass_allocInstance2(cls, typeId, 0); } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance2(TypeClass cls, Oid typeId, Form_pg_type pgType) { Type t = (Type)PgObjectClass_allocInstance((PgObjectClass)(cls), TopMemoryContext); t->typeId = typeId; t->arrayType = 0; t->elementType = 0; t->objectType = 0; t->inCoercions = 0; t->outCoercions = 0; if(pgType != 0) { t->length = pgType->typlen; t->byValue = pgType->typbyval; t->align = pgType->typalign; } else if(typeId != InvalidOid) { get_typlenbyvalalign(typeId, &t->length, &t->byValue, &t->align); } else { t->length = 0; t->byValue = true; t->align = 'i'; } return t; } /* * Register this type. */ static void _registerType(Oid typeId, const char* javaTypeName, Type type, TypeObtainer obtainer) { CacheEntry ce = (CacheEntry)MemoryContextAlloc(TopMemoryContext, sizeof(CacheEntryData)); ce->typeId = typeId; ce->type = type; ce->obtainer = obtainer; if(javaTypeName != 0) HashMap_putByString(s_obtainerByJavaName, javaTypeName, ce); if(typeId != InvalidOid && HashMap_getByOid(s_obtainerByOid, typeId) == 0) HashMap_putByOid(s_obtainerByOid, typeId, ce); } void Type_registerType(const char* javaTypeName, Type type) { _registerType(type->typeId, javaTypeName, type, (TypeObtainer)_PgObject_pureVirtualCalled); } void Type_registerType2(Oid typeId, const char* javaTypeName, TypeObtainer obtainer) { _registerType(typeId, javaTypeName, 0, obtainer); } pljava-1.4.3/src/C/pljava/type/Double.c~0000644000014500000120000001036411634451404017112 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_doubleClass; static jclass s_Double_class; static jmethodID s_Double_init; static jmethodID s_Double_doubleValue; /* * double primitive type. */ static Datum _asDatum(jdouble v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Float8GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _double_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticDoubleMethodA(cls, method, args)); } static jvalue _double_coerceDatum(Type self, Datum arg) { jvalue result; result.d = DatumGetFloat8(arg); return result; } static jvalue _doubleArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jdoubleArray doubleArray = JNI_newDoubleArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jdouble* values = (jdouble*)ARR_DATA_PTR(v); jdouble* elems = JNI_getDoubleArrayElements(doubleArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseDoubleArrayElements(doubleArray, elems, JNI_COMMIT); } else JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #endif result.l = (jobject)doubleArray; return result; } static Datum _doubleArray_coerceObject(Type self, jobject doubleArray) { ArrayType* v; jsize nElems; if(doubleArray == 0) return 0; nElems = JNI_getArrayLength((jarray)doubleArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jdouble), FLOAT8OID); #else v = createArrayType(nElems, sizeof(jdouble), FLOAT8OID, false); #endif JNI_getDoubleArrayRegion((jdoubleArray)doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Double type. */ static bool _Double_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_doubleClass; } static jvalue _Double_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Double_class, s_Double_init, DatumGetFloat8(arg)); return result; } static Datum _Double_coerceObject(Type self, jobject doubleObj) { return _asDatum(doubleObj == 0 ? 0 : JNI_callDoubleMethod(doubleObj, s_Double_doubleValue)); } static Type _double_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _doubleArray_coerceDatum, _doubleArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Double_initialize(void); void Double_initialize(void) { Type t_double; Type t_Double; TypeClass cls; s_Double_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Double")); s_Double_init = PgObject_getJavaMethod(s_Double_class, "", "(D)V"); s_Double_doubleValue = PgObject_getJavaMethod(s_Double_class, "doubleValue", "()D"); cls = TypeClass_alloc("type.Double"); cls->canReplaceType = _Double_canReplace; cls->JNISignature = "Ljava/lang/Double;"; cls->javaTypeName = "java.lang.Double"; cls->coerceDatum = _Double_coerceDatum; cls->coerceObject = _Double_coerceObject; t_Double = TypeClass_allocInstance(cls, FLOAT8OID); cls = TypeClass_alloc("type.double"); cls->JNISignature = "D"; cls->javaTypeName = "double"; cls->invoke = _double_invoke; cls->coerceDatum = _double_coerceDatum; cls->coerceObject = _Double_coerceObject; cls->createArrayType = _double_createArrayType; s_doubleClass = cls; t_double = TypeClass_allocInstance(cls, FLOAT8OID); t_double->objectType = t_Double; Type_registerType("double", t_double); Type_registerType("java.lang.Double", t_Double); } pljava-1.4.3/src/C/pljava/type/HeapTupleHeader.c~0000644000014500000120000000343011634451404020674 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/HeapTupleHeader.h" #include #include #include #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/TupleDesc.h" jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht) { return TupleDesc_create( lookup_rowtype_tupdesc( HeapTupleHeaderGetTypeId(ht), HeapTupleHeaderGetTypMod(ht))); } jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo) { jobject result = 0; HeapTupleHeader self = (HeapTupleHeader)Invocation_getWrappedPointer(hth); if(self != 0 && jtd != 0) { Ptr2Long p2l; p2l.longVal = jtd; BEGIN_NATIVE PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)attrNo); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute number \"%d\"", (int)attrNo); } else { Datum binVal; bool wasNull = false; Type type = Type_fromOid(typeId, Invocation_getTypeMap()); if(Type_isPrimitive(type)) /* * This is a primitive type */ type = Type_getObjectType(type); binVal = GetAttributeByNum(self, (AttrNumber)attrNo, &wasNull); if(!wasNull) result = Type_coerceDatum(type, binVal).l; } } PG_CATCH(); { Exception_throw_ERROR("GetAttributeByNum"); } PG_END_TRY(); END_NATIVE } return result; } void HeapTupleHeader_free(JNIEnv* env, jlong hth) { BEGIN_NATIVE_NO_ERRCHECK Invocation_freeLocalWrapper(hth); END_NATIVE } pljava-1.4.3/src/C/pljava/type/TupleTable.c0000644000014500000120000000400211634451404017533 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/TupleTable.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" static jclass s_TupleTable_class; static jmethodID s_TupleTable_init; jobject TupleTable_createFromSlot(TupleTableSlot* tts) { HeapTuple tuple; jobject tupdesc; jobjectArray tuples; MemoryContext curr; if(tts == 0) return 0; curr = MemoryContextSwitchTo(JavaMemoryContext); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) tupdesc = TupleDesc_internalCreate(tts->ttc_tupleDescriptor); tuple = heap_copytuple(tts->val); #else tupdesc = TupleDesc_internalCreate(tts->tts_tupleDescriptor); tuple = ExecCopySlotTuple(tts); #endif tuples = Tuple_createArray(&tuple, 1, false); MemoryContextSwitchTo(curr); return JNI_newObject(s_TupleTable_class, s_TupleTable_init, tupdesc, tuples); } jobject TupleTable_create(SPITupleTable* tts, jobject knownTD) { jobjectArray tuples; MemoryContext curr; if(tts == 0) return 0; curr = MemoryContextSwitchTo(JavaMemoryContext); if(knownTD == 0) knownTD = TupleDesc_internalCreate(tts->tupdesc); tuples = Tuple_createArray(tts->vals, (jint)(tts->alloced - tts->free), true); MemoryContextSwitchTo(curr); return JNI_newObject(s_TupleTable_class, s_TupleTable_init, knownTD, tuples); } /* Make this datatype available to the postgres system. */ extern void TupleTable_initialize(void); void TupleTable_initialize(void) { s_TupleTable_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/TupleTable")); s_TupleTable_init = PgObject_getJavaMethod( s_TupleTable_class, "", "(Lorg/postgresql/pljava/internal/TupleDesc;[Lorg/postgresql/pljava/internal/Tuple;)V"); } pljava-1.4.3/src/C/pljava/type/UDT.c0000644000014500000120000002440411634451404016136 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include #if PGSQL_MAJOR_VER > 8 #include #endif #include "pljava/type/UDT_priv.h" #include "pljava/type/String.h" #include "pljava/type/Tuple.h" #include "pljava/Invocation.h" #include "pljava/SQLInputFromChunk.h" #include "pljava/SQLOutputToChunk.h" #include "pljava/SQLInputFromTuple.h" #include "pljava/SQLOutputToTuple.h" static jobject coerceScalarDatum(UDT self, Datum arg) { jobject result; int16 dataLen = Type_getLength((Type)self); jclass javaClass = Type_getJavaClass((Type)self); if(dataLen == -2) { /* Data is a zero terminated string */ jstring jstr = String_createJavaStringFromNTS(DatumGetCString(arg)); result = JNI_callStaticObjectMethod(javaClass, self->parse, jstr, self->sqlTypeName); JNI_deleteLocalRef(jstr); } else { char* data; jobject inputStream; if(dataLen == -1) { /* Data is a varlena struct */ bytea* bytes = DatumGetByteaP(arg); dataLen = VARSIZE(bytes) - VARHDRSZ; data = VARDATA(bytes); } else { bool passByValue = Type_isByValue((Type)self); /* Data is a binary chunk of size dataLen */ if (passByValue) { /* pass by value data is stored in the least * significant bits of a Datum. */ #ifdef WORDS_BIGENDIAN data = ((char *)(&arg)) + SIZEOF_DATUM - dataLen; #else data = ((char *)(&arg)); #endif } else { data = DatumGetPointer(arg); } } result = JNI_newObject(javaClass, self->init); inputStream = SQLInputFromChunk_create(data, dataLen); JNI_callVoidMethod(result, self->readSQL, inputStream, self->sqlTypeName); SQLInputFromChunk_close(inputStream); } return result; } static jobject coerceTupleDatum(UDT udt, Datum arg) { jobject result = JNI_newObject(Type_getJavaClass((Type)udt), udt->init); jobject inputStream = SQLInputFromTuple_create(DatumGetHeapTupleHeader(arg), udt->tupleDesc); JNI_callVoidMethod(result, udt->readSQL, inputStream, udt->sqlTypeName); JNI_deleteLocalRef(inputStream); return result; } static Datum coerceScalarObject(UDT self, jobject value) { Datum result; int16 dataLen = Type_getLength((Type)self); if(dataLen == -2) { jstring jstr = (jstring)JNI_callObjectMethod(value, self->toString); char* tmp = String_createNTS(jstr); result = CStringGetDatum(tmp); JNI_deleteLocalRef(jstr); } else { jobject outputStream; StringInfoData buffer; bool passByValue = Type_isByValue((Type)self); MemoryContext currCtx = Invocation_switchToUpperContext(); initStringInfo(&buffer); if(dataLen < 0) /* * Reserve space for an int32 at the beginning. We are building * a varlena */ appendBinaryStringInfo(&buffer, (char*)&dataLen, sizeof(int32)); outputStream = SQLOutputToChunk_create(&buffer); JNI_callVoidMethod(value, self->writeSQL, outputStream); SQLOutputToChunk_close(outputStream); MemoryContextSwitchTo(currCtx); if(dataLen < 0) { /* Assign the correct length. */ #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(buffer.data) = buffer.len; #else SET_VARSIZE(buffer.data, buffer.len); #endif } else if(dataLen != buffer.len) { ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT for Oid %d produced image with incorrect size. Expected %d, was %d", Type_getOid((Type)self), dataLen, buffer.len))); } if (passByValue) { memset(&result, 0, SIZEOF_DATUM); /* pass by value data is stored in the least * significant bits of a Datum. */ #ifdef WORDS_BIGENDIAN memcpy(&result + SIZEOF_DATUM - dataLen, buffer.data, dataLen); #else memcpy(&result, buffer.data, dataLen); #endif } else { result = PointerGetDatum(buffer.data); } } return result; } static Datum coerceTupleObject(UDT self, jobject value) { Datum result = 0; if(value != 0) { HeapTuple tuple; jobject sqlOutput = SQLOutputToTuple_create(self->tupleDesc); JNI_callVoidMethod(value, self->writeSQL, sqlOutput); tuple = SQLOutputToTuple_getTuple(sqlOutput); if(tuple != 0) result = HeapTupleGetDatum(tuple); } return result; } jvalue _UDT_coerceDatum(Type self, Datum arg) { jvalue result; UDT udt = (UDT)self; if(UDT_isScalar(udt)) result.l = coerceScalarDatum(udt, arg); else result.l = coerceTupleDatum(udt, arg); return result; } Datum _UDT_coerceObject(Type self, jobject value) { Datum result; UDT udt = (UDT)self; if(UDT_isScalar(udt)) result = coerceScalarObject(udt, value); else result = coerceTupleObject(udt, value); return result; } Datum UDT_input(UDT udt, PG_FUNCTION_ARGS) { jstring jstr; jobject obj; char* txt; if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); txt = PG_GETARG_CSTRING(0); if(Type_getLength((Type)udt) == -2) { if(txt != 0) txt = pstrdup(txt); PG_RETURN_CSTRING(txt); } jstr = String_createJavaStringFromNTS(txt); obj = JNI_callStaticObjectMethod(Type_getJavaClass((Type)udt), udt->parse, jstr, udt->sqlTypeName); JNI_deleteLocalRef(jstr); return _UDT_coerceObject((Type)udt, obj); } Datum UDT_output(UDT udt, PG_FUNCTION_ARGS) { char* txt; if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); if(Type_getLength((Type)udt) == -2) { txt = PG_GETARG_CSTRING(0); if(txt != 0) txt = pstrdup(txt); } else { jobject value = _UDT_coerceDatum((Type)udt, PG_GETARG_DATUM(0)).l; jstring jstr = (jstring)JNI_callObjectMethod(value, udt->toString); MemoryContext currCtx = Invocation_switchToUpperContext(); txt = String_createNTS(jstr); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); JNI_deleteLocalRef(jstr); } PG_RETURN_CSTRING(txt); } Datum UDT_receive(UDT udt, PG_FUNCTION_ARGS) { StringInfo buf; char* tmp; int32 dataLen = Type_getLength((Type)udt); if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); if(dataLen == -1) return bytearecv(fcinfo); if(dataLen == -2) return unknownrecv(fcinfo); buf = (StringInfo)PG_GETARG_POINTER(0); tmp = palloc(dataLen); pq_copymsgbytes(buf, tmp, dataLen); PG_RETURN_POINTER(tmp); } Datum UDT_send(UDT udt, PG_FUNCTION_ARGS) { StringInfoData buf; int32 dataLen = Type_getLength((Type)udt); if(!UDT_isScalar(udt)) ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("UDT with Oid %d is not scalar", Type_getOid((Type)udt)))); if(dataLen == -1) return byteasend(fcinfo); if(dataLen == -2) return unknownsend(fcinfo); pq_begintypsend(&buf); appendBinaryStringInfo(&buf, PG_GETARG_POINTER(0), dataLen); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } bool UDT_isScalar(UDT udt) { return udt->tupleDesc == 0; } /* Make this datatype available to the postgres system. */ UDT UDT_registerUDT(jclass clazz, Oid typeId, Form_pg_type pgType, TupleDesc td, bool isJavaBasedScalar) { jstring jcn; MemoryContext currCtx; HeapTuple nspTup; Form_pg_namespace nspStruct; TypeClass udtClass; UDT udt; int signatureLen; jstring sqlTypeName; char* className; char* classSignature; char* sp; const char* cp; const char* tp; char c; Type existing = Type_fromOidCache(typeId); if(existing != 0) { if(existing->typeClass->coerceDatum != _UDT_coerceDatum) { ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("Attempt to register UDT with Oid %d failed. Oid appoints a non UDT type", typeId))); } return (UDT)existing; } nspTup = PgObject_getValidTuple(NAMESPACEOID, pgType->typnamespace, "namespace"); nspStruct = (Form_pg_namespace)GETSTRUCT(nspTup); /* Concatenate namespace + '.' + typename */ cp = NameStr(nspStruct->nspname); tp = NameStr(pgType->typname); sp = palloc(strlen(cp) + strlen(tp) + 2); sprintf(sp, "%s.%s", cp, tp); sqlTypeName = String_createJavaStringFromNTS(sp); pfree(sp); ReleaseSysCache(nspTup); /* Create a Java Signature String from the class name */ jcn = JNI_callObjectMethod(clazz, Class_getName); currCtx = MemoryContextSwitchTo(TopMemoryContext); className = String_createNTS(jcn); JNI_deleteLocalRef(jcn); signatureLen = strlen(className) + 2; classSignature = palloc(signatureLen + 1); MemoryContextSwitchTo(currCtx); sp = classSignature; cp = className; *sp++ = 'L'; while((c = *cp++) != 0) { if(c == '.') c = '/'; *sp++ = c; } *sp++ = ';'; *sp = 0; udtClass = TypeClass_alloc2("type.UDT", sizeof(struct TypeClass_), sizeof(struct UDT_)); udtClass->JNISignature = classSignature; udtClass->javaTypeName = className; udtClass->javaClass = JNI_newGlobalRef(clazz); udtClass->canReplaceType = _Type_canReplaceType; udtClass->coerceDatum = _UDT_coerceDatum; udtClass->coerceObject = _UDT_coerceObject; udt = (UDT)TypeClass_allocInstance2(udtClass, typeId, pgType); udt->sqlTypeName = JNI_newGlobalRef(sqlTypeName); JNI_deleteLocalRef(sqlTypeName); udt->init = PgObject_getJavaMethod(clazz, "", "()V"); if(isJavaBasedScalar) { /* A scalar mapping that is implemented in Java will have the static method: * * T parse(String stringRep, String sqlTypeName); * * and a matching: * * String toString(); * * instance method. A pure mapping (i.e. no Java I/O methods) will not have * this. */ udt->toString = PgObject_getJavaMethod(clazz, "toString", "()Ljava/lang/String;"); /* The parse method is a static method on the class with the signature * (Ljava/lang/String;Ljava/lang/String;) */ sp = palloc(signatureLen + 40); strcpy(sp, "(Ljava/lang/String;Ljava/lang/String;)"); strcpy(sp + 38, classSignature); udt->parse = PgObject_getStaticJavaMethod(clazz, "parse", sp); pfree(sp); } else { udt->toString = 0; udt->parse = 0; } udt->tupleDesc = td; udt->readSQL = PgObject_getJavaMethod(clazz, "readSQL", "(Ljava/sql/SQLInput;Ljava/lang/String;)V"); udt->writeSQL = PgObject_getJavaMethod(clazz, "writeSQL", "(Ljava/sql/SQLOutput;)V"); Type_registerType(className, (Type)udt); return udt; } pljava-1.4.3/src/C/pljava/type/Byte.c0000644000014500000120000001020611634451404016400 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" /* The byte maps to the postgres type "char", i.e. the * 8 bit, one byte quantity. The java byte was chosen instead of * char since a Java char is UTF-16 and "char" is not in any way * subject to character set encodings. */ static TypeClass s_byteClass; static jclass s_Byte_class; static jmethodID s_Byte_init; static jmethodID s_Byte_byteValue; /* * byte primitive type. */ static Datum _byte_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jbyte v = JNI_callStaticByteMethodA(cls, method, args); return CharGetDatum(v); } static jvalue _byte_coerceDatum(Type self, Datum arg) { jvalue result; result.b = DatumGetChar(arg); return result; } static jvalue _byteArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jbyteArray byteArray = JNI_newByteArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setByteArrayRegion(byteArray, 0, nElems, (jbyte*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jbyte* values = (jbyte*)ARR_DATA_PTR(v); jbyte* elems = JNI_getByteArrayElements(byteArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseByteArrayElements(byteArray, elems, JNI_COMMIT); } else JNI_setByteArrayRegion(byteArray, 0, nElems, (jbyte*)ARR_DATA_PTR(v)); #endif result.l = (jobject)byteArray; return result; } static Datum _byteArray_coerceObject(Type self, jobject byteArray) { ArrayType* v; jsize nElems; if(byteArray == 0) return 0; nElems = JNI_getArrayLength((jarray)byteArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jbyte), CHAROID); #else v = createArrayType(nElems, sizeof(jbyte), CHAROID, false); #endif JNI_getByteArrayRegion((jbyteArray)byteArray, 0, nElems, (jbyte*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Byte type. */ static bool _Byte_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_byteClass; } static jvalue _Byte_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Byte_class, s_Byte_init, DatumGetChar(arg)); return result; } static Datum _Byte_coerceObject(Type self, jobject byteObj) { return CharGetDatum(byteObj == 0 ? 0 : JNI_callByteMethod(byteObj, s_Byte_byteValue)); } static Type _byte_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _byteArray_coerceDatum, _byteArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Byte_initialize(void); void Byte_initialize(void) { Type t_byte; Type t_Byte; TypeClass cls; s_Byte_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Byte")); s_Byte_init = PgObject_getJavaMethod(s_Byte_class, "", "(B)V"); s_Byte_byteValue = PgObject_getJavaMethod(s_Byte_class, "byteValue", "()B"); cls = TypeClass_alloc("type.Byte"); cls->canReplaceType = _Byte_canReplace; cls->JNISignature = "Ljava/lang/Byte;"; cls->javaTypeName = "java.lang.Byte"; cls->coerceDatum = _Byte_coerceDatum; cls->coerceObject = _Byte_coerceObject; t_Byte = TypeClass_allocInstance(cls, CHAROID); cls = TypeClass_alloc("type.byte"); cls->JNISignature = "B"; cls->javaTypeName = "byte"; cls->invoke = _byte_invoke; cls->coerceDatum = _byte_coerceDatum; cls->coerceObject = _Byte_coerceObject; cls->createArrayType = _byte_createArrayType; s_byteClass = cls; t_byte = TypeClass_allocInstance(cls, CHAROID); t_byte->objectType = t_Byte; Type_registerType("byte", t_byte); Type_registerType("java.lang.Byte", t_Byte); } pljava-1.4.3/src/C/pljava/type/TupleDesc.c0000644000014500000120000001602411634451404017371 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_TupleDesc.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/Type_priv.h" #include "pljava/type/String.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" static jclass s_TupleDesc_class; static jmethodID s_TupleDesc_init; /* * org.postgresql.pljava.TupleDesc type. */ jobject TupleDesc_create(TupleDesc td) { jobject jtd = 0; if(td != 0) { MemoryContext curr = MemoryContextSwitchTo(JavaMemoryContext); jtd = TupleDesc_internalCreate(td); MemoryContextSwitchTo(curr); } return jtd; } jobject TupleDesc_internalCreate(TupleDesc td) { jobject jtd; Ptr2Long tdH; td = CreateTupleDescCopyConstr(td); tdH.longVal = 0L; /* ensure that the rest is zeroed out */ tdH.ptrVal = td; jtd = JNI_newObject(s_TupleDesc_class, s_TupleDesc_init, tdH.longVal, (jint)td->natts); return jtd; } Type TupleDesc_getColumnType(TupleDesc tupleDesc, int index) { Type type; Oid typeId = SPI_gettypeid(tupleDesc, index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); type = 0; } else type = Type_objectTypeFromOid(typeId, Invocation_getTypeMap()); return type; } static jvalue _TupleDesc_coerceDatum(Type self, Datum arg) { jvalue result; result.l = TupleDesc_create((TupleDesc)DatumGetPointer(arg)); return result; } /* Make this datatype available to the postgres system. */ extern void TupleDesc_initialize(void); void TupleDesc_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_getColumnName", "(JI)Ljava/lang/String;", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName }, { "_getColumnIndex", "(JLjava/lang/String;)I", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex }, { "_formTuple", "(J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_TupleDesc__1formTuple }, { "_getOid", "(JI)Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_TupleDesc__1getOid }, { "_free", "(J)V", Java_org_postgresql_pljava_internal_TupleDesc__1free }, { 0, 0, 0 }}; s_TupleDesc_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/TupleDesc")); PgObject_registerNatives2(s_TupleDesc_class, methods); s_TupleDesc_init = PgObject_getJavaMethod(s_TupleDesc_class, "", "(JI)V"); cls = JavaWrapperClass_alloc("type.TupleDesc"); cls->JNISignature = "Lorg/postgresql/pljava/internal/TupleDesc;"; cls->javaTypeName = "org.postgresql.pljava.internal.TupleDesc"; cls->coerceDatum = _TupleDesc_coerceDatum; Type_registerType("org.postgresql.pljava.internal.TupleDesc", TypeClass_allocInstance(cls, InvalidOid)); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnName * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName(JNIEnv* env, jclass cls, jlong _this, jint index) { jstring result = 0; BEGIN_NATIVE PG_TRY(); { char* name; Ptr2Long p2l; p2l.longVal = _this; name = SPI_fname((TupleDesc)p2l.ptrVal, (int)index); if(name == 0) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = String_createJavaStringFromNTS(name); pfree(name); } } PG_CATCH(); { Exception_throw_ERROR("SPI_fname"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnIndex * Signature: (JLjava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex(JNIEnv* env, jclass cls, jlong _this, jstring colName) { jint result = 0; BEGIN_NATIVE char* name = String_createNTS(colName); if(name != 0) { Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { result = SPI_fnumber((TupleDesc)p2l.ptrVal, name); if(result == SPI_ERROR_NOATTRIBUTE) { Exception_throw(ERRCODE_UNDEFINED_COLUMN, "Tuple has no attribute \"%s\"", name); } pfree(name); } PG_CATCH(); { Exception_throw_ERROR("SPI_fnumber"); } PG_END_TRY(); } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _formTuple * Signature: (J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1formTuple(JNIEnv* env, jclass cls, jlong _this, jobjectArray jvalues) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { jint idx; HeapTuple tuple; MemoryContext curr; TupleDesc self = (TupleDesc)p2l.ptrVal; int count = self->natts; Datum* values = (Datum*)palloc(count * sizeof(Datum)); char* nulls = palloc(count); jobject typeMap = Invocation_getTypeMap(); memset(values, 0, count * sizeof(Datum)); memset(nulls, 'n', count); /* all values null initially */ for(idx = 0; idx < count; ++idx) { jobject value = JNI_getObjectArrayElement(jvalues, idx); if(value != 0) { Type type = Type_fromOid(SPI_gettypeid(self, idx + 1), typeMap); values[idx] = Type_coerceObject(type, value); nulls[idx] = ' '; } } curr = MemoryContextSwitchTo(JavaMemoryContext); tuple = heap_formtuple(self, values, nulls); result = Tuple_internalCreate(tuple, false); MemoryContextSwitchTo(curr); pfree(values); pfree(nulls); } PG_CATCH(); { Exception_throw_ERROR("heap_formtuple"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; FreeTupleDesc((TupleDesc)p2l.ptrVal); END_NATIVE } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getOid * Signature: (JI)Lorg/postgresql/pljava/internal/Oid; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getOid(JNIEnv* env, jclass cls, jlong _this, jint index) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = Oid_create(typeId); } } PG_CATCH(); { Exception_throw_ERROR("SPI_gettypeid"); } PG_END_TRY(); END_NATIVE return result; } pljava-1.4.3/src/C/pljava/type/.#HeapTupleHeader.c.1.160000644000014500000120000000500711634451404021205 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/HeapTupleHeader.h" #include #include #include #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/type/TupleDesc.h" static HashMap s_TupleDescMap = 0; jobject HeapTupleHeader_getStaticTupleDesc(HeapTupleHeader ht) { TupleDesc td = lookup_rowtype_tupdesc( HeapTupleHeaderGetTypeId(ht), HeapTupleHeaderGetTypMod(ht)); jobject tupleDesc = HashMap_getByOpaque( s_TupleDescMap, td ); if(tupleDesc) { DecrTupleDescRefCount(td); return tupleDesc; } else { tupleDesc = JNI_newGlobalRef( TupleDesc_create( td )); HashMap_putByOpaque( s_TupleDescMap, td, tupleDesc ); return tupleDesc; } /* return TupleDesc_create( * lookup_rowtype_tupdesc( * HeapTupleHeaderGetTypeId(ht), * HeapTupleHeaderGetTypMod(ht))); */ } jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht) { return TupleDesc_create( lookup_rowtype_tupdesc( HeapTupleHeaderGetTypeId(ht), HeapTupleHeaderGetTypMod(ht))); } jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo) { jobject result = 0; HeapTupleHeader self = (HeapTupleHeader)Invocation_getWrappedPointer(hth); if(self != 0 && jtd != 0) { Ptr2Long p2l; p2l.longVal = jtd; BEGIN_NATIVE PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)attrNo); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute number \"%d\"", (int)attrNo); } else { Datum binVal; bool wasNull = false; Type type = Type_fromOid(typeId, Invocation_getTypeMap()); if(Type_isPrimitive(type)) /* * This is a primitive type */ type = Type_getObjectType(type); binVal = GetAttributeByNum(self, (AttrNumber)attrNo, &wasNull); if(!wasNull) result = Type_coerceDatum(type, binVal).l; } } PG_CATCH(); { Exception_throw_ERROR("GetAttributeByNum"); } PG_END_TRY(); END_NATIVE } return result; } void HeapTupleHeader_free(JNIEnv* env, jlong hth) { BEGIN_NATIVE_NO_ERRCHECK Invocation_freeLocalWrapper(hth); END_NATIVE } void HeapTupleHeader_initialize(void) { s_TupleDescMap = HashMap_create( 13, TopMemoryContext ); } pljava-1.4.3/src/C/pljava/type/Composite.c~0000644000014500000120000002353111634451404017642 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/HeapTupleHeader.h" #include "pljava/Invocation.h" #include "pljava/backports.h" #include "org_postgresql_pljava_jdbc_SingleRowReader.h" struct Composite_ { /* * The String "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the CompositeType to a Type. */ struct Type_ Type_extension; /* * The TupleDesc associated with the SETOF function. */ TupleDesc m_tupleDesc; }; typedef struct Composite_* Composite; static jclass s_ResultSetProvider_class; static jmethodID s_ResultSetProvider_assignRowValues; static jmethodID s_ResultSetProvider_close; static jclass s_ResultSetHandle_class; static jclass s_ResultSetPicker_class; static jmethodID s_ResultSetPicker_init; static jclass s_SingleRowReader_class; static jmethodID s_SingleRowReader_init; static jclass s_SingleRowWriter_class; static jmethodID s_SingleRowWriter_init; static jmethodID s_SingleRowWriter_getTupleAndClear; static TypeClass s_CompositeClass; static jobject _createWriter(jobject tupleDesc) { return JNI_newObject(s_SingleRowWriter_class, s_SingleRowWriter_init, tupleDesc); } static HeapTuple _getTupleAndClear(jobject jrps) { Ptr2Long p2l; if(jrps == 0) return 0; p2l.longVal = JNI_callLongMethod(jrps, s_SingleRowWriter_getTupleAndClear); return (HeapTuple)p2l.ptrVal; } /* * This function is a bit special in that it adds an additional parameter * to the parameter list (a java.sql.ResultSet implemented as a * SingleRowWriter) and calls a boolean method. It's assumed that the * SingleRowWriter has been initialized with values if the method returns * true. If so, the values are obtained in the form of a HeapTuple which in * turn is returned (as a Datum) from this method. */ static Datum _Composite_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; Datum result = 0; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); jobject jtd = TupleDesc_create(tupleDesc); jobject singleRowWriter = _createWriter(jtd); int numArgs = fcinfo->nargs; // Caller guarantees room for one extra slot // args[numArgs].l = singleRowWriter; hasRow = (JNI_callStaticBooleanMethodA(cls, method, args) == JNI_TRUE); if(hasRow) { /* Obtain tuple and return it as a Datum. Must be done using a more * durable context. */ MemoryContext currCtx = Invocation_switchToUpperContext(); HeapTuple tuple = _getTupleAndClear(singleRowWriter); result = HeapTupleGetDatum(tuple); MemoryContextSwitchTo(currCtx); } else fcinfo->isnull = true; JNI_deleteLocalRef(jtd); JNI_deleteLocalRef(singleRowWriter); return result; } static jobject _Composite_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { jobject tmp = JNI_callStaticObjectMethodA(cls, method, args); if(tmp != 0 && JNI_isInstanceOf(tmp, s_ResultSetHandle_class)) { jobject wrapper = JNI_newObject(s_ResultSetPicker_class, s_ResultSetPicker_init, tmp); JNI_deleteLocalRef(tmp); tmp = wrapper; } return tmp; } static jobject _Composite_getSRFCollector(Type self, PG_FUNCTION_ARGS) { jobject tmp1; jobject tmp2; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); if(tupleDesc == 0) ereport(ERROR, (errmsg("Unable to find tuple descriptor"))); tmp1 = TupleDesc_create(tupleDesc); tmp2 = _createWriter(tmp1); JNI_deleteLocalRef(tmp1); return tmp2; } static bool _Composite_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { /* Obtain next row using the RowCollector as a parameter to the * ResultSetProvider.assignRowValues method. */ return (JNI_callBooleanMethod(rowProducer, s_ResultSetProvider_assignRowValues, rowCollector, callCounter) == JNI_TRUE); } static Datum _Composite_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { Datum result = 0; HeapTuple tuple = _getTupleAndClear(rowCollector); if(tuple != 0) result = HeapTupleGetDatum(tuple); return result; } static void _Composite_closeSRF(Type self, jobject rowProducer) { JNI_callVoidMethod(rowProducer, s_ResultSetProvider_close); } /* Assume that the Datum is a HeapTupleHeader and convert it into * a SingleRowReader instance. */ static jvalue _Composite_coerceDatum(Type self, Datum arg) { jobject tupleDesc; jvalue result; jlong pointer; HeapTupleHeader hth = DatumGetHeapTupleHeader(arg); result.l = 0; if(hth == 0) return result; tupleDesc = HeapTupleHeader_getTupleDesc(hth); pointer = Invocation_createLocalWrapper(hth); result.l = JNI_newObject(s_SingleRowReader_class, s_SingleRowReader_init, pointer, tupleDesc); JNI_deleteLocalRef(tupleDesc); return result; } static TupleDesc createGlobalTupleDescCopy(TupleDesc td) { MemoryContext curr = MemoryContextSwitchTo(TopMemoryContext); td = CreateTupleDescCopyConstr(td); MemoryContextSwitchTo(curr); return td; } static TupleDesc _Composite_getTupleDesc(Type self, PG_FUNCTION_ARGS) { TupleDesc td = ((Composite)self)->m_tupleDesc; if(td != 0) return td; switch(get_call_result_type(fcinfo, 0, &td)) { case TYPEFUNC_COMPOSITE: case TYPEFUNC_RECORD: if(td->tdtypeid == RECORDOID) /* * We can't hold on to this one. It's anonymous * and may vary between calls. */ td = CreateTupleDescCopy(td); else { td = createGlobalTupleDescCopy(td); ((Composite)self)->m_tupleDesc = td; } break; default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } return td; } static const char* _Composite_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? (useAltRepr ? "Lorg/postgresql/pljava/ResultSetHandle;" : "Lorg/postgresql/pljava/ResultSetProvider;") : "Z"; } Type Composite_obtain(Oid typeId) { Composite infant = (Composite)TypeClass_allocInstance(s_CompositeClass, typeId); if(typeId == RECORDOID) infant->m_tupleDesc = 0; else { TupleDesc tmp = lookup_rowtype_tupdesc(typeId, -1); infant->m_tupleDesc = createGlobalTupleDescCopy(tmp); #if ((PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 2) || PGSQL_MAJOR_VER > 8) ReleaseTupleDesc(tmp); #endif } return (Type)infant; } /* Make this datatype available to the postgres system. */ extern void Composite_initialize(void); void Composite_initialize(void) { JNINativeMethod methods[] = { { "_getObject", "(JJI)Ljava/lang/Object;", Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject }, { "_free", "(J)V", Java_org_postgresql_pljava_jdbc_SingleRowReader__1free }, { 0, 0, 0 } }; s_SingleRowReader_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowReader")); PgObject_registerNatives2(s_SingleRowReader_class, methods); s_SingleRowReader_init = PgObject_getJavaMethod(s_SingleRowReader_class, "", "(JLorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowWriter")); s_SingleRowWriter_init = PgObject_getJavaMethod(s_SingleRowWriter_class, "", "(Lorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_getTupleAndClear = PgObject_getJavaMethod(s_SingleRowWriter_class, "getTupleAndClear", "()J"); s_ResultSetProvider_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetProvider")); s_ResultSetProvider_assignRowValues = PgObject_getJavaMethod(s_ResultSetProvider_class, "assignRowValues", "(Ljava/sql/ResultSet;I)Z"); s_ResultSetProvider_close = PgObject_getJavaMethod(s_ResultSetProvider_class, "close", "()V"); s_ResultSetHandle_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetHandle")); s_ResultSetPicker_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/ResultSetPicker")); s_ResultSetPicker_init = PgObject_getJavaMethod(s_ResultSetPicker_class, "", "(Lorg/postgresql/pljava/ResultSetHandle;)V"); s_CompositeClass = TypeClass_alloc2("type.Composite", sizeof(struct TypeClass_), sizeof(struct Composite_)); s_CompositeClass->JNISignature = "Ljava/sql/ResultSet;"; s_CompositeClass->javaTypeName = "java.sql.ResultSet"; s_CompositeClass->getTupleDesc = _Composite_getTupleDesc; s_CompositeClass->coerceDatum = _Composite_coerceDatum; s_CompositeClass->invoke = _Composite_invoke; s_CompositeClass->getSRFProducer = _Composite_getSRFProducer; s_CompositeClass->getSRFCollector = _Composite_getSRFCollector; s_CompositeClass->hasNextSRF = _Composite_hasNextSRF; s_CompositeClass->nextSRF = _Composite_nextSRF; s_CompositeClass->closeSRF = _Composite_closeSRF; s_CompositeClass->getJNIReturnSignature = _Composite_getJNIReturnSignature; s_CompositeClass->outParameter = true; Type_registerType2(InvalidOid, "java.sql.ResultSet", Composite_obtain); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1free(JNIEnv* env, jobject _this, jlong hth) { HeapTupleHeader_free(env, hth); } /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _getObject * Signature: (JJI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject(JNIEnv* env, jclass clazz, jlong hth, jlong jtd, jint attrNo) { return HeapTupleHeader_getObject(env, hth, jtd, attrNo); } pljava-1.4.3/src/C/pljava/type/.#Composite.c.1.50000644000014500000120000002415211634451404020067 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/HeapTupleHeader.h" #include "pljava/HashMap.h" #include "pljava/Invocation.h" #include "pljava/backports.h" #include "org_postgresql_pljava_jdbc_SingleRowReader.h" struct Composite_ { /* * The String "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the CompositeType to a Type. */ struct Type_ Type_extension; /* * The TupleDesc associated with the SETOF function. */ TupleDesc m_tupleDesc; }; typedef struct Composite_* Composite; static jclass s_ResultSetProvider_class; static jmethodID s_ResultSetProvider_assignRowValues; static jmethodID s_ResultSetProvider_close; static jclass s_ResultSetHandle_class; static jclass s_ResultSetPicker_class; static jmethodID s_ResultSetPicker_init; static jclass s_SingleRowReader_class; static jmethodID s_SingleRowReader_init; static jclass s_SingleRowWriter_class; static jmethodID s_SingleRowWriter_init; static jmethodID s_SingleRowWriter_getTupleAndClear; static TypeClass s_CompositeClass; static jobject _createWriter(jobject tupleDesc) { return JNI_newObject(s_SingleRowWriter_class, s_SingleRowWriter_init, tupleDesc); } static HeapTuple _getTupleAndClear(jobject jrps) { Ptr2Long p2l; if(jrps == 0) return 0; p2l.longVal = JNI_callLongMethod(jrps, s_SingleRowWriter_getTupleAndClear); return (HeapTuple)p2l.ptrVal; } /* * This function is a bit special in that it adds an additional parameter * to the parameter list (a java.sql.ResultSet implemented as a * SingleRowWriter) and calls a boolean method. It's assumed that the * SingleRowWriter has been initialized with values if the method returns * true. If so, the values are obtained in the form of a HeapTuple which in * turn is returned (as a Datum) from this method. */ static Datum _Composite_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; Datum result = 0; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); jobject jtd = TupleDesc_create(tupleDesc); jobject singleRowWriter = _createWriter(jtd); int numArgs = fcinfo->nargs; // Caller guarantees room for one extra slot // args[numArgs].l = singleRowWriter; hasRow = (JNI_callStaticBooleanMethodA(cls, method, args) == JNI_TRUE); if(hasRow) { /* Obtain tuple and return it as a Datum. Must be done using a more * durable context. */ MemoryContext currCtx = Invocation_switchToUpperContext(); HeapTuple tuple = _getTupleAndClear(singleRowWriter); result = HeapTupleGetDatum(tuple); MemoryContextSwitchTo(currCtx); } else fcinfo->isnull = true; JNI_deleteLocalRef(jtd); JNI_deleteLocalRef(singleRowWriter); return result; } static jobject _Composite_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { jobject tmp = JNI_callStaticObjectMethodA(cls, method, args); if(tmp != 0 && JNI_isInstanceOf(tmp, s_ResultSetHandle_class)) { jobject wrapper = JNI_newObject(s_ResultSetPicker_class, s_ResultSetPicker_init, tmp); JNI_deleteLocalRef(tmp); tmp = wrapper; } return tmp; } static jobject _Composite_getSRFCollector(Type self, PG_FUNCTION_ARGS) { jobject tmp1; jobject tmp2; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); if(tupleDesc == 0) ereport(ERROR, (errmsg("Unable to find tuple descriptor"))); tmp1 = TupleDesc_create(tupleDesc); tmp2 = _createWriter(tmp1); JNI_deleteLocalRef(tmp1); return tmp2; } static bool _Composite_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { /* Obtain next row using the RowCollector as a parameter to the * ResultSetProvider.assignRowValues method. */ return (JNI_callBooleanMethod(rowProducer, s_ResultSetProvider_assignRowValues, rowCollector, callCounter) == JNI_TRUE); } static Datum _Composite_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { Datum result = 0; HeapTuple tuple = _getTupleAndClear(rowCollector); if(tuple != 0) result = HeapTupleGetDatum(tuple); return result; } static void _Composite_closeSRF(Type self, jobject rowProducer) { JNI_callVoidMethod(rowProducer, s_ResultSetProvider_close); } /* Assume that the Datum is a HeapTupleHeader and convert it into * a SingleRowReader instance. */ static jvalue _Composite_coerceDatum(Type self, Datum arg) { jobject tupleDesc; jvalue result; jlong pointer; HeapTupleHeader hth = DatumGetHeapTupleHeader(arg); result.l = 0; if(hth == 0) return result; tupleDesc = HeapTupleHeader_getStaticTupleDesc(hth); /* * tupleDesc = HashMap_getByOpaque( s_TupleDescMap, hth ); * if ( tupleDesc == NULL ) * { * tupleDesc = JNI_newGlobalRef( HeapTupleHeader_getTupleDesc(hth) ); * HashMap_putByOpaque( s_TupleDescMap, hth, tupleDesc ); * } */ pointer = Invocation_createLocalWrapper(hth); result.l = JNI_newObject(s_SingleRowReader_class, s_SingleRowReader_init, pointer, tupleDesc); JNI_deleteLocalRef(tupleDesc); return result; } static TupleDesc createGlobalTupleDescCopy(TupleDesc td) { MemoryContext curr = MemoryContextSwitchTo(TopMemoryContext); td = CreateTupleDescCopyConstr(td); MemoryContextSwitchTo(curr); return td; } static TupleDesc _Composite_getTupleDesc(Type self, PG_FUNCTION_ARGS) { TupleDesc td = ((Composite)self)->m_tupleDesc; if(td != 0) return td; switch(get_call_result_type(fcinfo, 0, &td)) { case TYPEFUNC_COMPOSITE: case TYPEFUNC_RECORD: if(td->tdtypeid == RECORDOID) /* * We can't hold on to this one. It's anonymous * and may vary between calls. */ td = CreateTupleDescCopy(td); else { td = createGlobalTupleDescCopy(td); ((Composite)self)->m_tupleDesc = td; } break; default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } return td; } static const char* _Composite_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? (useAltRepr ? "Lorg/postgresql/pljava/ResultSetHandle;" : "Lorg/postgresql/pljava/ResultSetProvider;") : "Z"; } Type Composite_obtain(Oid typeId) { Composite infant = (Composite)TypeClass_allocInstance(s_CompositeClass, typeId); if(typeId == RECORDOID) infant->m_tupleDesc = 0; else { TupleDesc tmp = lookup_rowtype_tupdesc(typeId, -1); infant->m_tupleDesc = createGlobalTupleDescCopy(tmp); #if ((PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 2) || PGSQL_MAJOR_VER > 8) ReleaseTupleDesc(tmp); #endif } return (Type)infant; } /* Make this datatype available to the postgres system. */ extern void Composite_initialize(void); void Composite_initialize(void) { JNINativeMethod methods[] = { { "_getObject", "(JJI)Ljava/lang/Object;", Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject }, { "_free", "(J)V", Java_org_postgresql_pljava_jdbc_SingleRowReader__1free }, { 0, 0, 0 } }; s_SingleRowReader_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowReader")); PgObject_registerNatives2(s_SingleRowReader_class, methods); s_SingleRowReader_init = PgObject_getJavaMethod(s_SingleRowReader_class, "", "(JLorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowWriter")); s_SingleRowWriter_init = PgObject_getJavaMethod(s_SingleRowWriter_class, "", "(Lorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_getTupleAndClear = PgObject_getJavaMethod(s_SingleRowWriter_class, "getTupleAndClear", "()J"); s_ResultSetProvider_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetProvider")); s_ResultSetProvider_assignRowValues = PgObject_getJavaMethod(s_ResultSetProvider_class, "assignRowValues", "(Ljava/sql/ResultSet;I)Z"); s_ResultSetProvider_close = PgObject_getJavaMethod(s_ResultSetProvider_class, "close", "()V"); s_ResultSetHandle_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetHandle")); s_ResultSetPicker_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/ResultSetPicker")); s_ResultSetPicker_init = PgObject_getJavaMethod(s_ResultSetPicker_class, "", "(Lorg/postgresql/pljava/ResultSetHandle;)V"); s_CompositeClass = TypeClass_alloc2("type.Composite", sizeof(struct TypeClass_), sizeof(struct Composite_)); s_CompositeClass->JNISignature = "Ljava/sql/ResultSet;"; s_CompositeClass->javaTypeName = "java.sql.ResultSet"; s_CompositeClass->getTupleDesc = _Composite_getTupleDesc; s_CompositeClass->coerceDatum = _Composite_coerceDatum; s_CompositeClass->invoke = _Composite_invoke; s_CompositeClass->getSRFProducer = _Composite_getSRFProducer; s_CompositeClass->getSRFCollector = _Composite_getSRFCollector; s_CompositeClass->hasNextSRF = _Composite_hasNextSRF; s_CompositeClass->nextSRF = _Composite_nextSRF; s_CompositeClass->closeSRF = _Composite_closeSRF; s_CompositeClass->getJNIReturnSignature = _Composite_getJNIReturnSignature; s_CompositeClass->outParameter = true; Type_registerType2(InvalidOid, "java.sql.ResultSet", Composite_obtain); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1free(JNIEnv* env, jobject _this, jlong hth) { HeapTupleHeader_free(env, hth); } /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _getObject * Signature: (JJI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject(JNIEnv* env, jclass clazz, jlong hth, jlong jtd, jint attrNo) { return HeapTupleHeader_getObject(env, hth, jtd, attrNo); } pljava-1.4.3/src/C/pljava/type/Array.c0000644000014500000120000001341111634451404016554 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) void arraySetNull(bits8* bitmap, int offset, bool flag) { if(bitmap != 0) { int bitmask = 1 << (offset % 8); bitmap += offset / 8; if(flag) *bitmap &= ~bitmask; else *bitmap |= bitmask; } } bool arrayIsNull(const bits8* bitmap, int offset) { return bitmap == 0 ? false : !(bitmap[offset / 8] & (1 << (offset % 8))); } ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType, bool withNulls) #else ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType) #endif { ArrayType* v; int nBytes = elemSize * nElems; MemoryContext currCtx = Invocation_switchToUpperContext(); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) #define LEAFKEY (1<<31) nBytes += ARR_OVERHEAD(1); v = (ArrayType*)palloc0(nBytes); v->flags &= ~LEAFKEY; #else int dataoffset; if(withNulls) { dataoffset = ARR_OVERHEAD_WITHNULLS(1, nElems); nBytes += dataoffset; } else { dataoffset = 0; /* marker for no null bitmap */ nBytes += ARR_OVERHEAD_NONULLS(1); } v = (ArrayType*)palloc0(nBytes); v->dataoffset = dataoffset; #endif MemoryContextSwitchTo(currCtx); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) ARR_SIZE(v) = nBytes; #else SET_VARSIZE(v, nBytes); #endif ARR_NDIM(v) = 1; ARR_ELEMTYPE(v) = elemType; *((int*)ARR_DIMS(v)) = nElems; *((int*)ARR_LBOUND(v)) = 1; return v; } static jvalue _Array_coerceDatum(Type self, Datum arg) { jvalue result; jsize idx; Type elemType = Type_getElementType(self); int16 elemLength = Type_getLength(elemType); char elemAlign = Type_getAlign(elemType); bool elemByValue = Type_isByValue(elemType); ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jobjectArray objArray = JNI_newObjectArray(nElems, Type_getJavaClass(elemType), 0); const char* values = ARR_DATA_PTR(v); #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) bits8* nullBitMap = ARR_NULLBITMAP(v); #endif for(idx = 0; idx < nElems; ++idx) { #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); values = att_addlength(values, elemLength, PointerGetDatum(values)); values = (char*)att_align(values, elemAlign); #else if(arrayIsNull(nullBitMap, idx)) JNI_setObjectArrayElement(objArray, idx, 0); else { Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) values = att_addlength(values, elemLength, PointerGetDatum(values)); values = (char*)att_align(values, elemAlign); #else values = att_addlength_datum(values, elemLength, PointerGetDatum(values)); values = (char*)att_align_nominal(values, elemAlign); #endif } #endif } result.l = (jobject)objArray; return result; } static Datum _Array_coerceObject(Type self, jobject objArray) { ArrayType* v; jsize idx; int lowerBound = 1; Type elemType = Type_getElementType(self); int nElems = (int)JNI_getArrayLength((jarray)objArray); Datum* values = (Datum*)palloc(nElems * sizeof(Datum) + nElems * sizeof(bool)); bool* nulls = (bool*)(values + nElems); for(idx = 0; idx < nElems; ++idx) { jobject obj = JNI_getObjectArrayElement(objArray, idx); if(obj == 0) { nulls[idx] = true; values[idx] = 0; } else { nulls[idx] = false; values[idx] = Type_coerceObject(elemType, obj); JNI_deleteLocalRef(obj); } } v = construct_md_array( values, #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) nulls, #endif 1, &nElems, &lowerBound, Type_getOid(elemType), Type_getLength(elemType), Type_isByValue(elemType), Type_getAlign(elemType)); pfree(values); PG_RETURN_ARRAYTYPE_P(v); } static bool _Array_canReplaceType(Type self, Type other) { Type oe = Type_getElementType(other); return oe == 0 ? false : Type_canReplaceType(Type_getElementType(self), oe); } Type Array_fromOid(Oid typeId, Type elementType) { return Array_fromOid2(typeId, elementType, _Array_coerceDatum, _Array_coerceObject); } Type Array_fromOid2(Oid typeId, Type elementType, DatumCoercer coerceDatum, ObjectCoercer coerceObject) { Type self; TypeClass arrayClass; const char* elemClassName = PgObjectClass_getName(PgObject_getClass((PgObject)elementType)); const char* elemJNISignature = Type_getJNISignature(elementType); const char* elemJavaTypeName = Type_getJavaTypeName(elementType); MemoryContext currCtx = MemoryContextSwitchTo(TopMemoryContext); char* tmp = palloc(strlen(elemClassName) + 3); sprintf(tmp, "%s[]", elemClassName); arrayClass = TypeClass_alloc(tmp); tmp = palloc(strlen(elemJNISignature) + 2); sprintf(tmp, "[%s", elemJNISignature); arrayClass->JNISignature = tmp; tmp = palloc(strlen(elemJavaTypeName) + 3); sprintf(tmp, "%s[]", elemJavaTypeName); arrayClass->javaTypeName = tmp; arrayClass->coerceDatum = coerceDatum; arrayClass->coerceObject = coerceObject; arrayClass->canReplaceType = _Array_canReplaceType; self = TypeClass_allocInstance(arrayClass, typeId); MemoryContextSwitchTo(currCtx); self->elementType = elementType; Type_registerType(arrayClass->javaTypeName, self); if(Type_isPrimitive(elementType)) self->objectType = Array_fromOid(typeId, Type_getObjectType(elementType)); return self; } pljava-1.4.3/src/C/pljava/type/HeapTupleHeader.c0000644000014500000120000000343011634451404020476 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/HeapTupleHeader.h" #include #include #include #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/TupleDesc.h" jobject HeapTupleHeader_getTupleDesc(HeapTupleHeader ht) { return TupleDesc_create( lookup_rowtype_tupdesc( HeapTupleHeaderGetTypeId(ht), HeapTupleHeaderGetTypMod(ht))); } jobject HeapTupleHeader_getObject(JNIEnv* env, jlong hth, jlong jtd, jint attrNo) { jobject result = 0; HeapTupleHeader self = (HeapTupleHeader)Invocation_getWrappedPointer(hth); if(self != 0 && jtd != 0) { Ptr2Long p2l; p2l.longVal = jtd; BEGIN_NATIVE PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)attrNo); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute number \"%d\"", (int)attrNo); } else { Datum binVal; bool wasNull = false; Type type = Type_fromOid(typeId, Invocation_getTypeMap()); if(Type_isPrimitive(type)) /* * This is a primitive type */ type = Type_getObjectType(type); binVal = GetAttributeByNum(self, (AttrNumber)attrNo, &wasNull); if(!wasNull) result = Type_coerceDatum(type, binVal).l; } } PG_CATCH(); { Exception_throw_ERROR("GetAttributeByNum"); } PG_END_TRY(); END_NATIVE } return result; } void HeapTupleHeader_free(JNIEnv* env, jlong hth) { BEGIN_NATIVE_NO_ERRCHECK Invocation_freeLocalWrapper(hth); END_NATIVE } pljava-1.4.3/src/C/pljava/type/Boolean.c0000644000014500000120000001115411634451404017057 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_booleanClass; static jclass s_Boolean_class; static jclass s_BooleanArray_class; static jmethodID s_Boolean_init; static jmethodID s_Boolean_booleanValue; /* * boolean primitive type. */ static Datum _boolean_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jboolean v = JNI_callStaticBooleanMethodA(cls, method, args); return BoolGetDatum(v); } static jvalue _boolean_coerceDatum(Type self, Datum arg) { jvalue result; result.z = DatumGetBool(arg); return result; } static jvalue _booleanArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jbooleanArray booleanArray = JNI_newBooleanArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setBooleanArrayRegion(booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jboolean* values = (jboolean*)ARR_DATA_PTR(v); jboolean* elems = JNI_getBooleanArrayElements(booleanArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseBooleanArrayElements(booleanArray, elems, JNI_COMMIT); } else JNI_setBooleanArrayRegion(booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); #endif result.l = (jobject)booleanArray; return result; } static Datum _booleanArray_coerceObject(Type self, jobject booleanArray) { ArrayType* v; jsize nElems; if(booleanArray == 0) return 0; nElems = JNI_getArrayLength((jarray)booleanArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jboolean), BOOLOID); #else v = createArrayType(nElems, sizeof(jboolean), BOOLOID, false); #endif if(!JNI_isInstanceOf( booleanArray, s_BooleanArray_class)) JNI_getBooleanArrayRegion((jbooleanArray)booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); else { int idx = 0; jboolean *array = (jboolean*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callBooleanMethod(JNI_getObjectArrayElement(booleanArray, idx), s_Boolean_booleanValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Boolean type. */ static bool _Boolean_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_booleanClass; } static jvalue _Boolean_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Boolean_class, s_Boolean_init, DatumGetBool(arg)); return result; } static Datum _Boolean_coerceObject(Type self, jobject booleanObj) { return BoolGetDatum(booleanObj == 0 ? false : JNI_callBooleanMethod(booleanObj, s_Boolean_booleanValue) == JNI_TRUE); } static Type _boolean_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _booleanArray_coerceDatum, _booleanArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Boolean_initialize(void); void Boolean_initialize(void) { Type t_boolean; Type t_Boolean; TypeClass cls; s_Boolean_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Boolean")); s_BooleanArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Boolean;")); s_Boolean_init = PgObject_getJavaMethod(s_Boolean_class, "", "(Z)V"); s_Boolean_booleanValue = PgObject_getJavaMethod(s_Boolean_class, "booleanValue", "()Z"); cls = TypeClass_alloc("type.Boolean"); cls->canReplaceType = _Boolean_canReplace; cls->JNISignature = "Ljava/lang/Boolean;"; cls->javaTypeName = "java.lang.Boolean"; cls->coerceDatum = _Boolean_coerceDatum; cls->coerceObject = _Boolean_coerceObject; t_Boolean = TypeClass_allocInstance(cls, BOOLOID); cls = TypeClass_alloc("type.boolean"); cls->JNISignature = "Z"; cls->javaTypeName = "boolean"; cls->invoke = _boolean_invoke; cls->coerceDatum = _boolean_coerceDatum; cls->coerceObject = _Boolean_coerceObject; cls->createArrayType = _boolean_createArrayType; s_booleanClass = cls; t_boolean = TypeClass_allocInstance(cls, BOOLOID); t_boolean->objectType = t_Boolean; Type_registerType("boolean", t_boolean); Type_registerType("java.lang.Boolean", t_Boolean); } pljava-1.4.3/src/C/pljava/type/Tuple.c0000644000014500000120000000760611634451404016600 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_Tuple.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" static jclass s_Tuple_class; static jmethodID s_Tuple_init; /* * org.postgresql.pljava.type.Tuple type. */ jobject Tuple_create(HeapTuple ht) { jobject jht = 0; if(ht != 0) { MemoryContext curr = MemoryContextSwitchTo(JavaMemoryContext); jht = Tuple_internalCreate(ht, true); MemoryContextSwitchTo(curr); } return jht; } jobjectArray Tuple_createArray(HeapTuple* vals, jint size, bool mustCopy) { jobjectArray tuples = JNI_newObjectArray(size, s_Tuple_class, 0); while(--size >= 0) { jobject heapTuple = Tuple_internalCreate(vals[size], mustCopy); JNI_setObjectArrayElement(tuples, size, heapTuple); JNI_deleteLocalRef(heapTuple); } return tuples; } jobject Tuple_internalCreate(HeapTuple ht, bool mustCopy) { jobject jht; Ptr2Long htH; if(mustCopy) ht = heap_copytuple(ht); htH.longVal = 0L; /* ensure that the rest is zeroed out */ htH.ptrVal = ht; jht = JNI_newObject(s_Tuple_class, s_Tuple_init, htH.longVal); return jht; } static jvalue _Tuple_coerceDatum(Type self, Datum arg) { jvalue result; result.l = Tuple_create((HeapTuple)DatumGetPointer(arg)); return result; } /* Make this datatype available to the postgres system. */ extern void Tuple_initialize(void); void Tuple_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_getObject", "(JJI)Ljava/lang/Object;", Java_org_postgresql_pljava_internal_Tuple__1getObject }, { "_free", "(J)V", Java_org_postgresql_pljava_internal_Tuple__1free }, { 0, 0, 0 }}; s_Tuple_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Tuple")); PgObject_registerNatives2(s_Tuple_class, methods); s_Tuple_init = PgObject_getJavaMethod(s_Tuple_class, "", "(J)V"); cls = JavaWrapperClass_alloc("type.Tuple"); cls->JNISignature = "Lorg/postgresql/pljava/internal/Tuple;"; cls->javaTypeName = "org.postgresql.pljava.internal.Tuple"; cls->coerceDatum = _Tuple_coerceDatum; Type_registerType("org.postgresql.pljava.internal.Tuple", TypeClass_allocInstance(cls, InvalidOid)); } jobject Tuple_getObject(TupleDesc tupleDesc, HeapTuple tuple, int index) { jobject result = 0; PG_TRY(); { Type type = TupleDesc_getColumnType(tupleDesc, index); if(type != 0) { bool wasNull = false; Datum binVal = SPI_getbinval(tuple, tupleDesc, (int)index, &wasNull); if(!wasNull) result = Type_coerceDatum(type, binVal).l; } } PG_CATCH(); { Exception_throw_ERROR("SPI_getbinval"); } PG_END_TRY(); return result; } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Tuple * Method: _getObject * Signature: (JJI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Tuple__1getObject(JNIEnv* env, jclass cls, jlong _this, jlong _tupleDesc, jint index) { jobject result = 0; Ptr2Long p2l; p2l.longVal = _this; BEGIN_NATIVE HeapTuple self = (HeapTuple)p2l.ptrVal; p2l.longVal = _tupleDesc; result = Tuple_getObject((TupleDesc)p2l.ptrVal, self, (int)index); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_Tuple * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Tuple__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; heap_freetuple(p2l.ptrVal); END_NATIVE } pljava-1.4.3/src/C/pljava/type/TupleDesc.c~0000644000014500000120000001602411634451404017567 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_TupleDesc.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/Type_priv.h" #include "pljava/type/String.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" static jclass s_TupleDesc_class; static jmethodID s_TupleDesc_init; /* * org.postgresql.pljava.TupleDesc type. */ jobject TupleDesc_create(TupleDesc td) { jobject jtd = 0; if(td != 0) { MemoryContext curr = MemoryContextSwitchTo(JavaMemoryContext); jtd = TupleDesc_internalCreate(td); MemoryContextSwitchTo(curr); } return jtd; } jobject TupleDesc_internalCreate(TupleDesc td) { jobject jtd; Ptr2Long tdH; td = CreateTupleDescCopyConstr(td); tdH.longVal = 0L; /* ensure that the rest is zeroed out */ tdH.ptrVal = td; jtd = JNI_newObject(s_TupleDesc_class, s_TupleDesc_init, tdH.longVal, (jint)td->natts); return jtd; } Type TupleDesc_getColumnType(TupleDesc tupleDesc, int index) { Type type; Oid typeId = SPI_gettypeid(tupleDesc, index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); type = 0; } else type = Type_objectTypeFromOid(typeId, Invocation_getTypeMap()); return type; } static jvalue _TupleDesc_coerceDatum(Type self, Datum arg) { jvalue result; result.l = TupleDesc_create((TupleDesc)DatumGetPointer(arg)); return result; } /* Make this datatype available to the postgres system. */ extern void TupleDesc_initialize(void); void TupleDesc_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_getColumnName", "(JI)Ljava/lang/String;", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName }, { "_getColumnIndex", "(JLjava/lang/String;)I", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex }, { "_formTuple", "(J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_TupleDesc__1formTuple }, { "_getOid", "(JI)Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_TupleDesc__1getOid }, { "_free", "(J)V", Java_org_postgresql_pljava_internal_TupleDesc__1free }, { 0, 0, 0 }}; s_TupleDesc_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/TupleDesc")); PgObject_registerNatives2(s_TupleDesc_class, methods); s_TupleDesc_init = PgObject_getJavaMethod(s_TupleDesc_class, "", "(JI)V"); cls = JavaWrapperClass_alloc("type.TupleDesc"); cls->JNISignature = "Lorg/postgresql/pljava/internal/TupleDesc;"; cls->javaTypeName = "org.postgresql.pljava.internal.TupleDesc"; cls->coerceDatum = _TupleDesc_coerceDatum; Type_registerType("org.postgresql.pljava.internal.TupleDesc", TypeClass_allocInstance(cls, InvalidOid)); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnName * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName(JNIEnv* env, jclass cls, jlong _this, jint index) { jstring result = 0; BEGIN_NATIVE PG_TRY(); { char* name; Ptr2Long p2l; p2l.longVal = _this; name = SPI_fname((TupleDesc)p2l.ptrVal, (int)index); if(name == 0) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = String_createJavaStringFromNTS(name); pfree(name); } } PG_CATCH(); { Exception_throw_ERROR("SPI_fname"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnIndex * Signature: (JLjava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex(JNIEnv* env, jclass cls, jlong _this, jstring colName) { jint result = 0; BEGIN_NATIVE char* name = String_createNTS(colName); if(name != 0) { Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { result = SPI_fnumber((TupleDesc)p2l.ptrVal, name); if(result == SPI_ERROR_NOATTRIBUTE) { Exception_throw(ERRCODE_UNDEFINED_COLUMN, "Tuple has no attribute \"%s\"", name); } pfree(name); } PG_CATCH(); { Exception_throw_ERROR("SPI_fnumber"); } PG_END_TRY(); } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _formTuple * Signature: (J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1formTuple(JNIEnv* env, jclass cls, jlong _this, jobjectArray jvalues) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { jint idx; HeapTuple tuple; MemoryContext curr; TupleDesc self = (TupleDesc)p2l.ptrVal; int count = self->natts; Datum* values = (Datum*)palloc(count * sizeof(Datum)); char* nulls = palloc(count); jobject typeMap = Invocation_getTypeMap(); memset(values, 0, count * sizeof(Datum)); memset(nulls, 'n', count); /* all values null initially */ for(idx = 0; idx < count; ++idx) { jobject value = JNI_getObjectArrayElement(jvalues, idx); if(value != 0) { Type type = Type_fromOid(SPI_gettypeid(self, idx + 1), typeMap); values[idx] = Type_coerceObject(type, value); nulls[idx] = ' '; } } curr = MemoryContextSwitchTo(JavaMemoryContext); tuple = heap_formtuple(self, values, nulls); result = Tuple_internalCreate(tuple, false); MemoryContextSwitchTo(curr); pfree(values); pfree(nulls); } PG_CATCH(); { Exception_throw_ERROR("heap_formtuple"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; FreeTupleDesc((TupleDesc)p2l.ptrVal); END_NATIVE } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getOid * Signature: (JI)Lorg/postgresql/pljava/internal/Oid; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getOid(JNIEnv* env, jclass cls, jlong _this, jint index) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = Oid_create(typeId); } } PG_CATCH(); { Exception_throw_ERROR("SPI_gettypeid"); } PG_END_TRY(); END_NATIVE return result; } pljava-1.4.3/src/C/pljava/type/BigDecimal.c0000644000014500000120000000425211634451404017461 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/String_priv.h" /* * BigDecimal type. We use String conversions here. Perhaps there's * room for optimizations such as creating a 2's complement byte * array directly from the digits. Don't think we'd gain much though. */ static jclass s_BigDecimal_class; static jmethodID s_BigDecimal_init; static jmethodID s_BigDecimal_toString; static TypeClass s_BigDecimalClass; static jvalue _BigDecimal_coerceDatum(Type self, Datum arg) { jvalue result = _String_coerceDatum(self, arg); if(result.l != 0) result.l = JNI_newObject(s_BigDecimal_class, s_BigDecimal_init, result.l); return result; } static Datum _BigDecimal_coerceObject(Type self, jobject value) { jstring jstr = (jstring)JNI_callObjectMethod(value, s_BigDecimal_toString); Datum ret = _String_coerceObject(self, jstr); JNI_deleteLocalRef(jstr); return ret; } static Type BigDecimal_obtain(Oid typeId) { return (Type)StringClass_obtain(s_BigDecimalClass, typeId); } /* Make this datatype available to the postgres system. */ extern void BigDecimal_initialize(void); void BigDecimal_initialize(void) { s_BigDecimal_class = JNI_newGlobalRef(PgObject_getJavaClass("java/math/BigDecimal")); s_BigDecimal_init = PgObject_getJavaMethod(s_BigDecimal_class, "", "(Ljava/lang/String;)V"); s_BigDecimal_toString = PgObject_getJavaMethod(s_BigDecimal_class, "toString", "()Ljava/lang/String;"); s_BigDecimalClass = TypeClass_alloc2("type.BigDecimal", sizeof(struct TypeClass_), sizeof(struct String_)); s_BigDecimalClass->JNISignature = "Ljava/math/BigDecimal;"; s_BigDecimalClass->javaTypeName = "java.math.BigDecimal"; s_BigDecimalClass->canReplaceType = _Type_canReplaceType; s_BigDecimalClass->coerceDatum = _BigDecimal_coerceDatum; s_BigDecimalClass->coerceObject = _BigDecimal_coerceObject; Type_registerType2(NUMERICOID, "java.math.BigDecimal", BigDecimal_obtain); } pljava-1.4.3/src/C/pljava/type/Long.c0000644000014500000120000001070411634451404016377 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_longClass; static jclass s_Long_class; static jclass s_LongArray_class; static jmethodID s_Long_init; static jmethodID s_Long_longValue; /* * long primitive type. */ static Datum _asDatum(jlong v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Int64GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _long_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticLongMethodA(cls, method, args)); } static jvalue _long_coerceDatum(Type self, Datum arg) { jvalue result; result.j = DatumGetInt64(arg); return result; } static jvalue _longArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jlongArray longArray = JNI_newLongArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setLongArrayRegion(longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jlong* values = (jlong*)ARR_DATA_PTR(v); jlong* elems = JNI_getLongArrayElements(longArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseLongArrayElements(longArray, elems, JNI_COMMIT); } else JNI_setLongArrayRegion(longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); #endif result.l = (jobject)longArray; return result; } static Datum _longArray_coerceObject(Type self, jobject longArray) { ArrayType* v; jsize nElems; if(longArray == 0) return 0; nElems = JNI_getArrayLength((jarray)longArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jlong), INT8OID); #else v = createArrayType(nElems, sizeof(jlong), INT8OID, false); #endif if(!JNI_isInstanceOf( longArray, s_LongArray_class)) JNI_getLongArrayRegion((jlongArray)longArray, 0, nElems, (jlong*)ARR_DATA_PTR(v)); else { int idx = 0; jlong *array = (jlong*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callLongMethod(JNI_getObjectArrayElement(longArray, idx), s_Long_longValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Long type. */ static bool _Long_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_longClass; } static jvalue _Long_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Long_class, s_Long_init, DatumGetInt64(arg)); return result; } static Datum _Long_coerceObject(Type self, jobject longObj) { return _asDatum(longObj == 0 ? 0 : JNI_callLongMethod(longObj, s_Long_longValue)); } static Type _long_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _longArray_coerceDatum, _longArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Long_initialize(void); void Long_initialize(void) { Type t_long; Type t_Long; TypeClass cls; s_Long_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Long")); s_LongArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Long;")); s_Long_init = PgObject_getJavaMethod(s_Long_class, "", "(J)V"); s_Long_longValue = PgObject_getJavaMethod(s_Long_class, "longValue", "()J"); cls = TypeClass_alloc("type.Long"); cls->canReplaceType = _Long_canReplace; cls->JNISignature = "Ljava/lang/Long;"; cls->javaTypeName = "java.lang.Long"; cls->coerceDatum = _Long_coerceDatum; cls->coerceObject = _Long_coerceObject; t_Long = TypeClass_allocInstance(cls, INT8OID); cls = TypeClass_alloc("type.long"); cls->JNISignature = "J"; cls->javaTypeName = "long"; cls->invoke = _long_invoke; cls->coerceDatum = _long_coerceDatum; cls->coerceObject = _Long_coerceObject; cls->createArrayType = _long_createArrayType; s_longClass = cls; t_long = TypeClass_allocInstance(cls, INT8OID); t_long->objectType = t_Long; Type_registerType("long", t_long); Type_registerType("java.lang.Long", t_Long); } pljava-1.4.3/src/C/pljava/type/Type.c0000644000014500000120000005225111634451404016424 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include #include #include #include "pljava/type/String_priv.h" #include "pljava/type/Array.h" #include "pljava/type/Coerce.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" #include "pljava/type/UDT.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/SPI.h" static HashMap s_typeByOid; static HashMap s_obtainerByOid; static HashMap s_obtainerByJavaName; static jclass s_Map_class; static jmethodID s_Map_get; typedef struct CacheEntryData { Type type; TypeObtainer obtainer; Oid typeId; } CacheEntryData; typedef CacheEntryData* CacheEntry; static jclass s_Iterator_class; static jmethodID s_Iterator_hasNext; static jmethodID s_Iterator_next; /* Structure used in multi function calls (calls returning * SETOF ) */ typedef struct { Type elemType; jobject rowProducer; jobject rowCollector; jobject invocation; MemoryContext rowContext; MemoryContext spiContext; bool hasConnected; bool trusted; } CallContextData; static void _closeIteration(CallContextData* ctxData) { currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; Type_closeSRF(ctxData->elemType, ctxData->rowProducer); JNI_deleteGlobalRef(ctxData->rowProducer); if(ctxData->rowCollector != 0) JNI_deleteGlobalRef(ctxData->rowCollector); MemoryContextDelete(ctxData->rowContext); if(ctxData->hasConnected && ctxData->spiContext != 0) { /* Connect during SRF_IS_FIRSTCALL(). Switch context back to what * it was at that time and disconnect. */ MemoryContext currCtx = MemoryContextSwitchTo(ctxData->spiContext); Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); } } static void _endOfSetCB(Datum arg) { Invocation topCall; bool saveInExprCtxCB; CallContextData* ctxData = (CallContextData*)DatumGetPointer(arg); if(currentInvocation == 0) Invocation_pushInvocation(&topCall, ctxData->trusted); saveInExprCtxCB = currentInvocation->inExprContextCB; currentInvocation->inExprContextCB = true; _closeIteration(ctxData); currentInvocation->inExprContextCB = saveInExprCtxCB; } Type Type_getCoerceIn(Type self, Type other) { Oid funcId; Type coerce; Oid fromOid = other->typeId; Oid toOid = self->typeId; if(self->inCoercions != 0) { coerce = HashMap_getByOid(self->inCoercions, fromOid); if(coerce != 0) return coerce; } if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if(self->inCoercions == 0) self->inCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coerce = Coerce_createIn(self, other, funcId); HashMap_putByOid(self->inCoercions, fromOid, coerce); return coerce; } Type Type_getCoerceOut(Type self, Type other) { Oid funcId; Type coercer; Oid fromOid = self->typeId; Oid toOid = other->typeId; if(self->outCoercions != 0) { coercer = HashMap_getByOid(self->outCoercions, toOid); if(coercer != 0) return coercer; } if(funcId == InvalidOid) /* * Binary compatible type. No need for a special coercer */ return self; if (!find_coercion_pathway(toOid, fromOid, COERCION_EXPLICIT, &funcId)) { elog(ERROR, "no conversion function from %s to %s", format_type_be(fromOid), format_type_be(toOid)); } if(self->outCoercions == 0) self->outCoercions = HashMap_create(7, GetMemoryChunkContext(self)); coercer = Coerce_createOut(self, other, funcId); HashMap_putByOid(self->outCoercions, toOid, coercer); return coercer; } bool Type_canReplaceType(Type self, Type other) { return self->typeClass->canReplaceType(self, other); } bool Type_isDynamic(Type self) { return self->typeClass->dynamic; } bool Type_isOutParameter(Type self) { return self->typeClass->outParameter; } jvalue Type_coerceDatum(Type self, Datum value) { return self->typeClass->coerceDatum(self, value); } Datum Type_coerceObject(Type self, jobject object) { return self->typeClass->coerceObject(self, object); } char Type_getAlign(Type self) { return self->align; } TypeClass Type_getClass(Type self) { return self->typeClass; } int16 Type_getLength(Type self) { return self->length; } bool Type_isByValue(Type self) { return self->byValue; } jclass Type_getJavaClass(Type self) { TypeClass typeClass = self->typeClass; if(typeClass->javaClass == 0) { jclass cls; const char* cp = typeClass->JNISignature; if(cp == 0 || *cp == 0) ereport(ERROR, ( errmsg("Type '%s' has no corresponding java class", PgObjectClass_getName((PgObjectClass)typeClass)))); if(*cp == 'L') { /* L; should be just . Strange * since the L and ; are retained if its an array. */ int len = strlen(cp) - 2; char* bp = palloc(len + 1); memcpy(bp, cp + 1, len); bp[len] = 0; cls = PgObject_getJavaClass(bp); pfree(bp); } else cls = PgObject_getJavaClass(cp); typeClass->javaClass = JNI_newGlobalRef(cls); JNI_deleteLocalRef(cls); } return typeClass->javaClass; } const char* Type_getJavaTypeName(Type self) { return self->typeClass->javaTypeName; } const char* Type_getJNISignature(Type self) { return self->typeClass->getJNISignature(self); } const char* Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return self->typeClass->getJNIReturnSignature(self, forMultiCall, useAltRepr); } Type Type_getArrayType(Type self, Oid arrayTypeId) { Type arrayType = self->arrayType; if(arrayType != 0) { if(arrayType->typeId == arrayTypeId) return arrayType; if(arrayType->typeId == InvalidOid) { arrayType->typeId = arrayTypeId; return arrayType; } } arrayType = self->typeClass->createArrayType(self, arrayTypeId); self->arrayType = arrayType; return arrayType; } Type Type_getElementType(Type self) { return self->elementType; } Type Type_getObjectType(Type self) { return self->objectType; } Type Type_getRealType(Type self, Oid realTypeId, jobject typeMap) { return self->typeClass->getRealType(self, realTypeId, typeMap); } Oid Type_getOid(Type self) { return self->typeId; } TupleDesc Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getTupleDesc(self, fcinfo); } Datum Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return self->typeClass->invoke(self, cls, method, args, fcinfo); } Datum Type_invokeSRF(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; CallContextData* ctxData; FuncCallContext* context; MemoryContext currCtx; /* stuff done only on the first call of the function */ if(SRF_IS_FIRSTCALL()) { jobject tmp; /* create a function context for cross-call persistence */ context = SRF_FIRSTCALL_INIT(); currCtx = MemoryContextSwitchTo(context->multi_call_memory_ctx); /* Call the declared Java function. It returns an instance that can produce * the rows. */ tmp = Type_getSRFProducer(self, cls, method, args); if(tmp == 0) { Invocation_assertDisconnect(); MemoryContextSwitchTo(currCtx); fcinfo->isnull = true; SRF_RETURN_DONE(context); } ctxData = (CallContextData*)palloc(sizeof(CallContextData)); context->user_fctx = ctxData; ctxData->elemType = self; ctxData->rowProducer = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); /* Some row producers will need a writable result set in order * to produce the row. If one is needed, it's created here. */ tmp = Type_getSRFCollector(self, fcinfo); if(tmp == 0) ctxData->rowCollector = 0; else { ctxData->rowCollector = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); } ctxData->trusted = currentInvocation->trusted; ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; if(ctxData->hasConnected) ctxData->spiContext = CurrentMemoryContext; else ctxData->spiContext = 0; ctxData->rowContext = AllocSetContextCreate(context->multi_call_memory_ctx, "PL/Java row context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Register callback to be called when the function ends */ RegisterExprContextCallback(((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); MemoryContextSwitchTo(currCtx); } context = SRF_PERCALL_SETUP(); ctxData = (CallContextData*)context->user_fctx; MemoryContextReset(ctxData->rowContext); currCtx = MemoryContextSwitchTo(ctxData->rowContext); currentInvocation->hasConnected = ctxData->hasConnected; currentInvocation->invocation = ctxData->invocation; hasRow = Type_hasNextSRF(self, ctxData->rowProducer, ctxData->rowCollector, (jint)context->call_cntr); ctxData->hasConnected = currentInvocation->hasConnected; ctxData->invocation = currentInvocation->invocation; currentInvocation->hasConnected = false; currentInvocation->invocation = 0; if(hasRow) { Datum result = Type_nextSRF(self, ctxData->rowProducer, ctxData->rowCollector); MemoryContextSwitchTo(currCtx); SRF_RETURN_NEXT(context, result); } MemoryContextSwitchTo(currCtx); /* Unregister this callback and call it manually. We do this because * otherwise it will be called when the backend is in progress of * cleaning up Portals. If we close cursors (i.e. drop portals) in * the close, then that mechanism fails since attempts are made to * delete portals more then once. */ UnregisterExprContextCallback( ((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData)); _closeIteration(ctxData); /* This is the end of the set. */ SRF_RETURN_DONE(context); } bool Type_isPrimitive(Type self) { return self->objectType != 0; } Type Type_fromJavaType(Oid typeId, const char* javaTypeName) { CacheEntry ce = (CacheEntry)HashMap_getByString(s_obtainerByJavaName, javaTypeName); if(ce == 0) { int jtlen = strlen(javaTypeName) - 2; if(jtlen > 0 && strcmp("[]", javaTypeName + jtlen) == 0) { Type type; char* elemName = palloc(jtlen+1); memcpy(elemName, javaTypeName, jtlen); elemName[jtlen] = 0; type = Type_getArrayType(Type_fromJavaType(InvalidOid, elemName), typeId); pfree(elemName); return type; } ereport(ERROR, ( errcode(ERRCODE_CANNOT_COERCE), errmsg("No java type mapping installed for \"%s\"", javaTypeName))); } return ce->type == 0 ? ce->obtainer(typeId == InvalidOid ? ce->typeId : typeId) : ce->type; } void Type_cacheByOid(Oid typeId, Type type) { HashMap_putByOid(s_typeByOid, typeId, type); } Type Type_fromOidCache(Oid typeId) { return (Type)HashMap_getByOid(s_typeByOid, typeId); } Type Type_fromOid(Oid typeId, jobject typeMap) { CacheEntry ce; HeapTuple typeTup; Form_pg_type typeStruct; Type type = Type_fromOidCache(typeId); if(type != 0) return type; typeTup = PgObject_getValidTuple(TYPEOID, typeId, "type"); typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if(typeStruct->typelem != 0 && typeStruct->typlen == -1) { type = Type_getArrayType(Type_fromOid(typeStruct->typelem, typeMap), typeId); goto finally; } /* For some reason, the anyarray is *not* an array with anyelement as the * element type. We'd like to see it that way though. */ if(typeId == ANYARRAYOID) { type = Type_getArrayType(Type_fromOid(ANYELEMENTOID, typeMap), typeId); goto finally; } if(typeStruct->typbasetype != 0) { /* Domain type, recurse using the base type (which in turn may * also be a domain) */ type = Type_fromOid(typeStruct->typbasetype, typeMap); goto finally; } if(typeMap != 0) { jobject joid = Oid_create(typeId); jclass typeClass = (jclass)JNI_callObjectMethod(typeMap, s_Map_get, joid); JNI_deleteLocalRef(joid); if(typeClass != 0) { TupleDesc tupleDesc = lookup_rowtype_tupdesc_noerror(typeId, -1, true); type = (Type)UDT_registerUDT(typeClass, typeId, typeStruct, tupleDesc, false); JNI_deleteLocalRef(typeClass); goto finally; } } /* Composite and record types will not have a TypeObtainer registered */ if(typeStruct->typtype == 'c' || (typeStruct->typtype == 'p' && typeId == RECORDOID)) { type = Composite_obtain(typeId); goto finally; } ce = (CacheEntry)HashMap_getByOid(s_obtainerByOid, typeId); if(ce == 0) /* * Default to String and standard textin/textout coersion. */ type = String_obtain(typeId); else { type = ce->type; if(type == 0) type = ce->obtainer(typeId); } finally: ReleaseSysCache(typeTup); Type_cacheByOid(typeId, type); return type; } Type Type_objectTypeFromOid(Oid typeId, jobject typeMap) { Type type = Type_fromOid(typeId, typeMap); Type objectType = type->objectType; return (objectType == 0) ? type : objectType; } bool _Type_canReplaceType(Type self, Type other) { return self->typeClass == other->typeClass; } Datum _Type_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { MemoryContext currCtx; Datum ret; jobject value = JNI_callStaticObjectMethodA(cls, method, args); if(value == 0) { fcinfo->isnull = true; return 0; } /* The return value cannot be created in the current context since it * goes out of scope when SPI_finish is called. */ currCtx = Invocation_switchToUpperContext(); ret = self->typeClass->coerceObject(self, value); MemoryContextSwitchTo(currCtx); JNI_deleteLocalRef(value); return ret; } static Type _Type_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid(arrayTypeId, self); } static jobject _Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return JNI_callStaticObjectMethodA(cls, method, args); } static jobject _Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return 0; } static bool _Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return (JNI_callBooleanMethod(rowProducer, s_Iterator_hasNext) == JNI_TRUE); } static Datum _Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { jobject tmp = JNI_callObjectMethod(rowProducer, s_Iterator_next); Datum result = Type_coerceObject(self, tmp); JNI_deleteLocalRef(tmp); return result; } static void _Type_closeSRF(Type self, jobject rowProducer) { } jobject Type_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { return self->typeClass->getSRFProducer(self, cls, method, args); } jobject Type_getSRFCollector(Type self, PG_FUNCTION_ARGS) { return self->typeClass->getSRFCollector(self, fcinfo); } bool Type_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { return self->typeClass->hasNextSRF(self, rowProducer, rowCollector, callCounter); } Datum Type_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { return self->typeClass->nextSRF(self, rowProducer, rowCollector); } void Type_closeSRF(Type self, jobject rowProducer) { self->typeClass->closeSRF(self, rowProducer); } static Type _Type_getRealType(Type self, Oid realId, jobject typeMap) { return self; } static const char* _Type_getJNISignature(Type self) { return self->typeClass->JNISignature; } static const char* _Type_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? "Ljava/util/Iterator;" : Type_getJNISignature(self); } TupleDesc _Type_getTupleDesc(Type self, PG_FUNCTION_ARGS) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Type is not associated with a record"))); return 0; /* Keep compiler happy */ } /* * Shortcuts to initializers of known types */ extern void Any_initialize(void); extern void Coerce_initialize(void); extern void Void_initialize(void); extern void Boolean_initialize(void); extern void Byte_initialize(void); extern void Short_initialize(void); extern void Integer_initialize(void); extern void Long_initialize(void); extern void Float_initialize(void); extern void Double_initialize(void); extern void BigDecimal_initialize(void); extern void Date_initialize(void); extern void Time_initialize(void); extern void Timestamp_initialize(void); extern void Oid_initialize(void); extern void AclId_initialize(void); extern void ErrorData_initialize(void); extern void LargeObject_initialize(void); extern void String_initialize(void); extern void byte_array_initialize(void); extern void JavaWrapper_initialize(void); extern void ExecutionPlan_initialize(void); extern void Portal_initialize(void); extern void Relation_initialize(void); extern void TriggerData_initialize(void); extern void Tuple_initialize(void); extern void TupleDesc_initialize(void); extern void TupleTable_initialize(void); extern void Composite_initialize(void); extern void Type_initialize(void); void Type_initialize(void) { s_typeByOid = HashMap_create(59, TopMemoryContext); s_obtainerByOid = HashMap_create(59, TopMemoryContext); s_obtainerByJavaName = HashMap_create(59, TopMemoryContext); String_initialize(); Any_initialize(); Coerce_initialize(); Void_initialize(); Boolean_initialize(); Byte_initialize(); Short_initialize(); Integer_initialize(); Long_initialize(); Float_initialize(); Double_initialize(); BigDecimal_initialize(); Date_initialize(); Time_initialize(); Timestamp_initialize(); Oid_initialize(); AclId_initialize(); ErrorData_initialize(); LargeObject_initialize(); byte_array_initialize(); JavaWrapper_initialize(); ExecutionPlan_initialize(); Portal_initialize(); TriggerData_initialize(); Relation_initialize(); TupleDesc_initialize(); Tuple_initialize(); TupleTable_initialize(); Composite_initialize(); s_Map_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Map")); s_Map_get = PgObject_getJavaMethod(s_Map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); s_Iterator_class = JNI_newGlobalRef(PgObject_getJavaClass("java/util/Iterator")); s_Iterator_hasNext = PgObject_getJavaMethod(s_Iterator_class, "hasNext", "()Z"); s_Iterator_next = PgObject_getJavaMethod(s_Iterator_class, "next", "()Ljava/lang/Object;"); } /* * Abstract Type constructor */ TypeClass TypeClass_alloc(const char* typeName) { return TypeClass_alloc2(typeName, sizeof(struct TypeClass_), sizeof(struct Type_)); } TypeClass TypeClass_alloc2(const char* typeName, Size classSize, Size instanceSize) { TypeClass self = (TypeClass)MemoryContextAlloc(TopMemoryContext, classSize); PgObjectClass_init((PgObjectClass)self, typeName, instanceSize, 0); self->JNISignature = ""; self->javaTypeName = ""; self->javaClass = 0; self->canReplaceType = _Type_canReplaceType; self->coerceDatum = (DatumCoercer)_PgObject_pureVirtualCalled; self->coerceObject = (ObjectCoercer)_PgObject_pureVirtualCalled; self->createArrayType = _Type_createArrayType; self->invoke = _Type_invoke; self->getSRFProducer = _Type_getSRFProducer; self->getSRFCollector = _Type_getSRFCollector; self->hasNextSRF = _Type_hasNextSRF; self->nextSRF = _Type_nextSRF; self->closeSRF = _Type_closeSRF; self->getTupleDesc = _Type_getTupleDesc; self->getJNISignature = _Type_getJNISignature; self->getJNIReturnSignature = _Type_getJNIReturnSignature; self->dynamic = false; self->outParameter = false; self->getRealType = _Type_getRealType; return self; } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance(TypeClass cls, Oid typeId) { return TypeClass_allocInstance2(cls, typeId, 0); } /* * Types are always allocated in global context. */ Type TypeClass_allocInstance2(TypeClass cls, Oid typeId, Form_pg_type pgType) { Type t = (Type)PgObjectClass_allocInstance((PgObjectClass)(cls), TopMemoryContext); t->typeId = typeId; t->arrayType = 0; t->elementType = 0; t->objectType = 0; t->inCoercions = 0; t->outCoercions = 0; if(pgType != 0) { t->length = pgType->typlen; t->byValue = pgType->typbyval; t->align = pgType->typalign; } else if(typeId != InvalidOid) { get_typlenbyvalalign(typeId, &t->length, &t->byValue, &t->align); } else { t->length = 0; t->byValue = true; t->align = 'i'; } return t; } /* * Register this type. */ static void _registerType(Oid typeId, const char* javaTypeName, Type type, TypeObtainer obtainer) { CacheEntry ce = (CacheEntry)MemoryContextAlloc(TopMemoryContext, sizeof(CacheEntryData)); ce->typeId = typeId; ce->type = type; ce->obtainer = obtainer; if(javaTypeName != 0) HashMap_putByString(s_obtainerByJavaName, javaTypeName, ce); if(typeId != InvalidOid && HashMap_getByOid(s_obtainerByOid, typeId) == 0) HashMap_putByOid(s_obtainerByOid, typeId, ce); } void Type_registerType(const char* javaTypeName, Type type) { _registerType(type->typeId, javaTypeName, type, (TypeObtainer)_PgObject_pureVirtualCalled); } void Type_registerType2(Oid typeId, const char* javaTypeName, TypeObtainer obtainer) { _registerType(typeId, javaTypeName, 0, obtainer); } pljava-1.4.3/src/C/pljava/type/String.c0000644000014500000120000001471211634451404016751 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/String_priv.h" #include "pljava/HashMap.h" static TypeClass s_StringClass; jclass s_String_class; jclass s_Object_class; static jmethodID s_Object_toString; /* * Default type. Uses Posgres String conversion routines. */ static bool _String_canReplaceType(Type self, Type type) { /* All known postgres types can perform String coercsions. */ return true; } jvalue _String_coerceDatum(Type self, Datum arg) { jvalue result; char* tmp = DatumGetCString(FunctionCall3( &((String)self)->textOutput, arg, ObjectIdGetDatum(((String)self)->elementType), Int32GetDatum(-1))); result.l = String_createJavaStringFromNTS(tmp); pfree(tmp); return result; } Datum _String_coerceObject(Type self, jobject jstr) { char* tmp; Datum ret; if(jstr == 0) return 0; jstr = JNI_callObjectMethod(jstr, s_Object_toString); if(JNI_exceptionCheck()) return 0; tmp = String_createNTS(jstr); JNI_deleteLocalRef(jstr); ret = FunctionCall3( &((String)self)->textInput, CStringGetDatum(tmp), ObjectIdGetDatum(((String)self)->elementType), Int32GetDatum(-1)); pfree(tmp); return ret; } static String String_create(TypeClass cls, Oid typeId) { HeapTuple typeTup = PgObject_getValidTuple(TYPEOID, typeId, "type"); Form_pg_type pgType = (Form_pg_type)GETSTRUCT(typeTup); String self = (String)TypeClass_allocInstance(cls, typeId); MemoryContext ctx = GetMemoryChunkContext(self); fmgr_info_cxt(pgType->typoutput, &self->textOutput, ctx); fmgr_info_cxt(pgType->typinput, &self->textInput, ctx); self->elementType = pgType->typelem; ReleaseSysCache(typeTup); return self; } Type String_obtain(Oid typeId) { return (Type)StringClass_obtain(s_StringClass, typeId); } String StringClass_obtain(TypeClass self, Oid typeId) { return String_create(self, typeId); } jstring String_createJavaString(text* t) { jstring result = 0; if(t != 0) { char* utf8; char* src = VARDATA(t); int srcLen = VARSIZE(t) - VARHDRSZ; if(srcLen == 0) return 0; /* Would be nice if a direct conversion to UTF16 was provided. */ utf8 = (char*)pg_do_encoding_conversion((unsigned char*)src, srcLen, GetDatabaseEncoding(), PG_UTF8); result = JNI_newStringUTF(utf8); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(utf8 != src) pfree(utf8); } return result; } jstring String_createJavaStringFromNTS(const char* cp) { jstring result = 0; if(cp != 0) { /* Would be nice if a direct conversion to UTF16 was provided. */ char* utf8 = (char*)pg_do_encoding_conversion((unsigned char*)cp, strlen(cp), GetDatabaseEncoding(), PG_UTF8); result = JNI_newStringUTF(utf8); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(utf8 != cp) pfree(utf8); } return result; } text* String_createText(jstring javaString) { text* result = 0; if(javaString != 0) { /* Would be nice if a direct conversion from UTF16 was provided. */ char* utf8 = (char*)JNI_getStringUTFChars(javaString, 0); char* denc = (char*)pg_do_encoding_conversion( (unsigned char*)utf8, strlen(utf8), PG_UTF8, GetDatabaseEncoding()); int dencLen = strlen(denc); int varSize = dencLen + VARHDRSZ; /* Allocate and initialize the text structure. */ result = (text*)palloc(varSize); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) VARATT_SIZEP(result) = varSize; /* Total size of structure, not just data */ #else SET_VARSIZE(result, varSize); /* Total size of structure, not just data */ #endif memcpy(VARDATA(result), denc, dencLen); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(denc != utf8) pfree(denc); JNI_releaseStringUTFChars(javaString, utf8); } return result; } char* String_createNTS(jstring javaString) { char* result = 0; if(javaString != 0) { /* Would be nice if a direct conversion from UTF16 was provided. */ char* utf8 = (char*)JNI_getStringUTFChars(javaString, 0); result = (char*)pg_do_encoding_conversion( (unsigned char*)utf8, strlen(utf8), PG_UTF8, GetDatabaseEncoding()); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We always want a copy here. */ if(result == utf8) result = pstrdup(result); JNI_releaseStringUTFChars(javaString, utf8); } return result; } void String_appendJavaString(StringInfoData* buf, jstring javaString) { if(javaString != 0) { /* Would be nice if a direct conversion from UTF16 was provided. */ char* utf8 = (char*)JNI_getStringUTFChars(javaString, 0); char* dbEnc = (char*)pg_do_encoding_conversion( (unsigned char*)utf8, strlen(utf8), PG_UTF8, GetDatabaseEncoding()); appendStringInfoString(buf, dbEnc); /* pg_do_encoding_conversion will return the source argument * when no conversion is required. We don't want to accidentally * free that pointer. */ if(dbEnc != utf8) pfree(dbEnc); JNI_releaseStringUTFChars(javaString, utf8); } } extern void String_initialize(void); void String_initialize(void) { s_Object_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Object")); s_Object_toString = PgObject_getJavaMethod(s_Object_class, "toString", "()Ljava/lang/String;"); s_String_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/String")); s_StringClass = TypeClass_alloc2("type.String", sizeof(struct TypeClass_), sizeof(struct String_)); s_StringClass->JNISignature = "Ljava/lang/String;"; s_StringClass->javaTypeName = "java.lang.String"; s_StringClass->canReplaceType = _String_canReplaceType; s_StringClass->coerceDatum = _String_coerceDatum; s_StringClass->coerceObject = _String_coerceObject; /* * Registering known types will increase the performance * a bit. The "default" is used when all else fails. */ Type_registerType2(TEXTOID, 0, String_obtain); Type_registerType2(CSTRINGOID, 0, String_obtain); Type_registerType2(BPCHAROID, 0, String_obtain); Type_registerType2(NAMEOID, 0, String_obtain); Type_registerType2(VARCHAROID, "java.lang.String", String_obtain); } pljava-1.4.3/src/C/pljava/type/Short.c0000644000014500000120000001065211634451404016601 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_shortClass; static jclass s_Short_class; static jclass s_ShortArray_class; static jmethodID s_Short_init; static jmethodID s_Short_shortValue; /* * short primitive type. */ static Datum _short_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jshort v = JNI_callStaticShortMethodA(cls, method, args); return Int16GetDatum(v); } static jvalue _short_coerceDatum(Type self, Datum arg) { jvalue result; result.s = DatumGetInt16(arg); return result; } static jvalue _shortArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jshortArray shortArray = JNI_newShortArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setShortArrayRegion(shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jshort* values = (jshort*)ARR_DATA_PTR(v); jshort* elems = JNI_getShortArrayElements(shortArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseShortArrayElements(shortArray, elems, JNI_COMMIT); } else JNI_setShortArrayRegion(shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); #endif result.l = (jobject)shortArray; return result; } static Datum _shortArray_coerceObject(Type self, jobject shortArray) { ArrayType* v; jsize nElems; if(shortArray == 0) return 0; nElems = JNI_getArrayLength((jarray)shortArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jshort), INT2OID); #else v = createArrayType(nElems, sizeof(jshort), INT2OID, false); #endif if(!JNI_isInstanceOf( shortArray, s_ShortArray_class)) JNI_getIntArrayRegion((jshortArray)shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); else { int idx = 0; jshort *array = (jshort*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callShortMethod(JNI_getObjectArrayElement(shortArray, idx), s_Short_shortValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Short type. */ static bool _Short_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_shortClass; } static jvalue _Short_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Short_class, s_Short_init, DatumGetInt16(arg)); return result; } static Datum _Short_coerceObject(Type self, jobject shortObj) { return Int16GetDatum(shortObj == 0 ? 0 : JNI_callShortMethod(shortObj, s_Short_shortValue)); } static Type _short_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _shortArray_coerceDatum, _shortArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Short_initialize(void); void Short_initialize(void) { Type t_short; Type t_Short; TypeClass cls; s_Short_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Short")); s_ShortArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Short;")); s_Short_init = PgObject_getJavaMethod(s_Short_class, "", "(S)V"); s_Short_shortValue = PgObject_getJavaMethod(s_Short_class, "shortValue", "()S"); cls = TypeClass_alloc("type.Short"); cls->canReplaceType = _Short_canReplace; cls->JNISignature = "Ljava/lang/Short;"; cls->javaTypeName = "java.lang.Short"; cls->coerceDatum = _Short_coerceDatum; cls->coerceObject = _Short_coerceObject; t_Short = TypeClass_allocInstance(cls, INT2OID); cls = TypeClass_alloc("type.short"); cls->JNISignature = "S"; cls->javaTypeName = "short"; cls->invoke = _short_invoke; cls->coerceDatum = _short_coerceDatum; cls->coerceObject = _Short_coerceObject; cls->createArrayType = _short_createArrayType; s_shortClass = cls; t_short = TypeClass_allocInstance(cls, INT2OID); t_short->objectType = t_Short; Type_registerType("short", t_short); Type_registerType("java.lang.Short", t_Short); } pljava-1.4.3/src/C/pljava/type/#Portal.c#0000644000014500000120000002051011634451404017043 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include "org_postgresql_pljava_internal_Portal.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/type/Type_priv.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Portal.h" #include "pljava/type/String.h" static jclass s_Portal_class; static jmethodID s_Portal_init; static jfieldID s_Portal_pointer; typedef void (*PortalCleanupProc)(Portal portal); static HashMap s_portalMap = 0; static PortalCleanupProc s_originalCleanupProc = 0; static void _pljavaPortalCleanup(Portal portal) { /* * Remove this object from the cache and clear its * handle. */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_setLongField(jportal, s_Portal_pointer, 0); JNI_deleteGlobalRef(jportal); } portal->cleanup = s_originalCleanupProc; if(s_originalCleanupProc != 0) { (*s_originalCleanupProc)(portal); } } /* * org.postgresql.pljava.type.Tuple type. */ jobject Portal_create(Portal portal) { jobject jportal = 0; if(portal != 0) { jportal = (jobject)HashMap_getByOpaque(s_portalMap, portal); if(jportal == 0) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = portal; /* We need to know when a portal is dropped so that we * don't attempt to drop it twice. */ if(s_originalCleanupProc == 0) s_originalCleanupProc = portal->cleanup; jportal = JNI_newObject(s_Portal_class, s_Portal_init, p2l.longVal); HashMap_putByOpaque(s_portalMap, portal, JNI_newGlobalRef(jportal)); /* * Fail the day the backend decides to utilize the pointer for multiple * purposes. */ Assert(portal->cleanup == s_originalCleanupProc); portal->cleanup = _pljavaPortalCleanup; } } return jportal; } /* Make this datatype available to the postgres system. */ extern void Portal_initialize(void); void Portal_initialize(void) { JNINativeMethod methods[] = { { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Portal__1getName }, { "_getPortalPos", "(J)I", Java_org_postgresql_pljava_internal_Portal__1getPortalPos }, { "_getTupleDesc", "(J)Lorg/postgresql/pljava/internal/TupleDesc;", Java_org_postgresql_pljava_internal_Portal__1getTupleDesc }, { "_fetch", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1fetch }, { "_close", "(J)V", Java_org_postgresql_pljava_internal_Portal__1close }, { "_isAtEnd", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtEnd }, { "_isAtStart", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtStart }, { "_isPosOverflow", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isPosOverflow }, { "_move", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1move }, { 0, 0, 0 } }; s_Portal_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Portal")); PgObject_registerNatives2(s_Portal_class, methods); s_Portal_init = PgObject_getJavaMethod(s_Portal_class, "", "(J)V"); s_Portal_pointer = PgObject_getJavaField(s_Portal_class, "m_pointer", "J"); s_portalMap = HashMap_create(13, TopMemoryContext); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Portal * Method: _getPortalPos * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1getPortalPos(JNIEnv* env, jclass clazz, jlong _this) { jint result = 0; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jint)((Portal)p2l.ptrVal)->portalPos; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _fetch * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1fetch(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_fetch((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_fetch"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Portal__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((Portal)p2l.ptrVal)->name); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getTupleDesc * Signature: (J)Lorg/postgresql/pljava/internal/TupleDesc; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Portal__1getTupleDesc(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = TupleDesc_create(((Portal)p2l.ptrVal)->tupDesc); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _invalidate * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Portal__1close(JNIEnv* env, jclass clazz, jlong _this) { /* We don't use error checking here since we don't want an exception * caused by another exception when we attempt to close. */ if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; BEGIN_NATIVE_NO_ERRCHECK Portal portal = (Portal)p2l.ptrVal; /* Reset our own cleanup callback if needed. No need to come in * the backway */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_deleteGlobalRef(jportal); } if(portal->cleanup == _pljavaPortalCleanup) portal->cleanup = s_originalCleanupProc; if(!(currentInvocation->errorOccured || currentInvocation->inExprContextCB)) SPI_cursor_close(portal); END_NATIVE } } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtStart * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtStart(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atStart; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtEnd * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtEnd(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atEnd; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isPosOverflow * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isPosOverflow(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->posOverflow; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _move * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1move(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_move((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_move"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } pljava-1.4.3/src/C/pljava/type/TriggerData.c0000644000014500000120000002273711634451404017706 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "org_postgresql_pljava_internal_TriggerData.h" #include "pljava/Invocation.h" #include "pljava/Exception.h" #include "pljava/type/Type_priv.h" #include "pljava/type/JavaWrapper.h" #include "pljava/type/String.h" #include "pljava/type/TriggerData.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Relation.h" static jclass s_TriggerData_class; static jmethodID s_TriggerData_init; static jmethodID s_TriggerData_getTriggerReturnTuple; jobject TriggerData_create(TriggerData* triggerData) { return (triggerData == 0) ? 0 : JNI_newObject( s_TriggerData_class, s_TriggerData_init, Invocation_createLocalWrapper(triggerData)); } HeapTuple TriggerData_getTriggerReturnTuple(jobject jtd, bool* wasNull) { Ptr2Long p2l; HeapTuple ret = 0; p2l.longVal = JNI_callLongMethod(jtd, s_TriggerData_getTriggerReturnTuple); if(p2l.longVal != 0) ret = heap_copytuple((HeapTuple)p2l.ptrVal); else *wasNull = true; return ret; } /* Make this datatype available to the postgres system. */ extern void TriggerData_initialize(void); void TriggerData_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_free", "(J)V", Java_org_postgresql_pljava_internal_TriggerData__1free }, { "_getRelation", "(J)Lorg/postgresql/pljava/internal/Relation;", Java_org_postgresql_pljava_internal_TriggerData__1getRelation }, { "_getTriggerTuple", "(J)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_TriggerData__1getTriggerTuple }, { "_getNewTuple", "(J)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_TriggerData__1getNewTuple }, { "_getArguments", "(J)[Ljava/lang/String;", Java_org_postgresql_pljava_internal_TriggerData__1getArguments }, { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_TriggerData__1getName }, { "_isFiredAfter", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredAfter }, { "_isFiredBefore", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredBefore }, { "_isFiredForEachRow", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredForEachRow }, { "_isFiredForStatement", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredForStatement }, { "_isFiredByDelete", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredByDelete }, { "_isFiredByInsert", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredByInsert }, { "_isFiredByUpdate", "(J)Z", Java_org_postgresql_pljava_internal_TriggerData__1isFiredByUpdate }, { 0, 0, 0 } }; s_TriggerData_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/TriggerData")); PgObject_registerNatives2(s_TriggerData_class, methods); s_TriggerData_init = PgObject_getJavaMethod(s_TriggerData_class, "", "(J)V"); s_TriggerData_getTriggerReturnTuple = PgObject_getJavaMethod(s_TriggerData_class, "getTriggerReturnTuple", "()J"); /* Use interface name for signatures. */ cls = TypeClass_alloc("type.TriggerData"); cls->JNISignature = "Lorg/postgresql/pljava/TriggerData;"; cls->javaTypeName = "org.postgresql.pljava.TriggerData"; Type_registerType("org.postgresql.pljava.TriggerData", TypeClass_allocInstance(cls, InvalidOid)); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_TriggerData * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_TriggerData__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Invocation_freeLocalWrapper(pointer); END_NATIVE } /* * Class: org_postgresql_pljava_TriggerData * Method: _getRelation * Signature: (J)Lorg/postgresql/pljava/internal/Relation; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TriggerData__1getRelation(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = Relation_create(self->tg_relation); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _getTriggerTuple * Signature: (J)Lorg/postgresql/pljava/internal/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TriggerData__1getTriggerTuple(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = Tuple_create(self->tg_trigtuple); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _getNewTuple * Signature: (J)Lorg/postgresql/pljava/internal/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TriggerData__1getNewTuple(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = Tuple_create(self->tg_newtuple); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _getArguments * Signature: (J)[Ljava/lang/String; */ JNIEXPORT jobjectArray JNICALL Java_org_postgresql_pljava_internal_TriggerData__1getArguments(JNIEnv* env, jclass clazz, jlong _this) { jobjectArray result = 0; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) { char** cpp; jint idx; BEGIN_NATIVE Trigger* tg = self->tg_trigger; jint nargs = (jint)tg->tgnargs; result = JNI_newObjectArray(nargs, s_String_class, 0); cpp = tg->tgargs; for(idx = 0; idx < nargs; ++idx) { jstring js = String_createJavaStringFromNTS(cpp[idx]); JNI_setObjectArrayElement(result, idx, js); JNI_deleteLocalRef(js); } END_NATIVE } return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_TriggerData__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = String_createJavaStringFromNTS(self->tg_trigger->tgname); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredAfter * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredAfter(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_AFTER(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredBefore * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredBefore(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_BEFORE(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredForEachRow * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredForEachRow(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_FOR_ROW(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredForStatement * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredForStatement(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_FOR_STATEMENT(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredByDelete * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredByDelete(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_BY_DELETE(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredByInsert * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredByInsert(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_BY_INSERT(self->tg_event); return result; } /* * Class: org_postgresql_pljava_TriggerData * Method: _isFiredByUpdate * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_TriggerData__1isFiredByUpdate(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; TriggerData* self = Invocation_getWrappedPointer(_this); if(self != 0) result = (jboolean)TRIGGER_FIRED_BY_UPDATE(self->tg_event); return result; } pljava-1.4.3/src/C/pljava/type/.#TupleDesc.c.1.320000644000014500000120000001610511634451404020074 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "org_postgresql_pljava_internal_TupleDesc.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/Type_priv.h" #include "pljava/type/String.h" #include "pljava/type/Tuple.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Oid.h" static jclass s_TupleDesc_class; static jmethodID s_TupleDesc_init; /* * org.postgresql.pljava.TupleDesc type. */ jobject TupleDesc_create(TupleDesc td) { jobject jtd = 0; if(td != 0) { MemoryContext curr = MemoryContextSwitchTo(JavaMemoryContext); jtd = TupleDesc_internalCreate(td); MemoryContextSwitchTo(curr); } return jtd; } jobject TupleDesc_internalCreate(TupleDesc td) { jobject jtd; Ptr2Long tdH; td = CreateTupleDescCopyConstr(td); tdH.longVal = 0L; /* ensure that the rest is zeroed out */ tdH.ptrVal = td; jtd = JNI_newObject(s_TupleDesc_class, s_TupleDesc_init, tdH.longVal, (jint)td->natts); return jtd; } Type TupleDesc_getColumnType(TupleDesc tupleDesc, int index) { Type type; Oid typeId = SPI_gettypeid(tupleDesc, index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); type = 0; } else type = Type_objectTypeFromOid(typeId, Invocation_getTypeMap()); return type; } static jvalue _TupleDesc_coerceDatum(Type self, Datum arg) { jvalue result; result.l = TupleDesc_create((TupleDesc)DatumGetPointer(arg)); return result; } /* Make this datatype available to the postgres system. */ extern void TupleDesc_initialize(void); void TupleDesc_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_getColumnName", "(JI)Ljava/lang/String;", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName }, { "_getColumnIndex", "(JLjava/lang/String;)I", Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex }, { "_formTuple", "(J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_TupleDesc__1formTuple }, { "_getOid", "(JI)Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_TupleDesc__1getOid }, { "_free", "(J)V", Java_org_postgresql_pljava_internal_TupleDesc__1free }, { 0, 0, 0 }}; s_TupleDesc_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/TupleDesc")); PgObject_registerNatives2(s_TupleDesc_class, methods); s_TupleDesc_init = PgObject_getJavaMethod(s_TupleDesc_class, "", "(JI)V"); cls = JavaWrapperClass_alloc("type.TupleDesc"); cls->JNISignature = "Lorg/postgresql/pljava/internal/TupleDesc;"; cls->javaTypeName = "org.postgresql.pljava.internal.TupleDesc"; cls->coerceDatum = _TupleDesc_coerceDatum; Type_registerType("org.postgresql.pljava.internal.TupleDesc", TypeClass_allocInstance(cls, InvalidOid)); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnName * Signature: (JI)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnName(JNIEnv* env, jclass cls, jlong _this, jint index) { jstring result = 0; BEGIN_NATIVE PG_TRY(); { char* name; Ptr2Long p2l; p2l.longVal = _this; name = SPI_fname((TupleDesc)p2l.ptrVal, (int)index); if(name == 0) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = String_createJavaStringFromNTS(name); pfree(name); } } PG_CATCH(); { Exception_throw_ERROR("SPI_fname"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getColumnIndex * Signature: (JLjava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getColumnIndex(JNIEnv* env, jclass cls, jlong _this, jstring colName) { jint result = 0; BEGIN_NATIVE char* name = String_createNTS(colName); if(name != 0) { Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { result = SPI_fnumber((TupleDesc)p2l.ptrVal, name); if(result == SPI_ERROR_NOATTRIBUTE) { Exception_throw(ERRCODE_UNDEFINED_COLUMN, "Tuple has no attribute \"%s\"", name); } pfree(name); } PG_CATCH(); { Exception_throw_ERROR("SPI_fnumber"); } PG_END_TRY(); } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _formTuple * Signature: (J[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1formTuple(JNIEnv* env, jclass cls, jlong _this, jobjectArray jvalues) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { jint idx; HeapTuple tuple; MemoryContext curr; TupleDesc self = (TupleDesc)p2l.ptrVal; int count = self->natts; Datum* values = (Datum*)palloc(count * sizeof(Datum)); char* nulls = palloc(count); jobject typeMap = Invocation_getTypeMap(); memset(values, 0, count * sizeof(Datum)); memset(nulls, 'n', count); /* all values null initially */ for(idx = 0; idx < count; ++idx) { jobject value = JNI_getObjectArrayElement(jvalues, idx); if(value != 0) { Type type = Type_fromOid(SPI_gettypeid(self, idx + 1), typeMap); values[idx] = Type_coerceObject(type, value); nulls[idx] = ' '; } } curr = MemoryContextSwitchTo(JavaMemoryContext); tuple = heap_formtuple(self, values, nulls); result = Tuple_internalCreate(tuple, false); MemoryContextSwitchTo(curr); pfree(values); pfree(nulls); } PG_CATCH(); { Exception_throw_ERROR("heap_formtuple"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Ptr2Long p2l; p2l.longVal = pointer; DecrTupleDescRefCount((TupleDesc)p2l.ptrVal); //FreeTupleDesc((TupleDesc)p2l.ptrVal); END_NATIVE } /* * Class: org_postgresql_pljava_internal_TupleDesc * Method: _getOid * Signature: (JI)Lorg/postgresql/pljava/internal/Oid; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_TupleDesc__1getOid(JNIEnv* env, jclass cls, jlong _this, jint index) { jobject result = 0; BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; PG_TRY(); { Oid typeId = SPI_gettypeid((TupleDesc)p2l.ptrVal, (int)index); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", (int)index); } else { result = Oid_create(typeId); } } PG_CATCH(); { Exception_throw_ERROR("SPI_gettypeid"); } PG_END_TRY(); END_NATIVE return result; } pljava-1.4.3/src/C/pljava/type/Double.c0000644000014500000120000001123611634451404016713 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_doubleClass; static jclass s_Double_class; static jclass s_DoubleArray_class; static jmethodID s_Double_init; static jmethodID s_Double_doubleValue; /* * double primitive type. */ static Datum _asDatum(jdouble v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Float8GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _double_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticDoubleMethodA(cls, method, args)); } static jvalue _double_coerceDatum(Type self, Datum arg) { jvalue result; result.d = DatumGetFloat8(arg); return result; } static jvalue _doubleArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jdoubleArray doubleArray = JNI_newDoubleArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jdouble* values = (jdouble*)ARR_DATA_PTR(v); jdouble* elems = JNI_getDoubleArrayElements(doubleArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseDoubleArrayElements(doubleArray, elems, JNI_COMMIT); } else JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #endif result.l = (jobject)doubleArray; return result; } static Datum _doubleArray_coerceObject(Type self, jobject doubleArray) { ArrayType* v; jsize nElems; if(doubleArray == 0) return 0; nElems = JNI_getArrayLength((jarray)doubleArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jdouble), FLOAT8OID); #else v = createArrayType(nElems, sizeof(jdouble), FLOAT8OID, false); #endif if(!JNI_isInstanceOf( doubleArray, s_DoubleArray_class)) JNI_getDoubleArrayRegion((jdoubleArray)doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); else { int idx = 0; jdouble *array = (jdouble*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callDoubleMethod(JNI_getObjectArrayElement(doubleArray, idx), s_Double_doubleValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Double type. */ static bool _Double_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_doubleClass; } static jvalue _Double_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Double_class, s_Double_init, DatumGetFloat8(arg)); return result; } static Datum _Double_coerceObject(Type self, jobject doubleObj) { return _asDatum(doubleObj == 0 ? 0 : JNI_callDoubleMethod(doubleObj, s_Double_doubleValue)); } static Type _double_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _doubleArray_coerceDatum, _doubleArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Double_initialize(void); void Double_initialize(void) { Type t_double; Type t_Double; TypeClass cls; s_Double_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Double")); s_DoubleArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Double;")); s_Double_init = PgObject_getJavaMethod(s_Double_class, "", "(D)V"); s_Double_doubleValue = PgObject_getJavaMethod(s_Double_class, "doubleValue", "()D"); cls = TypeClass_alloc("type.Double"); cls->canReplaceType = _Double_canReplace; cls->JNISignature = "Ljava/lang/Double;"; cls->javaTypeName = "java.lang.Double"; cls->coerceDatum = _Double_coerceDatum; cls->coerceObject = _Double_coerceObject; t_Double = TypeClass_allocInstance(cls, FLOAT8OID); cls = TypeClass_alloc("type.double"); cls->JNISignature = "D"; cls->javaTypeName = "double"; cls->invoke = _double_invoke; cls->coerceDatum = _double_coerceDatum; cls->coerceObject = _Double_coerceObject; cls->createArrayType = _double_createArrayType; s_doubleClass = cls; t_double = TypeClass_allocInstance(cls, FLOAT8OID); t_double->objectType = t_Double; Type_registerType("double", t_double); Type_registerType("java.lang.Double", t_Double); } pljava-1.4.3/src/C/pljava/type/Integer.c0000644000014500000120000001057511634451404017103 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_intClass; static jclass s_Integer_class; static jclass s_IntegerArray_class; static jmethodID s_Integer_init; static jmethodID s_Integer_intValue; /* * int primitive type. */ static Datum _int_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jint iv = JNI_callStaticIntMethodA(cls, method, args); return Int32GetDatum(iv); } static jvalue _int_coerceDatum(Type self, Datum arg) { jvalue result; result.i = DatumGetInt32(arg); return result; } static jvalue _intArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jintArray intArray = JNI_newIntArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setIntArrayRegion(intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jint* values = (jint*)ARR_DATA_PTR(v); jint* elems = JNI_getIntArrayElements(intArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseIntArrayElements(intArray, elems, JNI_COMMIT); } else JNI_setIntArrayRegion(intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); #endif result.l = (jobject)intArray; return result; } static Datum _intArray_coerceObject(Type self, jobject intArray) { ArrayType* v; jsize nElems; if(intArray == 0) return 0; nElems = JNI_getArrayLength((jarray)intArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jint), INT4OID); #else v = createArrayType(nElems, sizeof(jint), INT4OID, false); #endif if(!JNI_isInstanceOf( intArray, s_IntegerArray_class)) JNI_getIntArrayRegion((jintArray)intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); else { int idx = 0; jint *array = (jint*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callIntMethod(JNI_getObjectArrayElement(intArray, idx), s_Integer_intValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Integer type. */ static bool _Integer_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_intClass; } static jvalue _Integer_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Integer_class, s_Integer_init, DatumGetInt32(arg)); return result; } static Datum _Integer_coerceObject(Type self, jobject intObj) { return Int32GetDatum(intObj == 0 ? 0 : JNI_callIntMethod(intObj, s_Integer_intValue)); } static Type _int_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _intArray_coerceDatum, _intArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Integer_initialize(void); void Integer_initialize(void) { Type t_int; Type t_Integer; TypeClass cls; s_Integer_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Integer")); s_IntegerArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Integer;")); s_Integer_init = PgObject_getJavaMethod(s_Integer_class, "", "(I)V"); s_Integer_intValue = PgObject_getJavaMethod(s_Integer_class, "intValue", "()I"); cls = TypeClass_alloc("type.Integer"); cls->canReplaceType = _Integer_canReplace; cls->JNISignature = "Ljava/lang/Integer;"; cls->javaTypeName = "java.lang.Integer"; cls->coerceDatum = _Integer_coerceDatum; cls->coerceObject = _Integer_coerceObject; t_Integer = TypeClass_allocInstance(cls, INT4OID); cls = TypeClass_alloc("type.int"); cls->JNISignature = "I"; cls->javaTypeName = "int"; cls->invoke = _int_invoke; cls->coerceDatum = _int_coerceDatum; cls->coerceObject = _Integer_coerceObject; cls->createArrayType = _int_createArrayType; s_intClass = cls; t_int = TypeClass_allocInstance(cls, INT4OID); t_int->objectType = t_Integer; Type_registerType("int", t_int); Type_registerType("java.lang.Integer", t_Integer); } pljava-1.4.3/src/C/pljava/type/Timestamp.c~0000644000014500000120000001633111634451404017643 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/Backend.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Timestamp.h" #define EPOCH_DIFF (((uint32)86400) * (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE)) /* * Timestamp type. Postgres will pass (and expect in return) a local timestamp. * Java on the other hand has no object that represents local time (localization * is added when the object is converted to/from readable form). Hence, all * postgres timestamps must be converted from local time to UTC when passed as * a parameter to a Java method and all Java Timestamps must be converted from UTC * to localtime when returned to postgres. */ static jclass s_Timestamp_class; static jmethodID s_Timestamp_init; static jmethodID s_Timestamp_getNanos; static jmethodID s_Timestamp_getTime; static jmethodID s_Timestamp_setNanos; static TypeClass s_TimestampClass; static TypeClass s_TimestamptzClass; static bool _Timestamp_canReplaceType(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_TimestamptzClass; } static jvalue Timestamp_coerceDatumTZ_id(Type self, Datum arg, bool tzAdjust) { jvalue result; int64 ts = DatumGetInt64(arg); int tz = Timestamp_getTimeZone_id(ts); /* Expect number of microseconds since 01 Jan 2000 */ jlong mSecs = ts / 1000; /* Convert to millisecs */ jint uSecs = (jint)(ts % 1000); /* preserve remaining microsecs */ if(tzAdjust) mSecs += tz * 1000; /* Adjust from local time to UTC */ mSecs += ((jlong)EPOCH_DIFF) * 1000L; /* Adjust for diff between Postgres and Java (Unix) */ result.l = JNI_newObject(s_Timestamp_class, s_Timestamp_init, mSecs); if(uSecs != 0) JNI_callVoidMethod(result.l, s_Timestamp_setNanos, uSecs * 1000); return result; } static jvalue Timestamp_coerceDatumTZ_dd(Type self, Datum arg, bool tzAdjust) { jlong mSecs; jint uSecs; jvalue result; double tmp; double ts = DatumGetFloat8(arg); int tz = Timestamp_getTimeZone_dd(ts); /* Expect . */ if(tzAdjust) ts += tz; /* Adjust from local time to UTC */ ts += EPOCH_DIFF; /* Adjust for diff between Postgres and Java (Unix) */ ts *= 1000.0; /* Convert to millisecs */ tmp = floor(ts); mSecs = (jlong)tmp; uSecs = ((ts - tmp) * 1000.0); /* Preserve remaining microsecs */ result.l = JNI_newObject(s_Timestamp_class, s_Timestamp_init, mSecs); if(uSecs != 0) JNI_callVoidMethod(result.l, s_Timestamp_setNanos, uSecs * 1000); return result; } static jvalue Timestamp_coerceDatumTZ(Type self, Datum arg, bool tzAdjust) { return integerDateTimes ? Timestamp_coerceDatumTZ_id(self, arg, tzAdjust) : Timestamp_coerceDatumTZ_dd(self, arg, tzAdjust); } static Datum Timestamp_coerceObjectTZ_id(Type self, jobject jts, bool tzAdjust) { int64 ts; jlong mSecs = JNI_callLongMethod(jts, s_Timestamp_getTime); jint nSecs = JNI_callIntMethod(jts, s_Timestamp_getNanos); mSecs -= ((jlong)EPOCH_DIFF) * 1000L; ts = mSecs * 1000L; /* Convert millisecs to microsecs */ if(nSecs != 0) ts += nSecs / 1000; /* Convert nanosecs to microsecs */ if(tzAdjust) ts -= ((jlong)Timestamp_getTimeZone_id(ts)) * 1000000L; /* Adjust from UTC to local time */ return Int64GetDatum(ts); } static Datum Timestamp_coerceObjectTZ_dd(Type self, jobject jts, bool tzAdjust) { double ts; jlong mSecs = JNI_callLongMethod(jts, s_Timestamp_getTime); jint nSecs = JNI_callIntMethod(jts, s_Timestamp_getNanos); ts = ((double)mSecs) / 1000.0; /* Convert to seconds */ ts -= EPOCH_DIFF; if(nSecs != 0) ts += ((double)nSecs) / 1000000000.0; /* Convert to seconds */ if(tzAdjust) ts -= Timestamp_getTimeZone_dd(ts); /* Adjust from UTC to local time */ return Float8GetDatum(ts); } static Datum Timestamp_coerceObjectTZ(Type self, jobject jts, bool tzAdjust) { return integerDateTimes ? Timestamp_coerceObjectTZ_id(self, jts, tzAdjust) : Timestamp_coerceObjectTZ_dd(self, jts, tzAdjust); } static jvalue _Timestamp_coerceDatum(Type self, Datum arg) { return Timestamp_coerceDatumTZ(self, arg, true); } static Datum _Timestamp_coerceObject(Type self, jobject ts) { return Timestamp_coerceObjectTZ(self, ts, true); } /* * Timestamp with time zone. Basically same as Timestamp but postgres will pass * this one in GMT timezone so there's no without ajustment for time zone. */ static bool _Timestamptz_canReplaceType(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_TimestampClass; } static jvalue _Timestamptz_coerceDatum(Type self, Datum arg) { return Timestamp_coerceDatumTZ(self, arg, false); } static Datum _Timestamptz_coerceObject(Type self, jobject ts) { return Timestamp_coerceObjectTZ(self, ts, false); } #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3)) extern PGDLLIMPORT pg_tz* session_timezone; #else extern DLLIMPORT pg_tz* global_timezone; #endif #endif static int Timestamp_getTimeZone(pg_time_t time) { #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) struct pg_tm* tx = pg_localtime(&time); #elif (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) struct pg_tm* tx = pg_localtime(&time, global_timezone); #else struct pg_tm* tx = pg_localtime(&time, session_timezone); #endif return -tx->tm_gmtoff; } int32 Timestamp_getTimeZone_id(int64 dt) { return Timestamp_getTimeZone( (dt / INT64CONST(1000000) + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400)); } int32 Timestamp_getTimeZone_dd(double dt) { return Timestamp_getTimeZone( (pg_time_t)rint(dt + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400)); } int32 Timestamp_getCurrentTimeZone(void) { return Timestamp_getTimeZone((pg_time_t)GetCurrentAbsoluteTime()); } extern void Timestamp_initialize(void); void Timestamp_initialize(void) { TypeClass cls; s_Timestamp_class = JNI_newGlobalRef(PgObject_getJavaClass("java/sql/Timestamp")); s_Timestamp_init = PgObject_getJavaMethod(s_Timestamp_class, "", "(J)V"); s_Timestamp_getNanos = PgObject_getJavaMethod(s_Timestamp_class, "getNanos", "()I"); s_Timestamp_getTime = PgObject_getJavaMethod(s_Timestamp_class, "getTime", "()J"); s_Timestamp_setNanos = PgObject_getJavaMethod(s_Timestamp_class, "setNanos", "(I)V"); cls = TypeClass_alloc("type.Timestamp"); cls->JNISignature = "Ljava/sql/Timestamp;"; cls->javaTypeName = "java.sql.Timestamp"; cls->canReplaceType = _Timestamp_canReplaceType; cls->coerceDatum = _Timestamp_coerceDatum; cls->coerceObject = _Timestamp_coerceObject; Type_registerType(0, TypeClass_allocInstance(cls, TIMESTAMPOID)); s_TimestampClass = cls; cls = TypeClass_alloc("type.Timestamptz"); cls->JNISignature = "Ljava/sql/Timestamp;"; cls->javaTypeName = "java.sql.Timestamp"; cls->canReplaceType = _Timestamptz_canReplaceType; cls->coerceDatum = _Timestamptz_coerceDatum; cls->coerceObject = _Timestamptz_coerceObject; Type_registerType("java.sql.Timestamp", TypeClass_allocInstance(cls, TIMESTAMPTZOID)); s_TimestamptzClass = cls; } pljava-1.4.3/src/C/pljava/type/Portal.c0000644000014500000120000002036411634451404016744 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include "org_postgresql_pljava_internal_Portal.h" #include "pljava/Backend.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/HashMap.h" #include "pljava/type/Type_priv.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Portal.h" #include "pljava/type/String.h" static jclass s_Portal_class; static jmethodID s_Portal_init; static jfieldID s_Portal_pointer; typedef void (*PortalCleanupProc)(Portal portal); static HashMap s_portalMap = 0; static PortalCleanupProc s_originalCleanupProc = 0; static void _pljavaPortalCleanup(Portal portal) { /* * Remove this object from the cache and clear its * handle. */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_setLongField(jportal, s_Portal_pointer, 0); JNI_deleteGlobalRef(jportal); } portal->cleanup = s_originalCleanupProc; if(s_originalCleanupProc != 0) { (*s_originalCleanupProc)(portal); } } /* * org.postgresql.pljava.type.Tuple type. */ jobject Portal_create(Portal portal) { jobject jportal = 0; if(portal != 0) { jportal = (jobject)HashMap_getByOpaque(s_portalMap, portal); if(jportal == 0) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = portal; /* We need to know when a portal is dropped so that we * don't attempt to drop it twice. */ if(s_originalCleanupProc == 0) s_originalCleanupProc = portal->cleanup; jportal = JNI_newObject(s_Portal_class, s_Portal_init, p2l.longVal); HashMap_putByOpaque(s_portalMap, portal, JNI_newGlobalRef(jportal)); /* * Fail the day the backend decides to utilize the pointer for multiple * purposes. */ Assert(portal->cleanup == s_originalCleanupProc); portal->cleanup = _pljavaPortalCleanup; } } return jportal; } /* Make this datatype available to the postgres system. */ extern void Portal_initialize(void); void Portal_initialize(void) { JNINativeMethod methods[] = { { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Portal__1getName }, { "_getPortalPos", "(J)I", Java_org_postgresql_pljava_internal_Portal__1getPortalPos }, { "_getTupleDesc", "(J)Lorg/postgresql/pljava/internal/TupleDesc;", Java_org_postgresql_pljava_internal_Portal__1getTupleDesc }, { "_fetch", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1fetch }, { "_close", "(J)V", Java_org_postgresql_pljava_internal_Portal__1close }, { "_isAtEnd", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtEnd }, { "_isAtStart", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isAtStart }, { "_isPosOverflow", "(J)Z", Java_org_postgresql_pljava_internal_Portal__1isPosOverflow }, { "_move", "(JJZI)I", Java_org_postgresql_pljava_internal_Portal__1move }, { 0, 0, 0 } }; s_Portal_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Portal")); PgObject_registerNatives2(s_Portal_class, methods); s_Portal_init = PgObject_getJavaMethod(s_Portal_class, "", "(J)V"); s_Portal_pointer = PgObject_getJavaField(s_Portal_class, "m_pointer", "J"); s_portalMap = HashMap_create(13, TopMemoryContext); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Portal * Method: _getPortalPos * Signature: (J)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1getPortalPos(JNIEnv* env, jclass clazz, jlong _this) { jint result = 0; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jint)((Portal)p2l.ptrVal)->portalPos; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _fetch * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1fetch(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_fetch((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_fetch"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Portal__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = String_createJavaStringFromNTS(((Portal)p2l.ptrVal)->name); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _getTupleDesc * Signature: (J)Lorg/postgresql/pljava/internal/TupleDesc; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Portal__1getTupleDesc(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; result = TupleDesc_create(((Portal)p2l.ptrVal)->tupDesc); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _invalidate * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Portal__1close(JNIEnv* env, jclass clazz, jlong _this) { /* We don't use error checking here since we don't want an exception * caused by another exception when we attempt to close. */ if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; BEGIN_NATIVE_NO_ERRCHECK Portal portal = (Portal)p2l.ptrVal; /* Reset our own cleanup callback if needed. No need to come in * the backway */ jobject jportal = (jobject)HashMap_removeByOpaque(s_portalMap, portal); if(jportal) { JNI_deleteGlobalRef(jportal); } if(portal->cleanup == _pljavaPortalCleanup) portal->cleanup = s_originalCleanupProc; if(!(currentInvocation->errorOccured || currentInvocation->inExprContextCB)) SPI_cursor_close(portal); END_NATIVE } } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtStart * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtStart(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atStart; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isAtEnd * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isAtEnd(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->atEnd; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _isPosOverflow * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Portal__1isPosOverflow(JNIEnv* env, jclass clazz, jlong _this) { jboolean result = JNI_FALSE; if(_this != 0) { Ptr2Long p2l; p2l.longVal = _this; result = (jboolean)((Portal)p2l.ptrVal)->posOverflow; } return result; } /* * Class: org_postgresql_pljava_internal_Portal * Method: _move * Signature: (JJZI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Portal__1move(JNIEnv* env, jclass clazz, jlong _this, jlong threadId, jboolean forward, jint count) { jint result = 0; if(_this != 0) { BEGIN_NATIVE Ptr2Long p2l; STACK_BASE_VARS STACK_BASE_PUSH(threadId) p2l.longVal = _this; PG_TRY(); { SPI_cursor_move((Portal)p2l.ptrVal, forward == JNI_TRUE, (int)count); result = (jint)SPI_processed; } PG_CATCH(); { Exception_throw_ERROR("SPI_cursor_move"); } PG_END_TRY(); STACK_BASE_POP() END_NATIVE } return result; } pljava-1.4.3/src/C/pljava/type/LargeObject.c0000644000014500000120000002216411634451404017664 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include "org_postgresql_pljava_internal_LargeObject.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Oid.h" #include "pljava/type/LargeObject.h" static jclass s_LargeObject_class; static jmethodID s_LargeObject_init; /* * org.postgresql.pljava.type.LargeObject type. */ jobject LargeObject_create(LargeObjectDesc* lo) { jobject jlo; Ptr2Long loH; if(lo == 0) return 0; loH.longVal = 0L; /* ensure that the rest is zeroed out */ loH.ptrVal = lo; jlo = JNI_newObject(s_LargeObject_class, s_LargeObject_init, loH.longVal); return jlo; } extern void LargeObject_initialize(void); void LargeObject_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_create", "(I)Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_LargeObject__1create }, { "_drop", "(Lorg/postgresql/pljava/internal/Oid;)I", Java_org_postgresql_pljava_internal_LargeObject__1drop }, { "_open", "(Lorg/postgresql/pljava/internal/Oid;I)Lorg/postgresql/pljava/internal/LargeObject;", Java_org_postgresql_pljava_internal_LargeObject__1open }, { "_close", "(J)V", Java_org_postgresql_pljava_internal_LargeObject__1close }, { "_getId", "(J)Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_LargeObject__1getId }, { "_length", "(J)J", Java_org_postgresql_pljava_internal_LargeObject__1length }, { "_seek", "(JJI)J", Java_org_postgresql_pljava_internal_LargeObject__1seek }, { "_tell", "(J)J", Java_org_postgresql_pljava_internal_LargeObject__1tell }, { "_read", "(J[B)I", Java_org_postgresql_pljava_internal_LargeObject__1read }, { "_write", "(J[B)I", Java_org_postgresql_pljava_internal_LargeObject__1write }, { 0, 0, 0 } }; s_LargeObject_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/LargeObject")); PgObject_registerNatives2(s_LargeObject_class, methods); s_LargeObject_init = PgObject_getJavaMethod(s_LargeObject_class, "", "(J)V"); cls = TypeClass_alloc("type.LargeObject"); cls->JNISignature = "Lorg/postgresql/pljava/internal/LargeObject;"; cls->javaTypeName = "org.postgresql.pljava.internal.LargeObject"; Type_registerType("org.postgresql.pljava.internal.LargeObject", TypeClass_allocInstance(cls, InvalidOid)); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _create * Signature: (I)Lorg/postgresql/pljava/internal/LargeObject; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_LargeObject__1create(JNIEnv* env, jclass cls, jint flags) { jobject result = 0; BEGIN_NATIVE PG_TRY(); { #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) LargeObjectDesc* lo = inv_create((int)flags); result = Oid_create(lo->id); pfree(lo); #else result = Oid_create(inv_create((int)flags)); #endif } PG_CATCH(); { Exception_throw_ERROR("inv_create"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _drop * Signature: (Lorg/postgresql/pljava/internal/Oid;)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_LargeObject__1drop(JNIEnv* env, jclass cls, jobject oid) { jint result = -1; BEGIN_NATIVE PG_TRY(); { result = inv_drop(Oid_getOid(oid)); } PG_CATCH(); { Exception_throw_ERROR("inv_drop"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _open * Signature: (Lorg/postgresql/pljava/internal/Oid;I)Lorg/postgresql/pljava/internal/LargeObject; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_LargeObject__1open(JNIEnv* env, jclass cls, jobject oid, jint flags) { jobject result = 0; #if (PGSQL_MAJOR_VER == 8 && (PGSQL_MINOR_VER < 1 || (PGSQL_MINOR_VER == 1 && PGSQL_PATCH_VER < 4))) MemoryContext currCtx = MemoryContextSwitchTo(JavaMemoryContext); #endif BEGIN_NATIVE PG_TRY(); { #if (PGSQL_MAJOR_VER == 8 && (PGSQL_MINOR_VER < 1 || (PGSQL_MINOR_VER == 1 && PGSQL_PATCH_VER < 4))) result = LargeObject_create(inv_open(Oid_getOid(oid), (int)flags)); #else result = LargeObject_create(inv_open(Oid_getOid(oid), (int)flags, JavaMemoryContext)); #endif } PG_CATCH(); { Exception_throw_ERROR("inv_open"); } PG_END_TRY(); END_NATIVE #if (PGSQL_MAJOR_VER == 8 && (PGSQL_MINOR_VER < 1 || (PGSQL_MINOR_VER == 1 && PGSQL_PATCH_VER < 4))) MemoryContextSwitchTo(currCtx); #endif return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _close * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_LargeObject__1close(JNIEnv* env, jclass cls, jlong _this) { LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { inv_close(self); } PG_CATCH(); { Exception_throw_ERROR("inv_close"); } PG_END_TRY(); END_NATIVE } } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _getId * Signature: (J)Lorg/postgresql/pljava/internal/Oid; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_LargeObject__1getId(JNIEnv* env, jclass cls, jlong _this) { jobject result = 0; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = Oid_create(self->id); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _length * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_postgresql_pljava_internal_LargeObject__1length(JNIEnv* env, jclass cls, jlong _this) { jlong result = 0; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { /* There's no inv_length call so we use inv_seek on * a temporary LargeObjectDesc. */ LargeObjectDesc lod; memcpy(&lod, self, sizeof(LargeObjectDesc)); result = (jlong)inv_seek(&lod, 0, SEEK_END); } PG_CATCH(); { Exception_throw_ERROR("inv_seek"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _seek * Signature: (JJI)J */ JNIEXPORT jlong JNICALL Java_org_postgresql_pljava_internal_LargeObject__1seek(JNIEnv* env, jclass cls, jlong _this, jlong pos, jint whence) { jlong result = 0; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { result = (jlong)inv_seek(self, (int)pos, (int)whence); } PG_CATCH(); { Exception_throw_ERROR("inv_seek"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _tell * Signature: (J)J */ JNIEXPORT jlong JNICALL Java_org_postgresql_pljava_internal_LargeObject__1tell(JNIEnv* env, jclass cls, jlong _this) { jlong result = 0; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { result = (jlong)inv_tell(self); } PG_CATCH(); { Exception_throw_ERROR("inv_tell"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _read * Signature: (J[B)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_LargeObject__1read(JNIEnv* env, jclass cls, jlong _this, jbyteArray buf) { jint result = -1; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0 && buf != 0) { BEGIN_NATIVE jint nBytes = JNI_getArrayLength(buf); if(nBytes != 0) { jbyte* byteBuf = JNI_getByteArrayElements(buf, 0); if(byteBuf != 0) { PG_TRY(); { result = (jint)inv_read(self, (char*)byteBuf, (int)nBytes); JNI_releaseByteArrayElements(buf, byteBuf, 0); } PG_CATCH(); { JNI_releaseByteArrayElements(buf, byteBuf, JNI_ABORT); Exception_throw_ERROR("inv_read"); } PG_END_TRY(); } } END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_LargeObject * Method: _write * Signature: (J[B)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_LargeObject__1write(JNIEnv* env, jclass cls, jlong _this, jbyteArray buf) { jint result = -1; LargeObjectDesc* self = Invocation_getWrappedPointer(_this); if(self != 0 && buf != 0) { BEGIN_NATIVE jint nBytes = JNI_getArrayLength(buf); if(nBytes != 0) { jbyte* byteBuf = JNI_getByteArrayElements(buf, 0); if(byteBuf != 0) { PG_TRY(); { result = (jint)inv_write(self, (char*)byteBuf, nBytes); /* No need to copy bytes back, hence the JNI_ABORT */ JNI_releaseByteArrayElements(buf, byteBuf, JNI_ABORT); } PG_CATCH(); { JNI_releaseByteArrayElements(buf, byteBuf, JNI_ABORT); Exception_throw_ERROR("inv_write"); } PG_END_TRY(); } } END_NATIVE } return result; } pljava-1.4.3/src/C/pljava/type/Any.c0000644000014500000120000000302011634451404016220 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" static Type _Any_getRealType(Type self, Oid realId, jobject typeMap) { Type real = Type_fromOid(realId, typeMap); if(Type_isPrimitive(real) && Type_getElementType(real) == 0) real = Type_getObjectType(real); return real; } static Type _AnyArray_getRealType(Type self, Oid realId, jobject typeMap) { Type real = Type_fromOid(realId, typeMap); if(Type_isPrimitive(real)) real = Type_getObjectType(real); return real; } static Type _Any_createArrayType(Type self, Oid arrayTypeId) { Type t = Array_fromOid(arrayTypeId, self); t->typeClass->getRealType = _AnyArray_getRealType; t->typeClass->dynamic = true; return t; } /* Make this datatype available to the postgres system. */ extern void Any_initialize(void); void Any_initialize(void) { TypeClass cls = TypeClass_alloc("type.any"); cls->JNISignature = "Ljava/lang/Object;"; cls->javaTypeName = "java.lang.Object"; cls->dynamic = true; cls->getRealType = _Any_getRealType; cls->createArrayType = _Any_createArrayType; Type_registerType("java.lang.Object", TypeClass_allocInstance(cls, ANYELEMENTOID)); Type_registerType(0, TypeClass_allocInstance(cls, ANYOID)); } pljava-1.4.3/src/C/pljava/type/Timestamp.c0000644000014500000120000001625111634451404017446 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/Backend.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Timestamp.h" #define EPOCH_DIFF (((uint32)86400) * (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE)) /* * Timestamp type. Postgres will pass (and expect in return) a local timestamp. * Java on the other hand has no object that represents local time (localization * is added when the object is converted to/from readable form). Hence, all * postgres timestamps must be converted from local time to UTC when passed as * a parameter to a Java method and all Java Timestamps must be converted from UTC * to localtime when returned to postgres. */ static jclass s_Timestamp_class; static jmethodID s_Timestamp_init; static jmethodID s_Timestamp_getNanos; static jmethodID s_Timestamp_getTime; static jmethodID s_Timestamp_setNanos; static TypeClass s_TimestampClass; static TypeClass s_TimestamptzClass; static bool _Timestamp_canReplaceType(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_TimestamptzClass; } static jvalue Timestamp_coerceDatumTZ_id(Type self, Datum arg, bool tzAdjust) { jvalue result; int64 ts = DatumGetInt64(arg); int tz = Timestamp_getTimeZone_id(ts); /* Expect number of microseconds since 01 Jan 2000 */ jlong mSecs = ts / 1000; /* Convert to millisecs */ jint uSecs = (jint)(ts % 1000000); /* preserve microsecs */ if(tzAdjust) mSecs += tz * 1000; /* Adjust from local time to UTC */ /* Adjust for diff between Postgres and Java (Unix) */ mSecs += ((jlong)EPOCH_DIFF) * 1000L; result.l = JNI_newObject(s_Timestamp_class, s_Timestamp_init, mSecs); if(uSecs != 0) JNI_callVoidMethod(result.l, s_Timestamp_setNanos, uSecs * 1000); return result; } static jvalue Timestamp_coerceDatumTZ_dd(Type self, Datum arg, bool tzAdjust) { jlong mSecs; jint uSecs; jvalue result; double ts = DatumGetFloat8(arg); int tz = Timestamp_getTimeZone_dd(ts); /* Expect . */ if(tzAdjust) ts += tz; /* Adjust from local time to UTC */ ts += EPOCH_DIFF; /* Adjust for diff between Postgres and Java (Unix) */ mSecs = (jlong) floor(ts * 1000.0); /* Convert to millisecs */ uSecs = (jint) ((ts - floor(ts)) * 1000000.0); /* Preserve microsecs */ result.l = JNI_newObject(s_Timestamp_class, s_Timestamp_init, mSecs); if(uSecs != 0) JNI_callVoidMethod(result.l, s_Timestamp_setNanos, uSecs * 1000); return result; } static jvalue Timestamp_coerceDatumTZ(Type self, Datum arg, bool tzAdjust) { return integerDateTimes ? Timestamp_coerceDatumTZ_id(self, arg, tzAdjust) : Timestamp_coerceDatumTZ_dd(self, arg, tzAdjust); } static Datum Timestamp_coerceObjectTZ_id(Type self, jobject jts, bool tzAdjust) { int64 ts; jlong mSecs = JNI_callLongMethod(jts, s_Timestamp_getTime); jint nSecs = JNI_callIntMethod(jts, s_Timestamp_getNanos); mSecs -= ((jlong)EPOCH_DIFF) * 1000L; ts = mSecs * 1000L; /* Convert millisecs to microsecs */ if(nSecs != 0) ts += nSecs / 1000; /* Convert nanosecs to microsecs */ if(tzAdjust) ts -= ((jlong)Timestamp_getTimeZone_id(ts)) * 1000000L; /* Adjust from UTC to local time */ return Int64GetDatum(ts); } static Datum Timestamp_coerceObjectTZ_dd(Type self, jobject jts, bool tzAdjust) { double ts; jlong mSecs = JNI_callLongMethod(jts, s_Timestamp_getTime); jint nSecs = JNI_callIntMethod(jts, s_Timestamp_getNanos); ts = ((double)mSecs) / 1000.0; /* Convert to seconds */ ts -= EPOCH_DIFF; if(nSecs != 0) ts += ((double)nSecs) / 1000000000.0; /* Convert to seconds */ if(tzAdjust) ts -= Timestamp_getTimeZone_dd(ts); /* Adjust from UTC to local time */ return Float8GetDatum(ts); } static Datum Timestamp_coerceObjectTZ(Type self, jobject jts, bool tzAdjust) { return integerDateTimes ? Timestamp_coerceObjectTZ_id(self, jts, tzAdjust) : Timestamp_coerceObjectTZ_dd(self, jts, tzAdjust); } static jvalue _Timestamp_coerceDatum(Type self, Datum arg) { return Timestamp_coerceDatumTZ(self, arg, true); } static Datum _Timestamp_coerceObject(Type self, jobject ts) { return Timestamp_coerceObjectTZ(self, ts, true); } /* * Timestamp with time zone. Basically same as Timestamp but postgres will pass * this one in GMT timezone so there's no without ajustment for time zone. */ static bool _Timestamptz_canReplaceType(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_TimestampClass; } static jvalue _Timestamptz_coerceDatum(Type self, Datum arg) { return Timestamp_coerceDatumTZ(self, arg, false); } static Datum _Timestamptz_coerceObject(Type self, jobject ts) { return Timestamp_coerceObjectTZ(self, ts, false); } #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3)) extern PGDLLIMPORT pg_tz* session_timezone; #else extern DLLIMPORT pg_tz* global_timezone; #endif #endif static int Timestamp_getTimeZone(pg_time_t time) { #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) struct pg_tm* tx = pg_localtime(&time); #elif (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) struct pg_tm* tx = pg_localtime(&time, global_timezone); #else struct pg_tm* tx = pg_localtime(&time, session_timezone); #endif return -tx->tm_gmtoff; } int32 Timestamp_getTimeZone_id(int64 dt) { return Timestamp_getTimeZone( (dt / INT64CONST(1000000) + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400)); } int32 Timestamp_getTimeZone_dd(double dt) { return Timestamp_getTimeZone( (pg_time_t)rint(dt + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400)); } int32 Timestamp_getCurrentTimeZone(void) { return Timestamp_getTimeZone((pg_time_t)GetCurrentAbsoluteTime()); } extern void Timestamp_initialize(void); void Timestamp_initialize(void) { TypeClass cls; s_Timestamp_class = JNI_newGlobalRef(PgObject_getJavaClass("java/sql/Timestamp")); s_Timestamp_init = PgObject_getJavaMethod(s_Timestamp_class, "", "(J)V"); s_Timestamp_getNanos = PgObject_getJavaMethod(s_Timestamp_class, "getNanos", "()I"); s_Timestamp_getTime = PgObject_getJavaMethod(s_Timestamp_class, "getTime", "()J"); s_Timestamp_setNanos = PgObject_getJavaMethod(s_Timestamp_class, "setNanos", "(I)V"); cls = TypeClass_alloc("type.Timestamp"); cls->JNISignature = "Ljava/sql/Timestamp;"; cls->javaTypeName = "java.sql.Timestamp"; cls->canReplaceType = _Timestamp_canReplaceType; cls->coerceDatum = _Timestamp_coerceDatum; cls->coerceObject = _Timestamp_coerceObject; Type_registerType(0, TypeClass_allocInstance(cls, TIMESTAMPOID)); s_TimestampClass = cls; cls = TypeClass_alloc("type.Timestamptz"); cls->JNISignature = "Ljava/sql/Timestamp;"; cls->javaTypeName = "java.sql.Timestamp"; cls->canReplaceType = _Timestamptz_canReplaceType; cls->coerceDatum = _Timestamptz_coerceDatum; cls->coerceObject = _Timestamptz_coerceObject; Type_registerType("java.sql.Timestamp", TypeClass_allocInstance(cls, TIMESTAMPTZOID)); s_TimestamptzClass = cls; } pljava-1.4.3/src/C/pljava/type/Float.c0000644000014500000120000001107511634451404016547 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_floatClass; static jclass s_Float_class; static jclass s_FloatArray_class; static jmethodID s_Float_init; static jmethodID s_Float_floatValue; /* * float primitive type. */ static Datum _asDatum(jfloat v) { MemoryContext currCtx = Invocation_switchToUpperContext(); Datum ret = Float4GetDatum(v); MemoryContextSwitchTo(currCtx); return ret; } static Datum _float_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { return _asDatum(JNI_callStaticFloatMethodA(cls, method, args)); } static jvalue _float_coerceDatum(Type self, Datum arg) { jvalue result; result.f = DatumGetFloat4(arg); return result; } static jvalue _floatArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jfloatArray floatArray = JNI_newFloatArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setFloatArrayRegion(floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jfloat* values = (jfloat*)ARR_DATA_PTR(v); jfloat* elems = JNI_getFloatArrayElements(floatArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseFloatArrayElements(floatArray, elems, JNI_COMMIT); } else JNI_setFloatArrayRegion(floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); #endif result.l = (jobject)floatArray; return result; } static Datum _floatArray_coerceObject(Type self, jobject floatArray) { ArrayType* v; jsize nElems; if(floatArray == 0) return 0; nElems = JNI_getArrayLength((jarray)floatArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jfloat), FLOAT4OID); #else v = createArrayType(nElems, sizeof(jfloat), FLOAT4OID, false); #endif if(!JNI_isInstanceOf( floatArray, s_FloatArray_class)) JNI_getFloatArrayRegion((jfloatArray)floatArray, 0, nElems, (jfloat*)ARR_DATA_PTR(v)); else { int idx = 0; jfloat *array = (jfloat*)ARR_DATA_PTR(v); for(idx = 0; idx < nElems; ++idx) { array[idx] = JNI_callFloatMethod(JNI_getObjectArrayElement(floatArray, idx), s_Float_floatValue); } } PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Float type. */ static bool _Float_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_floatClass; } static jvalue _Float_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Float_class, s_Float_init, DatumGetFloat4(arg)); return result; } static Datum _Float_coerceObject(Type self, jobject floatObj) { return _asDatum(floatObj == 0 ? 0.0 : JNI_callFloatMethod(floatObj, s_Float_floatValue)); } static Type _float_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _floatArray_coerceDatum, _floatArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Float_initialize(void); void Float_initialize(void) { Type t_float; Type t_Float; TypeClass cls; s_Float_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Float")); s_FloatArray_class = JNI_newGlobalRef(PgObject_getJavaClass("[Ljava/lang/Float;")); s_Float_init = PgObject_getJavaMethod(s_Float_class, "", "(F)V"); s_Float_floatValue = PgObject_getJavaMethod(s_Float_class, "floatValue", "()F"); cls = TypeClass_alloc("type.Float"); cls->canReplaceType = _Float_canReplace; cls->JNISignature = "Ljava/lang/Float;"; cls->javaTypeName = "java.lang.Float"; cls->coerceDatum = _Float_coerceDatum; cls->coerceObject = _Float_coerceObject; t_Float = TypeClass_allocInstance(cls, FLOAT4OID); cls = TypeClass_alloc("type.float"); cls->JNISignature = "F"; cls->javaTypeName = "float"; cls->invoke = _float_invoke; cls->coerceDatum = _float_coerceDatum; cls->coerceObject = _Float_coerceObject; cls->createArrayType = _float_createArrayType; s_floatClass = cls; t_float = TypeClass_allocInstance(cls, FLOAT4OID); t_float->objectType = t_Float; Type_registerType("float", t_float); Type_registerType("java.lang.Float", t_Float); } pljava-1.4.3/src/C/pljava/type/Integer.c~0000644000014500000120000000771611634451404017304 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_intClass; static jclass s_Integer_class; static jmethodID s_Integer_init; static jmethodID s_Integer_intValue; /* * int primitive type. */ static Datum _int_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jint iv = JNI_callStaticIntMethodA(cls, method, args); return Int32GetDatum(iv); } static jvalue _int_coerceDatum(Type self, Datum arg) { jvalue result; result.i = DatumGetInt32(arg); return result; } static jvalue _intArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jintArray intArray = JNI_newIntArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setIntArrayRegion(intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jint* values = (jint*)ARR_DATA_PTR(v); jint* elems = JNI_getIntArrayElements(intArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseIntArrayElements(intArray, elems, JNI_COMMIT); } else JNI_setIntArrayRegion(intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); #endif result.l = (jobject)intArray; return result; } static Datum _intArray_coerceObject(Type self, jobject intArray) { ArrayType* v; jsize nElems; if(intArray == 0) return 0; nElems = JNI_getArrayLength((jarray)intArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jint), INT4OID); #else v = createArrayType(nElems, sizeof(jint), INT4OID, false); #endif JNI_getIntArrayRegion((jintArray)intArray, 0, nElems, (jint*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Integer type. */ static bool _Integer_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_intClass; } static jvalue _Integer_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Integer_class, s_Integer_init, DatumGetInt32(arg)); return result; } static Datum _Integer_coerceObject(Type self, jobject intObj) { return Int32GetDatum(intObj == 0 ? 0 : JNI_callIntMethod(intObj, s_Integer_intValue)); } static Type _int_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _intArray_coerceDatum, _intArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Integer_initialize(void); void Integer_initialize(void) { Type t_int; Type t_Integer; TypeClass cls; s_Integer_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Integer")); s_Integer_init = PgObject_getJavaMethod(s_Integer_class, "", "(I)V"); s_Integer_intValue = PgObject_getJavaMethod(s_Integer_class, "intValue", "()I"); cls = TypeClass_alloc("type.Integer"); cls->canReplaceType = _Integer_canReplace; cls->JNISignature = "Ljava/lang/Integer;"; cls->javaTypeName = "java.lang.Integer"; cls->coerceDatum = _Integer_coerceDatum; cls->coerceObject = _Integer_coerceObject; t_Integer = TypeClass_allocInstance(cls, INT4OID); cls = TypeClass_alloc("type.int"); cls->JNISignature = "I"; cls->javaTypeName = "int"; cls->invoke = _int_invoke; cls->coerceDatum = _int_coerceDatum; cls->coerceObject = _Integer_coerceObject; cls->createArrayType = _int_createArrayType; s_intClass = cls; t_int = TypeClass_allocInstance(cls, INT4OID); t_int->objectType = t_Integer; Type_registerType("int", t_int); Type_registerType("java.lang.Integer", t_Integer); } pljava-1.4.3/src/C/pljava/type/AclId.c0000644000014500000120000001242611634451404016457 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 1) #include #endif #include "pljava/type/AclId.h" #include "pljava/type/Oid.h" #include "pljava/type/String.h" #include "pljava/type/Type_priv.h" #include "org_postgresql_pljava_internal_AclId.h" #include "pljava/Exception.h" static jclass s_AclId_class; static jmethodID s_AclId_init; static jfieldID s_AclId_m_native; /* * org.postgresql.pljava.type.AclId type. */ jobject AclId_create(AclId aclId) { return JNI_newObject(s_AclId_class, s_AclId_init, (jint)aclId); } AclId AclId_getAclId(jobject aclId) { return (AclId)JNI_getIntField(aclId, s_AclId_m_native); } extern void AclId_initialize(void); void AclId_initialize(void) { JNINativeMethod methods[] = { { "_getUser", "()Lorg/postgresql/pljava/internal/AclId;", Java_org_postgresql_pljava_internal_AclId__1getUser }, { "_getSessionUser", "()Lorg/postgresql/pljava/internal/AclId;", Java_org_postgresql_pljava_internal_AclId__1getSessionUser }, { "_fromName", "(Ljava/lang/String;)Lorg/postgresql/pljava/internal/AclId;", Java_org_postgresql_pljava_internal_AclId__1fromName }, { "_getName", "()Ljava/lang/String;", Java_org_postgresql_pljava_internal_AclId__1getName }, { "_hasSchemaCreatePermission", "(Lorg/postgresql/pljava/internal/Oid;)Z", Java_org_postgresql_pljava_internal_AclId__1hasSchemaCreatePermission }, { "_isSuperuser", "()Z", Java_org_postgresql_pljava_internal_AclId__1isSuperuser }, { 0, 0, 0 }}; s_AclId_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/AclId")); PgObject_registerNatives2(s_AclId_class, methods); s_AclId_init = PgObject_getJavaMethod(s_AclId_class, "", "(I)V"); s_AclId_m_native = PgObject_getJavaField(s_AclId_class, "m_native", "I"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_AclId * Method: _getUser * Signature: ()Lorg/postgresql/pljava/internal/AclId; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_AclId__1getUser(JNIEnv* env, jclass clazz) { jobject result = 0; BEGIN_NATIVE PG_TRY(); { result = AclId_create(GetUserId()); } PG_CATCH(); { Exception_throw_ERROR("GetUserId"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_AclId * Method: _getSessionUser * Signature: ()Lorg/postgresql/pljava/internal/AclId; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_AclId__1getSessionUser(JNIEnv* env, jclass clazz) { jobject result = 0; BEGIN_NATIVE PG_TRY(); { result = AclId_create(GetSessionUserId()); } PG_CATCH(); { Exception_throw_ERROR("GetSessionUserId"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_AclId * Method: _fromName * Signature: (Ljava/lang/String;)Lorg/postgresql/pljava/internal/AclId; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_AclId__1fromName(JNIEnv* env, jclass clazz, jstring jname) { jobject result = 0; if(jname != 0) { BEGIN_NATIVE PG_TRY(); { char* roleName = String_createNTS(jname); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 1) result = AclId_create(get_usesysid(roleName)); #else HeapTuple roleTup = SearchSysCache(AUTHNAME, PointerGetDatum(roleName), 0, 0, 0); if(!HeapTupleIsValid(roleTup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", roleName))); result = AclId_create(HeapTupleGetOid(roleTup)); ReleaseSysCache(roleTup); #endif } PG_CATCH(); { Exception_throw_ERROR("SearchSysCache"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_AclId * Method: _getName * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_AclId__1getName(JNIEnv* env, jobject aclId) { jstring result = 0; BEGIN_NATIVE PG_TRY(); { result = String_createJavaStringFromNTS(GetUserNameFromId(AclId_getAclId(aclId))); } PG_CATCH(); { Exception_throw_ERROR("GetUserNameFromId"); } PG_END_TRY(); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_AclId * Method: _hasSchemaCreatePermission * Signature: (Lorg/postgresql/pljava/internal/Oid)Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_AclId__1hasSchemaCreatePermission(JNIEnv* env, jobject aclId, jobject oid) { jboolean result = JNI_FALSE; BEGIN_NATIVE result = (jboolean)(pg_namespace_aclcheck(Oid_getOid(oid), AclId_getAclId(aclId), ACL_CREATE) == ACLCHECK_OK); END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_AclId * Method: _isSuperuser * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_AclId__1isSuperuser(JNIEnv* env, jobject aclId) { jboolean result = JNI_FALSE; BEGIN_NATIVE result = superuser_arg(AclId_getAclId(aclId)) ? JNI_TRUE : JNI_FALSE; END_NATIVE return result; } pljava-1.4.3/src/C/pljava/type/Composite.c0000644000014500000120000002353111634451404017444 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include "pljava/type/Type_priv.h" #include "pljava/type/Composite.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/HeapTupleHeader.h" #include "pljava/Invocation.h" #include "pljava/backports.h" #include "org_postgresql_pljava_jdbc_SingleRowReader.h" struct Composite_ { /* * The String "class" extends Type so the first * entry must be the Type_ structure. This enables us * to cast the CompositeType to a Type. */ struct Type_ Type_extension; /* * The TupleDesc associated with the SETOF function. */ TupleDesc m_tupleDesc; }; typedef struct Composite_* Composite; static jclass s_ResultSetProvider_class; static jmethodID s_ResultSetProvider_assignRowValues; static jmethodID s_ResultSetProvider_close; static jclass s_ResultSetHandle_class; static jclass s_ResultSetPicker_class; static jmethodID s_ResultSetPicker_init; static jclass s_SingleRowReader_class; static jmethodID s_SingleRowReader_init; static jclass s_SingleRowWriter_class; static jmethodID s_SingleRowWriter_init; static jmethodID s_SingleRowWriter_getTupleAndClear; static TypeClass s_CompositeClass; static jobject _createWriter(jobject tupleDesc) { return JNI_newObject(s_SingleRowWriter_class, s_SingleRowWriter_init, tupleDesc); } static HeapTuple _getTupleAndClear(jobject jrps) { Ptr2Long p2l; if(jrps == 0) return 0; p2l.longVal = JNI_callLongMethod(jrps, s_SingleRowWriter_getTupleAndClear); return (HeapTuple)p2l.ptrVal; } /* * This function is a bit special in that it adds an additional parameter * to the parameter list (a java.sql.ResultSet implemented as a * SingleRowWriter) and calls a boolean method. It's assumed that the * SingleRowWriter has been initialized with values if the method returns * true. If so, the values are obtained in the form of a HeapTuple which in * turn is returned (as a Datum) from this method. */ static Datum _Composite_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { bool hasRow; Datum result = 0; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); jobject jtd = TupleDesc_create(tupleDesc); jobject singleRowWriter = _createWriter(jtd); int numArgs = fcinfo->nargs; // Caller guarantees room for one extra slot // args[numArgs].l = singleRowWriter; hasRow = (JNI_callStaticBooleanMethodA(cls, method, args) == JNI_TRUE); if(hasRow) { /* Obtain tuple and return it as a Datum. Must be done using a more * durable context. */ MemoryContext currCtx = Invocation_switchToUpperContext(); HeapTuple tuple = _getTupleAndClear(singleRowWriter); result = HeapTupleGetDatum(tuple); MemoryContextSwitchTo(currCtx); } else fcinfo->isnull = true; JNI_deleteLocalRef(jtd); JNI_deleteLocalRef(singleRowWriter); return result; } static jobject _Composite_getSRFProducer(Type self, jclass cls, jmethodID method, jvalue* args) { jobject tmp = JNI_callStaticObjectMethodA(cls, method, args); if(tmp != 0 && JNI_isInstanceOf(tmp, s_ResultSetHandle_class)) { jobject wrapper = JNI_newObject(s_ResultSetPicker_class, s_ResultSetPicker_init, tmp); JNI_deleteLocalRef(tmp); tmp = wrapper; } return tmp; } static jobject _Composite_getSRFCollector(Type self, PG_FUNCTION_ARGS) { jobject tmp1; jobject tmp2; TupleDesc tupleDesc = Type_getTupleDesc(self, fcinfo); if(tupleDesc == 0) ereport(ERROR, (errmsg("Unable to find tuple descriptor"))); tmp1 = TupleDesc_create(tupleDesc); tmp2 = _createWriter(tmp1); JNI_deleteLocalRef(tmp1); return tmp2; } static bool _Composite_hasNextSRF(Type self, jobject rowProducer, jobject rowCollector, jint callCounter) { /* Obtain next row using the RowCollector as a parameter to the * ResultSetProvider.assignRowValues method. */ return (JNI_callBooleanMethod(rowProducer, s_ResultSetProvider_assignRowValues, rowCollector, callCounter) == JNI_TRUE); } static Datum _Composite_nextSRF(Type self, jobject rowProducer, jobject rowCollector) { Datum result = 0; HeapTuple tuple = _getTupleAndClear(rowCollector); if(tuple != 0) result = HeapTupleGetDatum(tuple); return result; } static void _Composite_closeSRF(Type self, jobject rowProducer) { JNI_callVoidMethod(rowProducer, s_ResultSetProvider_close); } /* Assume that the Datum is a HeapTupleHeader and convert it into * a SingleRowReader instance. */ static jvalue _Composite_coerceDatum(Type self, Datum arg) { jobject tupleDesc; jvalue result; jlong pointer; HeapTupleHeader hth = DatumGetHeapTupleHeader(arg); result.l = 0; if(hth == 0) return result; tupleDesc = HeapTupleHeader_getTupleDesc(hth); pointer = Invocation_createLocalWrapper(hth); result.l = JNI_newObject(s_SingleRowReader_class, s_SingleRowReader_init, pointer, tupleDesc); JNI_deleteLocalRef(tupleDesc); return result; } static TupleDesc createGlobalTupleDescCopy(TupleDesc td) { MemoryContext curr = MemoryContextSwitchTo(TopMemoryContext); td = CreateTupleDescCopyConstr(td); MemoryContextSwitchTo(curr); return td; } static TupleDesc _Composite_getTupleDesc(Type self, PG_FUNCTION_ARGS) { TupleDesc td = ((Composite)self)->m_tupleDesc; if(td != 0) return td; switch(get_call_result_type(fcinfo, 0, &td)) { case TYPEFUNC_COMPOSITE: case TYPEFUNC_RECORD: if(td->tdtypeid == RECORDOID) /* * We can't hold on to this one. It's anonymous * and may vary between calls. */ td = CreateTupleDescCopy(td); else { td = createGlobalTupleDescCopy(td); ((Composite)self)->m_tupleDesc = td; } break; default: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); } return td; } static const char* _Composite_getJNIReturnSignature(Type self, bool forMultiCall, bool useAltRepr) { return forMultiCall ? (useAltRepr ? "Lorg/postgresql/pljava/ResultSetHandle;" : "Lorg/postgresql/pljava/ResultSetProvider;") : "Z"; } Type Composite_obtain(Oid typeId) { Composite infant = (Composite)TypeClass_allocInstance(s_CompositeClass, typeId); if(typeId == RECORDOID) infant->m_tupleDesc = 0; else { TupleDesc tmp = lookup_rowtype_tupdesc(typeId, -1); infant->m_tupleDesc = createGlobalTupleDescCopy(tmp); #if ((PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 2) || PGSQL_MAJOR_VER > 8) ReleaseTupleDesc(tmp); #endif } return (Type)infant; } /* Make this datatype available to the postgres system. */ extern void Composite_initialize(void); void Composite_initialize(void) { JNINativeMethod methods[] = { { "_getObject", "(JJI)Ljava/lang/Object;", Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject }, { "_free", "(J)V", Java_org_postgresql_pljava_jdbc_SingleRowReader__1free }, { 0, 0, 0 } }; s_SingleRowReader_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowReader")); PgObject_registerNatives2(s_SingleRowReader_class, methods); s_SingleRowReader_init = PgObject_getJavaMethod(s_SingleRowReader_class, "", "(JLorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SingleRowWriter")); s_SingleRowWriter_init = PgObject_getJavaMethod(s_SingleRowWriter_class, "", "(Lorg/postgresql/pljava/internal/TupleDesc;)V"); s_SingleRowWriter_getTupleAndClear = PgObject_getJavaMethod(s_SingleRowWriter_class, "getTupleAndClear", "()J"); s_ResultSetProvider_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetProvider")); s_ResultSetProvider_assignRowValues = PgObject_getJavaMethod(s_ResultSetProvider_class, "assignRowValues", "(Ljava/sql/ResultSet;I)Z"); s_ResultSetProvider_close = PgObject_getJavaMethod(s_ResultSetProvider_class, "close", "()V"); s_ResultSetHandle_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/ResultSetHandle")); s_ResultSetPicker_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/ResultSetPicker")); s_ResultSetPicker_init = PgObject_getJavaMethod(s_ResultSetPicker_class, "", "(Lorg/postgresql/pljava/ResultSetHandle;)V"); s_CompositeClass = TypeClass_alloc2("type.Composite", sizeof(struct TypeClass_), sizeof(struct Composite_)); s_CompositeClass->JNISignature = "Ljava/sql/ResultSet;"; s_CompositeClass->javaTypeName = "java.sql.ResultSet"; s_CompositeClass->getTupleDesc = _Composite_getTupleDesc; s_CompositeClass->coerceDatum = _Composite_coerceDatum; s_CompositeClass->invoke = _Composite_invoke; s_CompositeClass->getSRFProducer = _Composite_getSRFProducer; s_CompositeClass->getSRFCollector = _Composite_getSRFCollector; s_CompositeClass->hasNextSRF = _Composite_hasNextSRF; s_CompositeClass->nextSRF = _Composite_nextSRF; s_CompositeClass->closeSRF = _Composite_closeSRF; s_CompositeClass->getJNIReturnSignature = _Composite_getJNIReturnSignature; s_CompositeClass->outParameter = true; Type_registerType2(InvalidOid, "java.sql.ResultSet", Composite_obtain); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1free(JNIEnv* env, jobject _this, jlong hth) { HeapTupleHeader_free(env, hth); } /* * Class: org_postgresql_pljava_jdbc_SingleRowReader * Method: _getObject * Signature: (JJI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_jdbc_SingleRowReader__1getObject(JNIEnv* env, jclass clazz, jlong hth, jlong jtd, jint attrNo) { return HeapTupleHeader_getObject(env, hth, jtd, attrNo); } pljava-1.4.3/src/C/pljava/type/Short.c~0000644000014500000120000000777511634451404017013 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_shortClass; static jclass s_Short_class; static jmethodID s_Short_init; static jmethodID s_Short_shortValue; /* * short primitive type. */ static Datum _short_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jshort v = JNI_callStaticShortMethodA(cls, method, args); return Int16GetDatum(v); } static jvalue _short_coerceDatum(Type self, Datum arg) { jvalue result; result.s = DatumGetInt16(arg); return result; } static jvalue _shortArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jshortArray shortArray = JNI_newShortArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setShortArrayRegion(shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jshort* values = (jshort*)ARR_DATA_PTR(v); jshort* elems = JNI_getShortArrayElements(shortArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseShortArrayElements(shortArray, elems, JNI_COMMIT); } else JNI_setShortArrayRegion(shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); #endif result.l = (jobject)shortArray; return result; } static Datum _shortArray_coerceObject(Type self, jobject shortArray) { ArrayType* v; jsize nElems; if(shortArray == 0) return 0; nElems = JNI_getArrayLength((jarray)shortArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jshort), INT2OID); #else v = createArrayType(nElems, sizeof(jshort), INT2OID, false); #endif JNI_getShortArrayRegion((jshortArray)shortArray, 0, nElems, (jshort*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Short type. */ static bool _Short_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_shortClass; } static jvalue _Short_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Short_class, s_Short_init, DatumGetInt16(arg)); return result; } static Datum _Short_coerceObject(Type self, jobject shortObj) { return Int16GetDatum(shortObj == 0 ? 0 : JNI_callShortMethod(shortObj, s_Short_shortValue)); } static Type _short_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _shortArray_coerceDatum, _shortArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Short_initialize(void); void Short_initialize(void) { Type t_short; Type t_Short; TypeClass cls; s_Short_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Short")); s_Short_init = PgObject_getJavaMethod(s_Short_class, "", "(S)V"); s_Short_shortValue = PgObject_getJavaMethod(s_Short_class, "shortValue", "()S"); cls = TypeClass_alloc("type.Short"); cls->canReplaceType = _Short_canReplace; cls->JNISignature = "Ljava/lang/Short;"; cls->javaTypeName = "java.lang.Short"; cls->coerceDatum = _Short_coerceDatum; cls->coerceObject = _Short_coerceObject; t_Short = TypeClass_allocInstance(cls, INT2OID); cls = TypeClass_alloc("type.short"); cls->JNISignature = "S"; cls->javaTypeName = "short"; cls->invoke = _short_invoke; cls->coerceDatum = _short_coerceDatum; cls->coerceObject = _Short_coerceObject; cls->createArrayType = _short_createArrayType; s_shortClass = cls; t_short = TypeClass_allocInstance(cls, INT2OID); t_short->objectType = t_Short; Type_registerType("short", t_short); Type_registerType("java.lang.Short", t_Short); } pljava-1.4.3/src/C/pljava/type/Oid.c0000644000014500000120000001421311634451404016212 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #define Type PGType #include #undef Type #include "org_postgresql_pljava_internal_Oid.h" #include "java_sql_Types.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Oid.h" #include "pljava/type/String.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" static jclass s_Oid_class; static jmethodID s_Oid_init; static jmethodID s_Oid_registerType; static jfieldID s_Oid_m_native; static jobject s_OidOid; /* * org.postgresql.pljava.type.Oid type. */ jobject Oid_create(Oid oid) { jobject joid; if(OidIsValid(oid)) joid = JNI_newObject(s_Oid_class, s_Oid_init, oid); else joid = 0; return joid; } Oid Oid_getOid(jobject joid) { if(joid == 0) return InvalidOid; return ObjectIdGetDatum(JNI_getIntField(joid, s_Oid_m_native)); } Oid Oid_forSqlType(int sqlType) { Oid typeId; switch(sqlType) { case java_sql_Types_BIT: typeId = BITOID; break; case java_sql_Types_TINYINT: typeId = CHAROID; break; case java_sql_Types_SMALLINT: typeId = INT2OID; break; case java_sql_Types_INTEGER: typeId = INT4OID; break; case java_sql_Types_BIGINT: typeId = INT8OID; break; case java_sql_Types_FLOAT: case java_sql_Types_REAL: typeId = FLOAT4OID; break; case java_sql_Types_DOUBLE: typeId = FLOAT8OID; break; case java_sql_Types_NUMERIC: case java_sql_Types_DECIMAL: typeId = NUMERICOID; break; case java_sql_Types_DATE: typeId = DATEOID; break; case java_sql_Types_TIME: typeId = TIMEOID; break; case java_sql_Types_TIMESTAMP: typeId = TIMESTAMPOID; break; case java_sql_Types_BOOLEAN: typeId = BOOLOID; break; case java_sql_Types_BINARY: case java_sql_Types_VARBINARY: case java_sql_Types_LONGVARBINARY: case java_sql_Types_BLOB: typeId = BYTEAOID; break; case java_sql_Types_CHAR: case java_sql_Types_VARCHAR: case java_sql_Types_LONGVARCHAR: case java_sql_Types_CLOB: case java_sql_Types_DATALINK: typeId = TEXTOID; break; /* case java_sql_Types_NULL: case java_sql_Types_OTHER: case java_sql_Types_JAVA_OBJECT: case java_sql_Types_DISTINCT: case java_sql_Types_STRUCT: case java_sql_Types_ARRAY: case java_sql_Types_REF: */ default: typeId = InvalidOid; /* Not yet mapped */ break; } return typeId; } static jvalue _Oid_coerceDatum(Type self, Datum arg) { jvalue result; result.l = Oid_create(DatumGetObjectId(arg)); return result; } static Datum _Oid_coerceObject(Type self, jobject oidObj) { return Oid_getOid(oidObj); } /* Make this datatype available to the postgres system. */ extern void Oid_initialize(void); void Oid_initialize(void) { TypeClass cls; JNINativeMethod methods[] = { { "_forTypeName", "(Ljava/lang/String;)I", Java_org_postgresql_pljava_internal_Oid__1forTypeName }, { "_forSqlType", "(I)I", Java_org_postgresql_pljava_internal_Oid__1forSqlType }, { "_getTypeId", "()Lorg/postgresql/pljava/internal/Oid;", Java_org_postgresql_pljava_internal_Oid__1getTypeId }, { "_getJavaClassName", "(I)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Oid__1getJavaClassName }, { 0, 0, 0 }}; jobject tmp; s_Oid_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Oid")); PgObject_registerNatives2(s_Oid_class, methods); s_Oid_init = PgObject_getJavaMethod(s_Oid_class, "", "(I)V"); s_Oid_m_native = PgObject_getJavaField(s_Oid_class, "m_native", "I"); cls = TypeClass_alloc("type.Oid"); cls->JNISignature = "Lorg/postgresql/pljava/internal/Oid;"; cls->javaTypeName = "org.postgresql.pljava.internal.Oid"; cls->coerceDatum = _Oid_coerceDatum; cls->coerceObject = _Oid_coerceObject; Type_registerType("org.postgresql.pljava.internal.Oid", TypeClass_allocInstance(cls, OIDOID)); tmp = Oid_create(OIDOID); s_OidOid = JNI_newGlobalRef(tmp); JNI_deleteLocalRef(tmp); s_Oid_registerType = PgObject_getStaticJavaMethod( s_Oid_class, "registerType", "(Ljava/lang/Class;Lorg/postgresql/pljava/internal/Oid;)V"); JNI_callStaticVoidMethod(s_Oid_class, s_Oid_registerType, s_Oid_class, s_OidOid); } /* * Class: org_postgresql_pljava_internal_Oid * Method: _forSqlType * Signature: (I)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Oid__1forSqlType(JNIEnv* env, jclass cls, jint sqlType) { Oid typeId = InvalidOid; BEGIN_NATIVE typeId = Oid_forSqlType(sqlType); if(typeId == InvalidOid) Exception_throw(ERRCODE_INTERNAL_ERROR, "No such SQL type: %d", (int)sqlType); END_NATIVE return typeId; } /* * Class: org_postgresql_pljava_internal_Oid * Method: _forTypeName * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Oid__1forTypeName(JNIEnv* env, jclass cls, jstring typeString) { Oid typeId = InvalidOid; BEGIN_NATIVE char* typeNameOrOid = String_createNTS(typeString); if(typeNameOrOid != 0) { PG_TRY(); { int32 typmod = 0; parseTypeString(typeNameOrOid, &typeId, &typmod); } PG_CATCH(); { Exception_throw_ERROR("parseTypeString"); } PG_END_TRY(); pfree(typeNameOrOid); } END_NATIVE return typeId; } /* * Class: org_postgresql_pljava_internal_Oid * Method: _getTypeId * Signature: ()Lorg/postgresql/pljava/internal/Oid; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Oid__1getTypeId(JNIEnv* env, jclass cls) { return s_OidOid; } /* * Class: org_postgresql_pljava_internal_Oid * Method: _getJavaClassName * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Oid__1getJavaClassName(JNIEnv* env, jclass cls, jint oid) { jstring result = 0; BEGIN_NATIVE if(!OidIsValid((Oid)oid)) { Exception_throw(ERRCODE_DATA_EXCEPTION, "Invalid OID \"%d\"", (int)oid); } else { Type type = Type_objectTypeFromOid((Oid)oid, Invocation_getTypeMap()); result = String_createJavaStringFromNTS(Type_getJavaTypeName(type)); } END_NATIVE return result; } pljava-1.4.3/src/C/pljava/type/Time.c0000644000014500000120000001302611634451404016376 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include #include #include "pljava/Backend.h" #include "pljava/type/Type_priv.h" #include "pljava/type/Time.h" #include "pljava/type/Timestamp.h" /* * Time type. Postgres will pass (and expect in return) a local Time. * The Java java.sql.Time is UTC time and not a perfect fit. Perhaps * a LocalTime object should be added to the Java domain? */ static jclass s_Time_class; static jmethodID s_Time_init; static jmethodID s_Time_getTime; static jlong msecsAtMidnight(void) { AbsoluteTime now = GetCurrentAbsoluteTime() / 86400; return INT64CONST(1000) * (jlong)(now * 86400); } static jvalue Time_coerceDatumTZ_dd(Type self, double t, bool tzAdjust) { jlong mSecs; jvalue result; if(tzAdjust) t += Timestamp_getCurrentTimeZone();/* Adjust from local time to UTC */ t *= 1000.0; /* Convert to millisecs */ mSecs = (jlong)floor(t); result.l = JNI_newObject(s_Time_class, s_Time_init, mSecs + msecsAtMidnight()); return result; } static jvalue Time_coerceDatumTZ_id(Type self, int64 t, bool tzAdjust) { jvalue result; jlong mSecs = t / 1000; /* Convert to millisecs */ if(tzAdjust) mSecs += Timestamp_getCurrentTimeZone() * 1000;/* Adjust from local time to UTC */ result.l = JNI_newObject(s_Time_class, s_Time_init, mSecs + msecsAtMidnight()); return result; } static jlong Time_getMillisecsToday(Type self, jobject jt, bool tzAdjust) { jlong mSecs = JNI_callLongMethod(jt, s_Time_getTime); if(tzAdjust) mSecs -= ((jlong)Timestamp_getCurrentTimeZone()) * 1000L; /* Adjust from UTC to local time */ mSecs %= 86400000; /* Strip everything above 24 hours */ return mSecs; } static double Time_coerceObjectTZ_dd(Type self, jobject jt, bool tzAdjust) { jlong mSecs = Time_getMillisecsToday(self, jt, tzAdjust); return ((double)mSecs) / 1000.0; /* Convert to seconds */ } static int64 Time_coerceObjectTZ_id(Type self, jobject jt, bool tzAdjust) { jlong mSecs = Time_getMillisecsToday(self, jt, tzAdjust); return mSecs * 1000L; /* Convert millisecs to microsecs */ } static jvalue _Time_coerceDatum(Type self, Datum arg) { #if (PGSQL_MAJOR_VERSION == 8 && PGSQL_MINOR_VERSION == 0) /* * PostgreSQL 8.0 and earlier has a major bug in how int64 times are * stored. They are actually first casted to a double */ return integerDateTimes ? Time_coerceDatumTZ_id(self, DatumGetFloat8(arg), true) : Time_coerceDatumTZ_dd(self, DatumGetFloat8(arg), true); #else return integerDateTimes ? Time_coerceDatumTZ_id(self, DatumGetInt64(arg), true) : Time_coerceDatumTZ_dd(self, DatumGetFloat8(arg), true); #endif } static Datum _Time_coerceObject(Type self, jobject time) { #if (PGSQL_MAJOR_VERSION == 8 && PGSQL_MINOR_VERSION == 0) /* * PostgreSQL 8.0 and earlier has a major bug in how int64 times are * stored. They are actually first casted to a double */ return integerDateTimes ? Float8GetDatum(Time_coerceObjectTZ_id(self, time, true)) : Float8GetDatum(Time_coerceObjectTZ_dd(self, time, true)); #else return integerDateTimes ? Int64GetDatum(Time_coerceObjectTZ_id(self, time, true)) : Float8GetDatum(Time_coerceObjectTZ_dd(self, time, true)); #endif } /* * Time with time zone. Postgres will pass local time and an associated * time zone. In the future, we might create a special java object for * this. For now, we just convert to UTC and pass a Time object. */ static jvalue _Timetz_coerceDatum(Type self, Datum arg) { jvalue val; if(integerDateTimes) { TimeTzADT_id* tza = (TimeTzADT_id*)DatumGetPointer(arg); int64 t = tza->time + (int64)tza->zone * 1000000; /* Convert to UTC */ val = Time_coerceDatumTZ_id(self, t, false); } else { TimeTzADT_dd* tza = (TimeTzADT_dd*)DatumGetPointer(arg); double t = tza->time + tza->zone; /* Convert to UTC */ val = Time_coerceDatumTZ_dd(self, t, false); } return val; } static Datum _Timetz_coerceObject(Type self, jobject time) { Datum datum; if(integerDateTimes) { TimeTzADT_id* tza = (TimeTzADT_id*)palloc(sizeof(TimeTzADT_id)); tza->time = Time_coerceObjectTZ_id(self, time, false); tza->zone = Timestamp_getCurrentTimeZone(); tza->time -= (int64)tza->zone * 1000000; /* Convert UTC to local time */ datum = PointerGetDatum(tza); } else { TimeTzADT_dd* tza = (TimeTzADT_dd*)palloc(sizeof(TimeTzADT_dd)); tza->time = Time_coerceObjectTZ_dd(self, time, false); tza->zone = Timestamp_getCurrentTimeZone(); tza->time -= tza->zone; /* Convert UTC to local time */ datum = PointerGetDatum(tza); } return datum; } extern void Time_initialize(void); void Time_initialize(void) { TypeClass cls; s_Time_class = JNI_newGlobalRef(PgObject_getJavaClass("java/sql/Time")); s_Time_init = PgObject_getJavaMethod(s_Time_class, "", "(J)V"); s_Time_getTime = PgObject_getJavaMethod(s_Time_class, "getTime", "()J"); cls = TypeClass_alloc("type.Time"); cls->JNISignature = "Ljava/sql/Time;"; cls->javaTypeName = "java.sql.Time"; cls->coerceDatum = _Time_coerceDatum; cls->coerceObject = _Time_coerceObject; Type_registerType(0, TypeClass_allocInstance(cls, TIMEOID)); cls = TypeClass_alloc("type.Timetz"); cls->JNISignature = "Ljava/sql/Time;"; cls->javaTypeName = "java.sql.Time"; cls->coerceDatum = _Timetz_coerceDatum; cls->coerceObject = _Timetz_coerceObject; Type_registerType("java.sql.Time", TypeClass_allocInstance(cls, TIMETZOID)); } pljava-1.4.3/src/C/pljava/type/Array.c~0000644000014500000120000001341111634451404016752 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) void arraySetNull(bits8* bitmap, int offset, bool flag) { if(bitmap != 0) { int bitmask = 1 << (offset % 8); bitmap += offset / 8; if(flag) *bitmap &= ~bitmask; else *bitmap |= bitmask; } } bool arrayIsNull(const bits8* bitmap, int offset) { return bitmap == 0 ? false : !(bitmap[offset / 8] & (1 << (offset % 8))); } ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType, bool withNulls) #else ArrayType* createArrayType(jsize nElems, size_t elemSize, Oid elemType) #endif { ArrayType* v; int nBytes = elemSize * nElems; MemoryContext currCtx = Invocation_switchToUpperContext(); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) #define LEAFKEY (1<<31) nBytes += ARR_OVERHEAD(1); v = (ArrayType*)palloc0(nBytes); v->flags &= ~LEAFKEY; #else int dataoffset; if(withNulls) { dataoffset = ARR_OVERHEAD_WITHNULLS(1, nElems); nBytes += dataoffset; } else { dataoffset = 0; /* marker for no null bitmap */ nBytes += ARR_OVERHEAD_NONULLS(1); } v = (ArrayType*)palloc0(nBytes); v->dataoffset = dataoffset; #endif MemoryContextSwitchTo(currCtx); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) ARR_SIZE(v) = nBytes; #else SET_VARSIZE(v, nBytes); #endif ARR_NDIM(v) = 1; ARR_ELEMTYPE(v) = elemType; *((int*)ARR_DIMS(v)) = nElems; *((int*)ARR_LBOUND(v)) = 1; return v; } static jvalue _Array_coerceDatum(Type self, Datum arg) { jvalue result; jsize idx; Type elemType = Type_getElementType(self); int16 elemLength = Type_getLength(elemType); char elemAlign = Type_getAlign(elemType); bool elemByValue = Type_isByValue(elemType); ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jobjectArray objArray = JNI_newObjectArray(nElems, Type_getJavaClass(elemType), 0); const char* values = ARR_DATA_PTR(v); #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) bits8* nullBitMap = ARR_NULLBITMAP(v); #endif for(idx = 0; idx < nElems; ++idx) { #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); values = att_addlength(values, elemLength, PointerGetDatum(values)); values = (char*)att_align(values, elemAlign); #else if(arrayIsNull(nullBitMap, idx)) JNI_setObjectArrayElement(objArray, idx, 0); else { Datum value = fetch_att(values, elemByValue, elemLength); jvalue obj = Type_coerceDatum(elemType, value); JNI_setObjectArrayElement(objArray, idx, obj.l); JNI_deleteLocalRef(obj.l); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 3) values = att_addlength(values, elemLength, PointerGetDatum(values)); values = (char*)att_align(values, elemAlign); #else values = att_addlength_datum(values, elemLength, PointerGetDatum(values)); values = (char*)att_align_nominal(values, elemAlign); #endif } #endif } result.l = (jobject)objArray; return result; } static Datum _Array_coerceObject(Type self, jobject objArray) { ArrayType* v; jsize idx; int lowerBound = 1; Type elemType = Type_getElementType(self); int nElems = (int)JNI_getArrayLength((jarray)objArray); Datum* values = (Datum*)palloc(nElems * sizeof(Datum) + nElems * sizeof(bool)); bool* nulls = (bool*)(values + nElems); for(idx = 0; idx < nElems; ++idx) { jobject obj = JNI_getObjectArrayElement(objArray, idx); if(obj == 0) { nulls[idx] = true; values[idx] = 0; } else { nulls[idx] = false; values[idx] = Type_coerceObject(elemType, obj); JNI_deleteLocalRef(obj); } } v = construct_md_array( values, #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) nulls, #endif 1, &nElems, &lowerBound, Type_getOid(elemType), Type_getLength(elemType), Type_isByValue(elemType), Type_getAlign(elemType)); pfree(values); PG_RETURN_ARRAYTYPE_P(v); } static bool _Array_canReplaceType(Type self, Type other) { Type oe = Type_getElementType(other); return oe == 0 ? false : Type_canReplaceType(Type_getElementType(self), oe); } Type Array_fromOid(Oid typeId, Type elementType) { return Array_fromOid2(typeId, elementType, _Array_coerceDatum, _Array_coerceObject); } Type Array_fromOid2(Oid typeId, Type elementType, DatumCoercer coerceDatum, ObjectCoercer coerceObject) { Type self; TypeClass arrayClass; const char* elemClassName = PgObjectClass_getName(PgObject_getClass((PgObject)elementType)); const char* elemJNISignature = Type_getJNISignature(elementType); const char* elemJavaTypeName = Type_getJavaTypeName(elementType); MemoryContext currCtx = MemoryContextSwitchTo(TopMemoryContext); char* tmp = palloc(strlen(elemClassName) + 3); sprintf(tmp, "%s[]", elemClassName); arrayClass = TypeClass_alloc(tmp); tmp = palloc(strlen(elemJNISignature) + 2); sprintf(tmp, "[%s", elemJNISignature); arrayClass->JNISignature = tmp; tmp = palloc(strlen(elemJavaTypeName) + 3); sprintf(tmp, "%s[]", elemJavaTypeName); arrayClass->javaTypeName = tmp; arrayClass->coerceDatum = coerceDatum; arrayClass->coerceObject = coerceObject; arrayClass->canReplaceType = _Array_canReplaceType; self = TypeClass_allocInstance(arrayClass, typeId); MemoryContextSwitchTo(currCtx); self->elementType = elementType; Type_registerType(arrayClass->javaTypeName, self); if(Type_isPrimitive(elementType)) self->objectType = Array_fromOid(typeId, Type_getObjectType(elementType)); return self; } pljava-1.4.3/src/C/pljava/type/Boolean.c~0000644000014500000120000001027011634451404017253 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/type/Type_priv.h" #include "pljava/type/Array.h" #include "pljava/Invocation.h" static TypeClass s_booleanClass; static jclass s_Boolean_class; static jmethodID s_Boolean_init; static jmethodID s_Boolean_booleanValue; /* * boolean primitive type. */ static Datum _boolean_invoke(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS) { jboolean v = JNI_callStaticBooleanMethodA(cls, method, args); return BoolGetDatum(v); } static jvalue _boolean_coerceDatum(Type self, Datum arg) { jvalue result; result.z = DatumGetBool(arg); return result; } static jvalue _booleanArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jbooleanArray booleanArray = JNI_newBooleanArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setBooleanArrayRegion(booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jboolean* values = (jboolean*)ARR_DATA_PTR(v); jboolean* elems = JNI_getBooleanArrayElements(booleanArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseBooleanArrayElements(booleanArray, elems, JNI_COMMIT); } else JNI_setBooleanArrayRegion(booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); #endif result.l = (jobject)booleanArray; return result; } static Datum _booleanArray_coerceObject(Type self, jobject booleanArray) { ArrayType* v; jsize nElems; if(booleanArray == 0) return 0; nElems = JNI_getArrayLength((jarray)booleanArray); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) v = createArrayType(nElems, sizeof(jboolean), BOOLOID); #else v = createArrayType(nElems, sizeof(jboolean), BOOLOID, false); #endif JNI_getBooleanArrayRegion((jbooleanArray)booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); PG_RETURN_ARRAYTYPE_P(v); } /* * java.lang.Boolean type. */ static bool _Boolean_canReplace(Type self, Type other) { TypeClass cls = Type_getClass(other); return Type_getClass(self) == cls || cls == s_booleanClass; } static jvalue _Boolean_coerceDatum(Type self, Datum arg) { jvalue result; result.l = JNI_newObject(s_Boolean_class, s_Boolean_init, DatumGetBool(arg)); return result; } static Datum _Boolean_coerceObject(Type self, jobject booleanObj) { return BoolGetDatum(booleanObj == 0 ? false : JNI_callBooleanMethod(booleanObj, s_Boolean_booleanValue) == JNI_TRUE); } static Type _boolean_createArrayType(Type self, Oid arrayTypeId) { return Array_fromOid2(arrayTypeId, self, _booleanArray_coerceDatum, _booleanArray_coerceObject); } /* Make this datatype available to the postgres system. */ extern void Boolean_initialize(void); void Boolean_initialize(void) { Type t_boolean; Type t_Boolean; TypeClass cls; s_Boolean_class = JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Boolean")); s_Boolean_init = PgObject_getJavaMethod(s_Boolean_class, "", "(Z)V"); s_Boolean_booleanValue = PgObject_getJavaMethod(s_Boolean_class, "booleanValue", "()Z"); cls = TypeClass_alloc("type.Boolean"); cls->canReplaceType = _Boolean_canReplace; cls->JNISignature = "Ljava/lang/Boolean;"; cls->javaTypeName = "java.lang.Boolean"; cls->coerceDatum = _Boolean_coerceDatum; cls->coerceObject = _Boolean_coerceObject; t_Boolean = TypeClass_allocInstance(cls, BOOLOID); cls = TypeClass_alloc("type.boolean"); cls->JNISignature = "Z"; cls->javaTypeName = "boolean"; cls->invoke = _boolean_invoke; cls->coerceDatum = _boolean_coerceDatum; cls->coerceObject = _Boolean_coerceObject; cls->createArrayType = _boolean_createArrayType; s_booleanClass = cls; t_boolean = TypeClass_allocInstance(cls, BOOLOID); t_boolean->objectType = t_Boolean; Type_registerType("boolean", t_boolean); Type_registerType("java.lang.Boolean", t_Boolean); } pljava-1.4.3/src/C/pljava/type/Relation.c0000644000014500000120000001443411634451404017261 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "org_postgresql_pljava_internal_Relation.h" #include "pljava/Exception.h" #include "pljava/Invocation.h" #include "pljava/SPI.h" #include "pljava/type/Type_priv.h" #include "pljava/type/String.h" #include "pljava/type/TupleDesc.h" #include "pljava/type/Tuple.h" #include "pljava/type/Relation.h" static jclass s_Relation_class; static jmethodID s_Relation_init; /* * org.postgresql.pljava.Relation type. */ jobject Relation_create(Relation td) { return (td == 0) ? 0 : JNI_newObject( s_Relation_class, s_Relation_init, Invocation_createLocalWrapper(td)); } extern void Relation_initialize(void); void Relation_initialize(void) { JNINativeMethod methods[] = { { "_free", "(J)V", Java_org_postgresql_pljava_internal_Relation__1free }, { "_getName", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Relation__1getName }, { "_getSchema", "(J)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Relation__1getSchema }, { "_getTupleDesc", "(J)Lorg/postgresql/pljava/internal/TupleDesc;", Java_org_postgresql_pljava_internal_Relation__1getTupleDesc }, { "_modifyTuple", "(JJ[I[Ljava/lang/Object;)Lorg/postgresql/pljava/internal/Tuple;", Java_org_postgresql_pljava_internal_Relation__1modifyTuple }, { 0, 0, 0 } }; s_Relation_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/Relation")); PgObject_registerNatives2(s_Relation_class, methods); s_Relation_init = PgObject_getJavaMethod(s_Relation_class, "", "(J)V"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_internal_Relation * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Relation__1free(JNIEnv* env, jobject _this, jlong pointer) { BEGIN_NATIVE_NO_ERRCHECK Invocation_freeLocalWrapper(pointer); END_NATIVE } /* * Class: org_postgresql_pljava_internal_Relation * Method: _getName * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Relation__1getName(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; Relation self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { char* relName = SPI_getrelname(self); result = String_createJavaStringFromNTS(relName); pfree(relName); } PG_CATCH(); { Exception_throw_ERROR("SPI_getrelname"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Relation * Method: _getSchema * Signature: (J)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_postgresql_pljava_internal_Relation__1getSchema(JNIEnv* env, jclass clazz, jlong _this) { jstring result = 0; Relation self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE PG_TRY(); { char* schema = SPI_getnspname(self); result = String_createJavaStringFromNTS(schema); pfree(schema); } PG_CATCH(); { Exception_throw_ERROR("SPI_getnspname"); } PG_END_TRY(); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Relation * Method: _getTupleDesc * Signature: (J)Lorg/postgresql/pljava/internal/TupleDesc; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Relation__1getTupleDesc(JNIEnv* env, jclass clazz, jlong _this) { jobject result = 0; Relation self = Invocation_getWrappedPointer(_this); if(self != 0) { BEGIN_NATIVE result = TupleDesc_create(self->rd_att); END_NATIVE } return result; } /* * Class: org_postgresql_pljava_internal_Relation * Method: _modifyTuple * Signature: (JJ[I[Ljava/lang/Object;)Lorg/postgresql/internal/pljava/Tuple; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_internal_Relation__1modifyTuple(JNIEnv* env, jclass clazz, jlong _this, jlong _tuple, jintArray _indexes, jobjectArray _values) { Relation self = Invocation_getWrappedPointer(_this); jobject result = 0; if(self != 0 && _tuple != 0) { Ptr2Long p2l; p2l.longVal = _tuple; BEGIN_NATIVE HeapTuple tuple = (HeapTuple)p2l.ptrVal; PG_TRY(); { jint idx; TupleDesc tupleDesc = self->rd_att; jobject typeMap = Invocation_getTypeMap(); jint count = JNI_getArrayLength(_indexes); Datum* values = (Datum*)palloc(count * sizeof(Datum)); char* nulls = 0; jint* javaIdxs = JNI_getIntArrayElements(_indexes, 0); int* indexes; if(sizeof(int) == sizeof(jint)) /* compiler will optimize this */ indexes = (int*)javaIdxs; else indexes = (int*)palloc(count * sizeof(int)); for(idx = 0; idx < count; ++idx) { int attIndex; Oid typeId; Type type; jobject value; if(sizeof(int) == sizeof(jint)) /* compiler will optimize this */ attIndex = indexes[idx]; else { attIndex = (int)javaIdxs[idx]; indexes[idx] = attIndex; } typeId = SPI_gettypeid(tupleDesc, attIndex); if(!OidIsValid(typeId)) { Exception_throw(ERRCODE_INVALID_DESCRIPTOR_INDEX, "Invalid attribute index \"%d\"", attIndex); return 0L; /* Exception */ } type = Type_fromOid(typeId, typeMap); value = JNI_getObjectArrayElement(_values, idx); if(value != 0) values[idx] = Type_coerceObject(type, value); else { if(nulls == 0) { nulls = (char*)palloc(count+1); memset(nulls, ' ', count); /* all values non-null initially */ nulls[count] = 0; } nulls[idx] = 'n'; values[idx] = 0; } } tuple = SPI_modifytuple(self, tuple, count, indexes, values, nulls); if(tuple == 0) Exception_throwSPI("modifytuple", SPI_result); JNI_releaseIntArrayElements(_indexes, javaIdxs, JNI_ABORT); if(sizeof(int) != sizeof(jint)) /* compiler will optimize this */ pfree(indexes); pfree(values); if(nulls != 0) pfree(nulls); } PG_CATCH(); { tuple = 0; Exception_throw_ERROR("SPI_gettypeid"); } PG_END_TRY(); if(tuple != 0) result = Tuple_create(tuple); END_NATIVE } return result; } pljava-1.4.3/src/C/pljava/SQLInputFromChunk.c0000644000014500000120000000502311634451404020011 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include "pljava/SQLInputFromChunk.h" #include "org_postgresql_pljava_jdbc_SQLInputFromChunk.h" static jclass s_SQLInputFromChunk_class; static jmethodID s_SQLInputFromChunk_init; static jmethodID s_SQLInputFromChunk_close; jobject SQLInputFromChunk_create(void* data, size_t sz) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = data; return JNI_newObject(s_SQLInputFromChunk_class, s_SQLInputFromChunk_init, p2l.longVal, (jint)sz); } void SQLInputFromChunk_close(jobject stream) { JNI_callVoidMethod(stream, s_SQLInputFromChunk_close); } /* Make this datatype available to the postgres system. */ extern void SQLInputFromChunk_initialize(void); void SQLInputFromChunk_initialize(void) { JNINativeMethod methods[] = { { "_readByte", "(JI)I", Java_org_postgresql_pljava_jdbc_SQLInputFromChunk__1readByte }, { "_readBytes", "(JI[BI)V", Java_org_postgresql_pljava_jdbc_SQLInputFromChunk__1readBytes }, { 0, 0, 0 }}; s_SQLInputFromChunk_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SQLInputFromChunk")); PgObject_registerNatives2(s_SQLInputFromChunk_class, methods); s_SQLInputFromChunk_init = PgObject_getJavaMethod(s_SQLInputFromChunk_class, "", "(JI)V"); s_SQLInputFromChunk_close = PgObject_getJavaMethod(s_SQLInputFromChunk_class, "close", "()V"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SQLInputFromChunk * Method: _readByte * Signature: (JI)I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_jdbc_SQLInputFromChunk__1readByte(JNIEnv* env, jclass cls, jlong _this, jint pos) { Ptr2Long p2l; p2l.longVal = _this; /* Bounds checking has already been made */ return (jint)((unsigned char*)p2l.ptrVal)[pos]; } /* * Class: org_postgresql_pljava_jdbc_SQLInputFromChunk * Method: _readBytes * Signature: (JI[BI)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SQLInputFromChunk__1readBytes(JNIEnv* env, jclass cls, jlong _this, jint pos, jbyteArray ba, jint len) { BEGIN_NATIVE Ptr2Long p2l; p2l.longVal = _this; /* Bounds checking has already been made */ JNI_setByteArrayRegion(ba, 0, len, ((jbyte*)p2l.ptrVal) + pos); END_NATIVE } pljava-1.4.3/src/C/pljava/Exception.c0000644000014500000120000001344211634451404016457 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "pljava/Backend.h" #include "pljava/Invocation.h" #include "pljava/Exception.h" #include "pljava/type/String.h" #include "pljava/type/ErrorData.h" jclass Class_class; jmethodID Class_getName; jclass ServerException_class; jmethodID ServerException_getErrorData; jmethodID ServerException_init; jclass Throwable_class; jmethodID Throwable_getMessage; jmethodID Throwable_printStackTrace; jclass IllegalArgumentException_class; jmethodID IllegalArgumentException_init; jclass SQLException_class; jmethodID SQLException_init; jmethodID SQLException_getSQLState; jclass UnsupportedOperationException_class; jmethodID UnsupportedOperationException_init; void Exception_featureNotSupported(const char* requestedFeature, const char* introVersion) { jstring jmsg; jobject ex; StringInfoData buf; initStringInfo(&buf); PG_TRY(); { appendStringInfoString(&buf, "Feature: "); appendStringInfoString(&buf, requestedFeature); appendStringInfoString(&buf, " lacks support in PostgreSQL version "); appendStringInfo(&buf, "%d.%d", PGSQL_MAJOR_VER, PGSQL_MINOR_VER); appendStringInfoString(&buf, ". It was introduced in version "); appendStringInfoString(&buf, introVersion); ereport(DEBUG3, (errmsg(buf.data))); jmsg = String_createJavaStringFromNTS(buf.data); ex = JNI_newObject(UnsupportedOperationException_class, UnsupportedOperationException_init, jmsg); JNI_deleteLocalRef(jmsg); JNI_throw(ex); } PG_CATCH(); { ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Exception while generating exception: %s", buf.data))); } PG_END_TRY(); pfree(buf.data); } void Exception_throw(int errCode, const char* errMessage, ...) { char buf[1024]; va_list args; jstring message; jstring sqlState; jobject ex; int idx; va_start(args, errMessage); vsnprintf(buf, sizeof(buf), errMessage, args); ereport(DEBUG3, (errcode(errCode), errmsg(buf))); PG_TRY(); { message = String_createJavaStringFromNTS(buf); /* unpack MAKE_SQLSTATE code */ for (idx = 0; idx < 5; ++idx) { buf[idx] = PGUNSIXBIT(errCode); errCode >>= 6; } buf[idx] = 0; sqlState = String_createJavaStringFromNTS(buf); ex = JNI_newObject(SQLException_class, SQLException_init, message, sqlState); JNI_deleteLocalRef(message); JNI_deleteLocalRef(sqlState); JNI_throw(ex); } PG_CATCH(); { ereport(WARNING, (errcode(errCode), errmsg("Exception while generating exception: %s", buf))); } PG_END_TRY(); va_end(args); } void Exception_throwIllegalArgument(const char* errMessage, ...) { char buf[1024]; va_list args; jstring message; jobject ex; va_start(args, errMessage); vsnprintf(buf, sizeof(buf), errMessage, args); ereport(DEBUG3, (errmsg(buf))); PG_TRY(); { message = String_createJavaStringFromNTS(buf); ex = JNI_newObject(IllegalArgumentException_class, IllegalArgumentException_init, message); JNI_deleteLocalRef(message); JNI_throw(ex); } PG_CATCH(); { ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Exception while generating exception: %s", buf))); } PG_END_TRY(); va_end(args); } void Exception_throwSPI(const char* function, int errCode) { Exception_throw(ERRCODE_INTERNAL_ERROR, "SPI function SPI_%s failed with error %s", function, SPI_result_code_string(errCode)); } void Exception_throw_ERROR(const char* funcName) { jobject ex; PG_TRY(); { jobject ed = ErrorData_getCurrentError(); FlushErrorState(); ex = JNI_newObject(ServerException_class, ServerException_init, ed); currentInvocation->errorOccured = true; elog(DEBUG1, "Exception in function %s", funcName); JNI_deleteLocalRef(ed); JNI_throw(ex); } PG_CATCH(); { elog(WARNING, "Exception while generating exception"); } PG_END_TRY(); } extern void Exception_initialize(void); void Exception_initialize(void) { Class_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Class")); Throwable_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Throwable")); Throwable_getMessage = PgObject_getJavaMethod(Throwable_class, "getMessage", "()Ljava/lang/String;"); Throwable_printStackTrace = PgObject_getJavaMethod(Throwable_class, "printStackTrace", "()V"); IllegalArgumentException_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/IllegalArgumentException")); IllegalArgumentException_init = PgObject_getJavaMethod(IllegalArgumentException_class, "", "(Ljava/lang/String;)V"); SQLException_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/sql/SQLException")); SQLException_init = PgObject_getJavaMethod(SQLException_class, "", "(Ljava/lang/String;Ljava/lang/String;)V"); SQLException_getSQLState = PgObject_getJavaMethod(SQLException_class, "getSQLState", "()Ljava/lang/String;"); UnsupportedOperationException_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/UnsupportedOperationException")); UnsupportedOperationException_init = PgObject_getJavaMethod(UnsupportedOperationException_class, "", "(Ljava/lang/String;)V"); Class_getName = PgObject_getJavaMethod(Class_class, "getName", "()Ljava/lang/String;"); } extern void Exception_initialize2(void); void Exception_initialize2(void) { ServerException_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/ServerException")); ServerException_init = PgObject_getJavaMethod(ServerException_class, "", "(Lorg/postgresql/pljava/internal/ErrorData;)V"); ServerException_getErrorData = PgObject_getJavaMethod(ServerException_class, "getErrorData", "()Lorg/postgresql/pljava/internal/ErrorData;"); } pljava-1.4.3/src/C/pljava/SQLInputFromTuple.c0000644000014500000120000000452411634451404020037 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include "pljava/type/HeapTupleHeader.h" #include "pljava/type/TupleDesc.h" #include "pljava/Invocation.h" #include "pljava/SQLInputFromTuple.h" #include "org_postgresql_pljava_jdbc_SQLInputFromTuple.h" static jclass s_SQLInputFromTuple_class; static jmethodID s_SQLInputFromTuple_init; jobject SQLInputFromTuple_create(HeapTupleHeader hth, TupleDesc td) { jobject tupleDesc; jobject result; jlong pointer; if(hth == 0) return 0; tupleDesc = TupleDesc_create(td); pointer = Invocation_createLocalWrapper(hth); result = JNI_newObject(s_SQLInputFromTuple_class, s_SQLInputFromTuple_init, pointer, tupleDesc); JNI_deleteLocalRef(tupleDesc); return result; } /* Make this datatype available to the postgres system. */ extern void SQLInputFromTuple_initialize(void); void SQLInputFromTuple_initialize(void) { JNINativeMethod methods[] = { { "_getObject", "(JJI)Ljava/lang/Object;", Java_org_postgresql_pljava_jdbc_SQLInputFromTuple__1getObject }, { "_free", "(J)V", Java_org_postgresql_pljava_jdbc_SQLInputFromTuple__1free }, { 0, 0, 0 } }; s_SQLInputFromTuple_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/jdbc/SQLInputFromTuple")); PgObject_registerNatives2(s_SQLInputFromTuple_class, methods); s_SQLInputFromTuple_init = PgObject_getJavaMethod(s_SQLInputFromTuple_class, "", "(JLorg/postgresql/pljava/internal/TupleDesc;)V"); } /**************************************** * JNI methods ****************************************/ /* * Class: org_postgresql_pljava_jdbc_SQLInputFromTuple * Method: _free * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_jdbc_SQLInputFromTuple__1free(JNIEnv* env, jobject _this, jlong hth) { HeapTupleHeader_free(env, hth); } /* * Class: org_postgresql_pljava_jdbc_SQLInputFromTuple * Method: _getObject * Signature: (JJI)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_org_postgresql_pljava_jdbc_SQLInputFromTuple__1getObject(JNIEnv* env, jclass clazz, jlong hth, jlong jtd, jint attrNo) { return HeapTupleHeader_getObject(env, hth, jtd, attrNo); } pljava-1.4.3/src/C/pljava/PgObject.c0000644000014500000120000001146711634451404016223 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #include "pljava/PgObject_priv.h" #include "pljava/type/String.h" static bool s_loopLock = false; static jclass s_Class_class = 0; static jmethodID s_Class_getName = 0; /* effectiveClassPath is set at initialization time (in Backend.c) */ const char* effectiveClassPath; void PgObject_free(PgObject object) { Finalizer finalizer = object->m_class->finalize; if(finalizer != 0) finalizer(object); pfree(object); } PgObject PgObjectClass_allocInstance(PgObjectClass clazz, MemoryContext ctx) { Size sz = clazz->instanceSize; PgObject infant = (PgObject)MemoryContextAlloc(ctx, sz); memset(infant, 0, sz); infant->m_class = clazz; return infant; } void PgObjectClass_init(PgObjectClass clazz, const char* name, Size instanceSize, Finalizer finalizer) { clazz->name = name; clazz->instanceSize = instanceSize; clazz->finalize = finalizer; } PgObjectClass PgObjectClass_create(const char* name, Size instanceSize, Finalizer finalizer) { PgObjectClass self = (PgObjectClass)MemoryContextAlloc(TopMemoryContext, sizeof(struct PgObjectClass_)); memset(self, 0, sizeof(struct PgObjectClass_)); PgObjectClass_init(self, name, instanceSize, finalizer); return self; } PgObjectClass PgObject_getClass(PgObject self) { return self->m_class; } const char* PgObjectClass_getName(PgObjectClass self) { return self->name; } void _PgObject_pureVirtualCalled(PgObject object) { ereport(ERROR, (errmsg("Pure virtual method called"))); } static char* PgObject_getClassName(jclass cls) { jstring jstr; char* tmp; if(s_Class_getName == 0) { if(s_loopLock) return ""; s_loopLock = true; s_Class_class = (jclass)JNI_newGlobalRef(PgObject_getJavaClass("java/lang/Class")); s_Class_getName = PgObject_getJavaMethod(s_Class_class, "getName", "()Ljava/lang/String;"); s_loopLock = false; } jstr = (jstring)JNI_callObjectMethod(cls, s_Class_getName); tmp = String_createNTS(jstr); JNI_deleteLocalRef(jstr); return tmp; } void PgObject_throwMemberError(jclass cls, const char* memberName, const char* signature, bool isMethod, bool isStatic) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errmsg("Unable to find%s %s %s.%s with signature %s", (isStatic ? " static" : ""), (isMethod ? "method" : "field"), PgObject_getClassName(cls), memberName, signature))); } jclass PgObject_getJavaClass(const char* className) { jclass cls = JNI_findClass(className); if(cls == 0) { if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); } ereport(ERROR, ( errmsg("Unable to load class %s using CLASSPATH '%s'", className, effectiveClassPath == 0 ? "null" : effectiveClassPath))); } return cls; } void PgObject_registerNatives(const char* className, JNINativeMethod* methods) { jclass cls = PgObject_getJavaClass(className); PgObject_registerNatives2(cls, methods); JNI_deleteLocalRef(cls); } void PgObject_registerNatives2(jclass cls, JNINativeMethod* methods) { #ifndef GCJ jint nMethods = 0; JNINativeMethod* m = methods; while(m->name != 0) { m++; nMethods++; } if(JNI_registerNatives(cls, methods, nMethods) != 0) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errmsg("Unable to register native methods"))); } #endif } jmethodID PgObject_getJavaMethod(jclass cls, const char* methodName, const char* signature) { jmethodID m = JNI_getMethodID(cls, methodName, signature); if(m == 0) PgObject_throwMemberError(cls, methodName, signature, true, false); return m; } jmethodID PgObject_getStaticJavaMethod(jclass cls, const char* methodName, const char* signature) { jmethodID m = JNI_getStaticMethodID(cls, methodName, signature); if(m == 0) PgObject_throwMemberError(cls, methodName, signature, true, true); return m; } jfieldID PgObject_getJavaField(jclass cls, const char* fieldName, const char* signature) { jfieldID m = JNI_getFieldID(cls, fieldName, signature); if(m == 0) PgObject_throwMemberError(cls, fieldName, signature, false, false); return m; } jfieldID PgObject_getStaticJavaField(jclass cls, const char* fieldName, const char* signature) { jfieldID m = JNI_getStaticFieldID(cls, fieldName, signature); if(m == 0) PgObject_throwMemberError(cls, fieldName, signature, false, true); return m; } HeapTuple PgObject_getValidTuple(int cacheId, Oid tupleId, const char* tupleType) { HeapTuple tuple = SearchSysCache(cacheId, ObjectIdGetDatum(tupleId), 0, 0, 0); if(!HeapTupleIsValid(tuple)) ereport(ERROR, (errmsg("cache lookup failed for %s %u", tupleType, tupleId))); return tuple; } pljava-1.4.3/src/C/pljava/XactListener.c0000644000014500000120000000566711634451404017140 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/Backend.h" #include "pljava/Exception.h" #include "org_postgresql_pljava_internal_XactListener.h" #if ((PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 1) || PGSQL_MAJOR_VER > 8) #define HAS_2PC 1 #include "access/xact.h" #endif static jclass s_XactListener_class; static jmethodID s_XactListener_onAbort; static jmethodID s_XactListener_onCommit; #if HAS_2PC static jmethodID s_XactListener_onPrepare; #endif static void xactCB(XactEvent event, void* arg) { Ptr2Long p2l; p2l.longVal = 0L; /* ensure that the rest is zeroed out */ p2l.ptrVal = arg; switch(event) { case XACT_EVENT_ABORT: JNI_callStaticVoidMethod(s_XactListener_class, s_XactListener_onAbort, p2l.longVal); break; case XACT_EVENT_COMMIT: JNI_callStaticVoidMethod(s_XactListener_class, s_XactListener_onCommit, p2l.longVal); break; #ifdef HAS_2PC case XACT_EVENT_PREPARE: JNI_callStaticVoidMethod(s_XactListener_class, s_XactListener_onPrepare, p2l.longVal); break; #endif } } extern void XactListener_initialize(void); void XactListener_initialize(void) { JNINativeMethod methods[] = { { "_register", "(J)V", Java_org_postgresql_pljava_internal_XactListener__1register }, { "_unregister", "(J)V", Java_org_postgresql_pljava_internal_XactListener__1unregister }, { 0, 0, 0 }}; PgObject_registerNatives("org/postgresql/pljava/internal/XactListener", methods); s_XactListener_class = JNI_newGlobalRef(PgObject_getJavaClass("org/postgresql/pljava/internal/XactListener")); s_XactListener_onAbort = PgObject_getStaticJavaMethod(s_XactListener_class, "onAbort", "(J)V"); s_XactListener_onCommit = PgObject_getStaticJavaMethod(s_XactListener_class, "onCommit", "(J)V"); #if HAS_2PC s_XactListener_onPrepare = PgObject_getStaticJavaMethod(s_XactListener_class, "onPrepare", "(J)V"); #endif } /* * Class: org_postgresql_pljava_internal_XactListener * Method: _register * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_XactListener__1register(JNIEnv* env, jclass cls, jlong listenerId) { BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; p2l.longVal = listenerId; RegisterXactCallback(xactCB, p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("RegisterXactCallback"); } PG_END_TRY(); END_NATIVE } /* * Class: org_postgresql_pljava_internal_XactListener * Method: _unregister * Signature: (J)V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_XactListener__1unregister(JNIEnv* env, jclass cls, jlong listenerId) { BEGIN_NATIVE PG_TRY(); { Ptr2Long p2l; p2l.longVal = listenerId; UnregisterXactCallback(xactCB, p2l.ptrVal); } PG_CATCH(); { Exception_throw_ERROR("UnregisterXactCallback"); } PG_END_TRY(); END_NATIVE } pljava-1.4.3/src/C/pljava/Iterator.c0000644000014500000120000000340511634451404016310 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/HashMap_priv.h" #include "pljava/Iterator.h" struct Iterator_ { struct PgObject_ PgObject_extension; HashMap source; uint32 sourceTableSize; uint32 currentBucket; Entry nextEntry; }; static PgObjectClass s_IteratorClass; Iterator Iterator_create(HashMap source) { Iterator self = (Iterator)PgObjectClass_allocInstance(s_IteratorClass, GetMemoryChunkContext(source)); self->source = source; self->sourceTableSize = source->tableSize; self->currentBucket = 0; self->nextEntry = 0; return self; } static Entry Iterator_peekNext(Iterator self) { uint32 tableSize = self->source->tableSize; if(tableSize != self->sourceTableSize) { /* Rehash during Iteration. We can't continue. */ self->nextEntry = 0; } else if(self->nextEntry == 0) { /* Go to next bucket */ Entry* table = self->source->table; while(self->currentBucket < tableSize) { Entry nxt = table[self->currentBucket]; if(nxt != 0) { self->nextEntry = nxt; break; } self->currentBucket++; } } return self->nextEntry; } bool Iterator_hasNext(Iterator self) { return Iterator_peekNext(self) != 0; } Entry Iterator_next(Iterator self) { Entry nxt = Iterator_peekNext(self); if(nxt != 0) { Entry nxtNxt = nxt->next; if(nxtNxt == 0) /* * Leave this bucket. */ self->currentBucket++; self->nextEntry = nxtNxt; } return nxt; } extern void Iterator_initialize(void); void Iterator_initialize(void) { s_IteratorClass = PgObjectClass_create("Iterator", sizeof(struct Iterator_), 0); } pljava-1.4.3/src/C/pljava/Backend.c~0000644000014500000120000005220611634451404016247 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "org_postgresql_pljava_internal_Backend.h" #include "pljava/Invocation.h" #include "pljava/Function.h" #include "pljava/HashMap.h" #include "pljava/Exception.h" #include "pljava/Backend.h" #include "pljava/Session.h" #include "pljava/SPI.h" #include "pljava/type/String.h" /* Example format: "/usr/local/pgsql/lib" */ #ifndef PKGLIBDIR #error "PKGLIBDIR needs to be defined to compile this file." #endif /* Include the 'magic block' that PostgreSQL 8.2 and up will use to ensure * that a module is not loaded into an incompatible server. */ #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif #define LOCAL_REFERENCE_COUNT 128 jlong mainThreadId; static JavaVM* s_javaVM = 0; static jclass s_Backend_class; static jmethodID s_setTrusted; static char* vmoptions; static char* classpath; static int statementCacheSize; static bool pljavaDebug; static bool pljavaReleaseLingeringSavepoints; static bool s_currentTrust; static int s_javaLogLevel; bool integerDateTimes = false; extern void Invocation_initialize(void); extern void Exception_initialize(void); extern void Exception_initialize2(void); extern void HashMap_initialize(void); extern void SPI_initialize(void); extern void Type_initialize(void); extern void Function_initialize(void); extern void Session_initialize(void); extern void PgSavepoint_initialize(void); extern void XactListener_initialize(void); extern void SubXactListener_initialize(void); extern void SQLInputFromChunk_initialize(void); extern void SQLOutputToChunk_initialize(void); extern void SQLInputFromTuple_initialize(void); extern void SQLOutputToTuple_initialize(void); static void initPLJavaClasses(void) { jfieldID tlField; JNINativeMethod backendMethods[] = { { "isCallingJava", "()Z", Java_org_postgresql_pljava_internal_Backend_isCallingJava }, { "isReleaseLingeringSavepoints", "()Z", Java_org_postgresql_pljava_internal_Backend_isReleaseLingeringSavepoints }, { "_getConfigOption", "(Ljava/lang/String;)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Backend__1getConfigOption }, { "_getStatementCacheSize", "()I", Java_org_postgresql_pljava_internal_Backend__1getStatementCacheSize }, { "_log", "(ILjava/lang/String;)V", Java_org_postgresql_pljava_internal_Backend__1log }, { "_clearFunctionCache", "()V", Java_org_postgresql_pljava_internal_Backend__1clearFunctionCache }, { 0, 0, 0 } }; Exception_initialize(); elog(DEBUG1, "Getting Backend class pljava.jar"); s_Backend_class = PgObject_getJavaClass("org/postgresql/pljava/internal/Backend"); elog(DEBUG1, "Backend class was there"); PgObject_registerNatives2(s_Backend_class, backendMethods); tlField = PgObject_getStaticJavaField(s_Backend_class, "THREADLOCK", "Ljava/lang/Object;"); JNI_setThreadLock(JNI_getStaticObjectField(s_Backend_class, tlField)); Invocation_initialize(); Exception_initialize2(); SPI_initialize(); Type_initialize(); Function_initialize(); Session_initialize(); PgSavepoint_initialize(); XactListener_initialize(); SubXactListener_initialize(); SQLInputFromChunk_initialize(); SQLOutputToChunk_initialize(); SQLInputFromTuple_initialize(); SQLOutputToTuple_initialize(); s_setTrusted = PgObject_getStaticJavaMethod(s_Backend_class, "setTrusted", "(Z)V"); } /** * Initialize security */ void Backend_setJavaSecurity(bool trusted) { if(trusted != s_currentTrust) { /* GCJ has major issues here. Real work on SecurityManager and * related classes has just started in version 4.0.0. */ #ifndef GCJ JNI_callStaticVoidMethod(s_Backend_class, s_setTrusted, (jboolean)trusted); if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize java security"))); } #endif s_currentTrust = trusted; } } int Backend_setJavaLogLevel(int logLevel) { int oldLevel = s_javaLogLevel; s_javaLogLevel = logLevel; return oldLevel; } /** * Special purpose logging function called from JNI when verbose is enabled. */ static jint JNICALL my_vfprintf(FILE* fp, const char* format, va_list args) { char buf[1024]; char* ep; char* bp = buf; vsnprintf(buf, sizeof(buf), format, args); /* Trim off trailing newline and other whitespace. */ ep = bp + strlen(bp) - 1; while(ep >= bp && isspace(*ep)) --ep; ++ep; *ep = 0; elog(s_javaLogLevel, buf); return 0; } /* * Append those parts of path that has not yet been appended. The HashMap unique is * keeping track of what has been appended already. First appended part will be * prefixed with prefix. */ static void appendPathParts(const char* path, StringInfoData* bld, HashMap unique, const char* prefix) { StringInfoData buf; if(path == 0 || strlen(path) == 0) return; for (;;) { char* pathPart; size_t len; if(*path == 0) break; len = strcspn(path, ";:"); if(len == 1 && *(path+1) == ':' && isalnum(*path)) /* * Windows drive designator, leave it "as is". */ len = strcspn(path+2, ";:") + 2; else if(len == 0) { /* Ignore zero length components. */ ++path; continue; } initStringInfo(&buf); if(*path == '$') { if(len == 7 || (strcspn(path, "/\\") == 7 && strncmp(path, "$libdir", 7) == 0)) { len -= 7; path += 7; appendStringInfo(&buf, PKGLIBDIR); } else ereport(ERROR, ( errcode(ERRCODE_INVALID_NAME), errmsg("invalid macro name '%*s' in dynamic library path", (int)len, path))); } if(len > 0) { appendBinaryStringInfo(&buf, path, len); path += len; } pathPart = buf.data; if(HashMap_getByString(unique, pathPart) == 0) { if(HashMap_size(unique) == 0) appendStringInfo(bld, prefix); else #if defined(WIN32) appendStringInfoChar(bld, ';'); #else appendStringInfoChar(bld, ':'); #endif appendStringInfo(bld, pathPart); HashMap_putByString(unique, pathPart, (void*)1); } pfree(pathPart); if(*path == 0) break; ++path; /* Skip ':' */ } } /* * Get the CLASSPATH. Result is always freshly palloc'd. */ static char* getClassPath(const char* prefix) { char* path; HashMap unique = HashMap_create(13, CurrentMemoryContext); StringInfoData buf; initStringInfo(&buf); appendPathParts(classpath, &buf, unique, prefix); appendPathParts(getenv("CLASSPATH"), &buf, unique, prefix); PgObject_free((PgObject)unique); path = buf.data; if(strlen(path) == 0) { pfree(path); path = 0; } return path; } #if !defined(WIN32) static void pljavaStatementCancelHandler(int signum) { if(!proc_exit_inprogress) { /* Never service the interrupt immediately. In order to find out if * its safe, we would need to know what kind of threading mechanism * the VM uses. That would count for a lot of conditional code. */ QueryCancelPending = true; InterruptPending = true; } } static void pljavaDieHandler(int signum) { if(!proc_exit_inprogress) { /* Never service the interrupt immediately. In order to find out if * its safe, we would need to know what kind of threading mechanism * the VM uses. That would count for a lot of conditional code. */ ProcDiePending = true; InterruptPending = true; } } static void pljavaQuickDieHandler(int signum) { /* Just die. No ereporting here since we don't know what thread this is. */ exit(1); } static sigjmp_buf recoverBuf; static void terminationTimeoutHandler(int signum) { kill(MyProcPid, SIGQUIT); /* Some sleep to get the SIGQUIT a chance to generate * the needed output. */ pg_usleep(1); /* JavaVM did not die within the alloted time */ siglongjmp(recoverBuf, 1); } #endif /* * proc_exit callback to tear down the JVM */ static void _destroyJavaVM(int status, Datum dummy) { if(s_javaVM != 0) { Invocation ctx; #if !defined(WIN32) pqsigfunc saveSigAlrm; Invocation_pushInvocation(&ctx, false); if(sigsetjmp(recoverBuf, 1) != 0) { elog(DEBUG1, "JavaVM destroyed with force"); s_javaVM = 0; return; } saveSigAlrm = pqsignal(SIGALRM, terminationTimeoutHandler); enable_sig_alarm(5000, false); elog(DEBUG1, "Destroying JavaVM..."); JNI_destroyVM(s_javaVM); disable_sig_alarm(false); pqsignal(SIGALRM, saveSigAlrm); #else Invocation_pushInvocation(&ctx, false); elog(DEBUG1, "Destroying JavaVM..."); JNI_destroyVM(s_javaVM); #endif elog(DEBUG1, "JavaVM destroyed"); s_javaVM = 0; currentInvocation = 0; } } typedef struct { JavaVMOption* options; unsigned int size; unsigned int capacity; } JVMOptList; static void JVMOptList_init(JVMOptList* jol) { jol->options = (JavaVMOption*)palloc(10 * sizeof(JavaVMOption)); jol->size = 0; jol->capacity = 10; } static void JVMOptList_delete(JVMOptList* jol) { JavaVMOption* opt = jol->options; JavaVMOption* top = opt + jol->size; while(opt < top) { pfree(opt->optionString); opt++; } pfree(jol->options); } static void JVMOptList_add(JVMOptList* jol, const char* optString, void* extraInfo, bool mustCopy) { JavaVMOption* added; int newPos = jol->size; if(newPos >= jol->capacity) { int newCap = jol->capacity * 2; JavaVMOption* newOpts = (JavaVMOption*)palloc(newCap * sizeof(JavaVMOption)); memcpy(newOpts, jol->options, newPos * sizeof(JavaVMOption)); pfree(jol->options); jol->options = newOpts; jol->capacity = newCap; } added = jol->options + newPos; if(mustCopy) optString = pstrdup(optString); added->optionString = (char*)optString; added->extraInfo = extraInfo; jol->size++; elog(DEBUG1, "Added JVM option string \"%s\"", optString); } /* Split JVM options. The string is split on whitespace unless the * whitespace is found within a string or is escaped by backslash. A * backslash escaped quote is not considered a string delimiter. */ static void addUserJVMOptions(JVMOptList* optList) { const char* cp = vmoptions; if(cp != NULL) { StringInfoData buf; char quote = 0; char c; initStringInfo(&buf); for(;;) { c = *cp++; switch(c) { case 0: break; case '"': case '\'': if(quote == c) quote = 0; else quote = c; appendStringInfoChar(&buf, c); continue; case '\\': appendStringInfoChar(&buf, '\\'); c = *cp++; /* Interpret next character verbatim */ if(c == 0) break; appendStringInfoChar(&buf, c); continue; default: if(quote == 0 && isspace((int)c)) { while((c = *cp++) != 0) { if(!isspace((int)c)) break; } if(c == 0) break; if(c != '-') appendStringInfoChar(&buf, ' '); else if(buf.len > 0) { /* Whitespace followed by '-' triggers new * option declaration. */ JVMOptList_add(optList, buf.data, 0, true); buf.len = 0; buf.data[0] = 0; } } appendStringInfoChar(&buf, c); continue; } break; } if(buf.len > 0) JVMOptList_add(optList, buf.data, 0, true); pfree(buf.data); } } /** * Initialize the session */ static void initJavaSession(void) { jclass sessionClass = PgObject_getJavaClass("org/postgresql/pljava/internal/Session"); jmethodID init = PgObject_getStaticJavaMethod(sessionClass, "init", "()J"); mainThreadId = JNI_callStaticLongMethod(sessionClass, init); JNI_deleteLocalRef(sessionClass); if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize java session"))); } } static void checkIntTimeType(void) { #if (PGSQL_MAJOR_VER > 8) const char* idt = GetConfigOption("integer_datetimes", true); #else const char* idt = GetConfigOption("integer_datetimes"); #endif integerDateTimes = (strcmp(idt, "on") == 0); elog(DEBUG1, integerDateTimes ? "Using integer_datetimes" : "Not using integer_datetimes"); } static bool s_firstTimeInit = true; static void initializeJavaVM(void) { jboolean jstat; JavaVMInitArgs vm_args; JVMOptList optList; JVMOptList_init(&optList); if(s_firstTimeInit) { s_firstTimeInit = false; s_javaLogLevel = INFO; checkIntTimeType(); HashMap_initialize(); DefineCustomStringVariable( "pljava.vmoptions", "Options sent to the JVM when it is created", NULL, &vmoptions, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) NULL, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomStringVariable( "pljava.classpath", "Classpath used by the JVM", NULL, &classpath, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) NULL, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomBoolVariable( "pljava.debug", "Stop the backend to attach a debugger", NULL, &pljavaDebug, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) false, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomIntVariable( "pljava.statement_cache_size", "Size of the prepared statement MRU cache", NULL, &statementCacheSize, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 11, #endif 0, 512, PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomBoolVariable( "pljava.release_lingering_savepoints", "If true, lingering savepoints will be released on function exit. If false, the will be rolled back", NULL, &pljavaReleaseLingeringSavepoints, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) false, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); EmitWarningsOnPlaceholders("pljava"); s_firstTimeInit = false; } #ifdef PLJAVA_DEBUG /* Hard setting for debug. Don't forget to recompile... */ pljavaDebug = 1; #endif addUserJVMOptions(&optList); effectiveClassPath = getClassPath("-Djava.class.path="); if(effectiveClassPath != 0) { JVMOptList_add(&optList, effectiveClassPath, 0, true); } /** * As stipulated by JRT-2003 */ JVMOptList_add(&optList, "-Dsqlj.defaultconnection=jdbc:default:connection", 0, true); JVMOptList_add(&optList, "vfprintf", (void*)my_vfprintf, true); #ifndef GCJ JVMOptList_add(&optList, "-Xrs", 0, true); #endif if(pljavaDebug) { elog(INFO, "Backend pid = %d. Attach the debugger and set pljavaDebug to false to continue", getpid()); while(pljavaDebug) pg_usleep(1000000L); } vm_args.nOptions = optList.size; vm_args.options = optList.options; vm_args.version = JNI_VERSION_1_4; vm_args.ignoreUnrecognized = JNI_FALSE; elog(DEBUG1, "Creating JavaVM"); jstat = JNI_createVM(&s_javaVM, &vm_args); if(jstat == JNI_OK && JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); jstat = JNI_ERR; } JVMOptList_delete(&optList); if(jstat != JNI_OK) ereport(ERROR, (errmsg("Failed to create Java VM"))); #if !defined(WIN32) pqsignal(SIGINT, pljavaStatementCancelHandler); pqsignal(SIGTERM, pljavaDieHandler); pqsignal(SIGQUIT, pljavaQuickDieHandler); #endif elog(DEBUG1, "JavaVM created"); /* Register an on_proc_exit handler that destroys the VM */ on_proc_exit(_destroyJavaVM, 0); initPLJavaClasses(); initJavaSession(); } static Datum internalCallHandler(bool trusted, PG_FUNCTION_ARGS); extern Datum javau_call_handler(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(javau_call_handler); /* * This is the entry point for all untrusted calls. */ Datum javau_call_handler(PG_FUNCTION_ARGS) { return internalCallHandler(false, fcinfo); } extern Datum java_call_handler(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(java_call_handler); /* * This is the entry point for all trusted calls. */ Datum java_call_handler(PG_FUNCTION_ARGS) { return internalCallHandler(true, fcinfo); } static Datum internalCallHandler(bool trusted, PG_FUNCTION_ARGS) { Invocation ctx; Datum retval = 0; if(s_javaVM == 0) { Invocation_pushBootContext(&ctx); PG_TRY(); { initializeJavaVM(); Invocation_popBootContext(); } PG_CATCH(); { Invocation_popBootContext(); /* JVM initialization failed for some reason. Destroy * the VM if it exists. Perhaps the user will try * fixing the pljava.classpath and make a new attempt. */ _destroyJavaVM(0, 0); /* We can't stay here... */ PG_RE_THROW(); } PG_END_TRY(); /* Force initial setting */ s_currentTrust = !trusted; } Invocation_pushInvocation(&ctx, trusted); PG_TRY(); { Function function = Function_getFunction(fcinfo); if(CALLED_AS_TRIGGER(fcinfo)) { /* Called as a trigger procedure */ retval = Function_invokeTrigger(function, fcinfo); } else { /* Called as a function */ retval = Function_invoke(function, fcinfo); } Invocation_popInvocation(false); } PG_CATCH(); { Invocation_popInvocation(true); PG_RE_THROW(); } PG_END_TRY(); return retval; } /**************************************** * JNI methods ****************************************/ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_VERSION_1_4; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _getConfigOption * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL JNICALL Java_org_postgresql_pljava_internal_Backend__1getConfigOption(JNIEnv* env, jclass cls, jstring jkey) { jstring result = 0; BEGIN_NATIVE char* key = String_createNTS(jkey); if(key != 0) { PG_TRY(); { #if (PGSQL_MAJOR_VER > 8) const char* value = GetConfigOption(key, true); #else const char* value = GetConfigOption(key); #endif pfree(key); if(value != 0) result = String_createJavaStringFromNTS(value); } PG_CATCH(); { Exception_throw_ERROR("GetConfigOption"); } PG_END_TRY(); } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _getStatementCacheSize * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Backend__1getStatementCacheSize(JNIEnv* env, jclass cls) { return statementCacheSize; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _log * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL JNICALL Java_org_postgresql_pljava_internal_Backend__1log(JNIEnv* env, jclass cls, jint logLevel, jstring jstr) { BEGIN_NATIVE_NO_ERRCHECK char* str = String_createNTS(jstr); if(str != 0) { /* elog uses printf formatting but the logger does not so we must escape all * '%' in the string. */ char c; const char* cp; int percentCount = 0; for(cp = str; (c = *cp) != 0; ++cp) { if(c == '%') ++percentCount; } if(percentCount > 0) { /* Make room to expand all "%" to "%%" */ char* str2 = palloc((cp - str) + percentCount + 1); char* cp2 = str2; /* Expand... */ for(cp = str; (c = *cp) != 0; ++cp) { if(c == '%') *cp2++ = c; *cp2++ = c; } *cp2 = 0; pfree(str); str = str2; } PG_TRY(); { elog(logLevel, str); pfree(str); } PG_CATCH(); { Exception_throw_ERROR("ereport"); } PG_END_TRY(); } END_NATIVE } /* * Class: org_postgresql_pljava_internal_Backend * Method: isCallingJava * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Backend_isCallingJava(JNIEnv* env, jclass cls) { return JNI_isCallingJava(); } /* * Class: org_postgresql_pljava_internal_Backend * Method: isReleaseLingeringSavepoints * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Backend_isReleaseLingeringSavepoints(JNIEnv* env, jclass cls) { return pljavaReleaseLingeringSavepoints ? JNI_TRUE : JNI_FALSE; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _clearFunctionCache * Signature: ()V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Backend__1clearFunctionCache(JNIEnv* env, jclass cls) { BEGIN_NATIVE_NO_ERRCHECK Function_clearFunctionCache(); END_NATIVE } pljava-1.4.3/src/C/pljava/Makefile~0000644000014500000120000001260211634451404016210 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- MODULE_big := pljava SRCDIR := $(MODULEROOT)/$(MODULE_big) INCLDIR := -I$(MODULEROOT)/include -I$(JNIDIR) mkobjs = $(subst $(SRCDIR)/,,$(1:%.c=%.o)) SRCS = $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.c -print) OBJS = $(call mkobjs,$(SRCS)) DLLTOOL_DEFFLAGS := --add-stdcall-alias .PHONY: checkjavahome build pljava-plugin $(OBJS): %.o : $(SRCDIR)/%.c @-mkdir -p $(@D) $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ # Must include PGXS at this point since it defines PORTNAME and VERSION # include $(PGXS) SS_VERSION := $(subst ., ,$(subst devel,.99,$(subst beta,.99,$(subst alpha,.99,$(subst rc,.99,$(subst RC,.99,$(VERSION))))))) PGSQL_MAJOR_VER = $(word 1,$(SS_VERSION)) PGSQL_MINOR_VER = $(word 2,$(SS_VERSION)) PGSQL_PATCH_VER = $(word 3,$(SS_VERSION)) PLJAVA_CPPFLAGS = \ -DPKGLIBDIR=\"$(pkglibdir)\" $(INCLDIR) \ -DPGSQL_MAJOR_VER=$(PGSQL_MAJOR_VER) \ -DPGSQL_MINOR_VER=$(PGSQL_MINOR_VER) \ -DPGSQL_PATCH_VER=$(PGSQL_PATCH_VER) ifeq ($(PORTNAME), win32) DLL_BUILD := 1 else ifeq ($(PORTNAME), darwin) # # override with JAVAVM_FWX_ROOT=/... on command line # if something else is needed # JAVAVM_FWX_ROOT := /System/Library/Frameworks PLJAVA_CPPFLAGS += -I$(JAVAVM_FWX_ROOT)/JavaVM.framework/Headers/ else ifeq ($(PORTNAME), ibmos2) DLL_BUILD := 1 else JRE_INCL := $(PORTNAME) ifeq ($(host_cpu), i686) JRE_CPU := i386 else ifeq ($(host_cpu), i586) JRE_CPU := i386 else ifeq ($(host_cpu), i486) JRE_CPU := i386 else ifeq ($(host_cpu), x86_64) JRE_CPU := amd64 else JRE_CPU := $(host_cpu) endif endif endif endif ifeq ($(JRE_CPU), i386) ifeq ($(findstring -m64, $(CFLAGS)), -m64) JRE_CPU := amd64 endif endif endif endif endif ifdef USE_GCJ PLJAVA_CPPFLAGS += -DGCJ SHLIB_LINK = -lgcj ifdef DLL_BUILD SHLIB_LINK += -lws2_32 endif OBJS += pljava_jar.o else pljava-jar = pljava.jar ifeq ($(PORTNAME), darwin) SHLIB_LINK += -L. -framework JavaVM else ifdef JAVA_HOME ifeq ($(PORTNAME), win32) JDK_HOME := $(subst \,/,$(JAVA_HOME)) else JDK_HOME := $(JAVA_HOME) endif else ifeq ($(PORTNAME), ibmos2) JAVA_HOME_TEST_RESULT := $(echo "You must set JAVA_HOME") else JAVA_HOME_TEST_RESULT := $(error You must set JAVA_HOME) endif endif ifdef DLL_BUILD ifeq ($(PORTNAME),ibmos2) JRE_INCL := os2 else JRE_INCL := win32 endif JVM_LIB := $(JDK_HOME)/jre/bin/client else JRE_LIB := $(JDK_HOME)/jre/lib/$(JRE_CPU) JVM_LIB := $(firstword $(shell /bin/ls -d \ $(JRE_LIB)/client \ $(JRE_LIB)/server \ $(JRE_LIB)/jrockit \ 2> /dev/null)) endif PLJAVA_CPPFLAGS += -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/$(JRE_INCL)" SHLIB_LINK += -L. -L"$(JVM_LIB)" -ljvm ifdef USE_LD_RPATH ifeq (${USE_LD_RPATH},1) SHLIB_LINK += -rpath ${JVM_LIB} else ifeq (${USE_LD_RPATH},2) SHLIB_LINK += -R${JVM_LIB} else ifeq (${USE_LD_RPATH},3) SHLIB_LINK += -R ${JVM_LIB} else ifeq (${USE_LD_RPATH},4) SHLIB_LINK += -Wl,-R${JVM_LIB} else ifeq (${USE_LD_RPATH},5) SHLIB_LINK += -Wl,-R,${JVM_LIB} endif endif endif endif endif endif endif endif ifeq ($(JRE_INCL), win32) # # Mingw is a bit special in that we use a "normal" windows # port of the Java Runtime Environment (unless we use gcj # of course). The headers etc. for the JRE is windows style # and contains __int64. GNU compiler doesn't know __int64, # instead it uses long long. # ifndef USE_GCJ PLJAVA_CPPFLAGS += -D__int64='long long' # Don't use higher standard then c89 since this rules out older compilers. We # must use 'long long' however, since JNI defines it. # PLJAVA_CFLAGS += -Wno-long-long endif endif override CPPFLAGS += $(PLJAVA_CPPFLAGS) override CFLAGS += $(PLJAVA_CFLAGS) # shlib naming convention for plugins (no 'lib') # plugin = $(NAME)$(DLSUFFIX) soname = $(plugin) shlib = $(plugin) ifndef USE_GCJ checkjavahome: $(JAVA_HOME_TEST_RESULT) endif ifneq ($(PORTNAME), win32) ifneq ($(PORTNAME), aix) # Normal case # $(plugin): $(OBJS) $(LINK.shared) $(LDFLAGS_SL) $(OBJS) $(SHLIB_LINK) -o $(plugin) else # PORTNAME == aix # AIX case # $(plugin): lib$(NAME).a $(MKLDEXPORT) lib$(NAME).a > $(NAME)$(EXPSUFF) $(COMPILER) $(LDFLAGS_NO_L) $(LDFLAGS_SL) -o $(plugin) $< -Wl,-bE:$(NAME)$(EXPSUFF) $(SHLIB_LINK) endif # PORTNAME == aix else # PORTNAME == win32 # win32 case # $(plugin): $(OBJS) ifndef USE_GCJ $(DLLTOOL) --input-def $(SRCDIR)/jvm.def --kill-at --dllname jvm.dll --output-lib libjvm.dll.a endif $(DLLTOOL) --export-all $(DLLTOOL_DEFFLAGS) --output-def $(NAME).def $(OBJS) $(DLLWRAP) $(LDFLAGS_SL) -o $(plugin) --dllname $(plugin) $(DLLWRAP_FLAGS) --def $(NAME).def $(OBJS) $(SHLIB_LINK) $(DLLTOOL) --dllname $(plugin) $(DLLTOOL_LIBFLAGS) --def $(NAME).def endif # PORTNAME == win32 build_all: $(plugin) build_install: build_all installdirs $(INSTALL_SHLIB) $(plugin) $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ $(INSTALL_DATA) ../$(pljava-jar) $(DESTDIR)$(pkglibdir) endif build_uninstall: rm -f $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ rm -f $(DESTDIR)$(pkglibdir)/$(pljava-jar) endif pljava-1.4.3/src/C/pljava/.#Makefile.1.420000644000014500000120000001204211634451404016514 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT # found in the root folder of this project or at # http://eng.tada.se/osprojects/COPYRIGHT.html # # @author Thomas Hallgren #------------------------------------------------------------------------- MODULE_big := pljava SRCDIR := $(MODULEROOT)/$(MODULE_big) INCLDIR := -I$(MODULEROOT)/include -I$(JNIDIR) mkobjs = $(subst $(SRCDIR)/,,$(1:%.c=%.o)) SRCS = $(shell find $(SRCDIR) -name CVS -prune -o -type f -name \*.c -print) OBJS = $(call mkobjs,$(SRCS)) DLLTOOL_DEFFLAGS := --add-stdcall-alias .PHONY: checkjavahome build pljava-plugin $(OBJS): %.o : $(SRCDIR)/%.c @-mkdir -p $(@D) $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ # Must include PGXS at this point since it defines PORTNAME and VERSION # include $(PGXS) SS_VERSION := $(subst ., ,$(subst devel,.99,$(subst beta,.99,$(subst alpha,.99,$(subst rc,.99,$(subst RC,.99,$(VERSION))))))) PGSQL_MAJOR_VER = $(word 1,$(SS_VERSION)) PGSQL_MINOR_VER = $(word 2,$(SS_VERSION)) PGSQL_PATCH_VER = $(word 3,$(SS_VERSION)) PLJAVA_CPPFLAGS = \ -DPKGLIBDIR=\"$(pkglibdir)\" $(INCLDIR) \ -DPGSQL_MAJOR_VER=$(PGSQL_MAJOR_VER) \ -DPGSQL_MINOR_VER=$(PGSQL_MINOR_VER) \ -DPGSQL_PATCH_VER=$(PGSQL_PATCH_VER) ifeq ($(PORTNAME), win32) DLL_BUILD := 1 else ifeq ($(PORTNAME), darwin) # # override with JAVAVM_FWX_ROOT=/... on command line # if something else is needed # JAVAVM_FWX_ROOT := /System/Library/Frameworks PLJAVA_CPPFLAGS += -I$(JAVAVM_FWX_ROOT)/JavaVM.framework/Headers/ else ifeq ($(PORTNAME), ibmos2) DLL_BUILD := 1 else JRE_INCL := $(PORTNAME) ifeq ($(host_cpu), i686) JRE_CPU := i386 else ifeq ($(host_cpu), i586) JRE_CPU := i386 else ifeq ($(host_cpu), i486) JRE_CPU := i386 else ifeq ($(host_cpu), x86_64) JRE_CPU := amd64 else JRE_CPU := $(host_cpu) endif endif endif endif ifeq ($(JRE_CPU), i386) ifeq ($(findstring -m64, $(CFLAGS)), -m64) JRE_CPU := amd64 endif endif endif endif endif ifdef USE_GCJ PLJAVA_CPPFLAGS += -DGCJ SHLIB_LINK = -lgcj ifdef DLL_BUILD SHLIB_LINK += -lws2_32 endif OBJS += pljava_jar.o else pljava-jar = pljava.jar ifeq ($(PORTNAME), darwin) SHLIB_LINK += -L. -framework JavaVM else ifdef JAVA_HOME ifeq ($(PORTNAME), win32) JDK_HOME := $(subst \,/,$(JAVA_HOME)) else JDK_HOME := $(JAVA_HOME) endif else ifeq ($(PORTNAME), ibmos2) JAVA_HOME_TEST_RESULT := $(echo "You must set JAVA_HOME") else JAVA_HOME_TEST_RESULT := $(error You must set JAVA_HOME) endif endif ifdef DLL_BUILD ifeq ($(PORTNAME),ibmos2) JRE_INCL := os2 else JRE_INCL := win32 endif JVM_LIB := $(JDK_HOME)/jre/bin/client else JRE_LIB := $(JDK_HOME)/jre/lib/$(JRE_CPU) JVM_LIB := $(firstword $(shell /bin/ls -d \ $(JRE_LIB)/client \ $(JRE_LIB)/server \ $(JRE_LIB)/jrockit \ 2> /dev/null)) endif PLJAVA_CPPFLAGS += -I"$(JDK_HOME)/include" -I"$(JDK_HOME)/include/$(JRE_INCL)" SHLIB_LINK += -L. -L"$(JVM_LIB)" -ljvm ifdef USE_LD_RPATH SHLIB_LINK += -Wl,-R"$(JVM_LIB)" endif endif endif ifeq ($(JRE_INCL), win32) # # Mingw is a bit special in that we use a "normal" windows # port of the Java Runtime Environment (unless we use gcj # of course). The headers etc. for the JRE is windows style # and contains __int64. GNU compiler doesn't know __int64, # instead it uses long long. # ifndef USE_GCJ PLJAVA_CPPFLAGS += -D__int64='long long' # Don't use higher standard then c89 since this rules out older compilers. We # must use 'long long' however, since JNI defines it. # PLJAVA_CFLAGS += -Wno-long-long endif endif override CPPFLAGS += $(PLJAVA_CPPFLAGS) override CFLAGS += $(PLJAVA_CFLAGS) # shlib naming convention for plugins (no 'lib') # plugin = $(NAME)$(DLSUFFIX) soname = $(plugin) shlib = $(plugin) ifndef USE_GCJ checkjavahome: $(JAVA_HOME_TEST_RESULT) endif ifneq ($(PORTNAME), win32) ifneq ($(PORTNAME), aix) # Normal case # $(plugin): $(OBJS) $(LINK.shared) $(LDFLAGS_SL) $(OBJS) $(SHLIB_LINK) -o $(plugin) else # PORTNAME == aix # AIX case # $(plugin): lib$(NAME).a $(MKLDEXPORT) lib$(NAME).a > $(NAME)$(EXPSUFF) $(COMPILER) $(LDFLAGS_NO_L) $(LDFLAGS_SL) -o $(plugin) $< -Wl,-bE:$(NAME)$(EXPSUFF) $(SHLIB_LINK) endif # PORTNAME == aix else # PORTNAME == win32 # win32 case # $(plugin): $(OBJS) ifndef USE_GCJ $(DLLTOOL) --input-def $(SRCDIR)/jvm.def --kill-at --dllname jvm.dll --output-lib libjvm.dll.a endif $(DLLTOOL) --export-all $(DLLTOOL_DEFFLAGS) --output-def $(NAME).def $(OBJS) $(DLLWRAP) $(LDFLAGS_SL) -o $(plugin) --dllname $(plugin) $(DLLWRAP_FLAGS) --def $(NAME).def $(OBJS) $(SHLIB_LINK) $(DLLTOOL) --dllname $(plugin) $(DLLTOOL_LIBFLAGS) --def $(NAME).def endif # PORTNAME == win32 build_all: $(plugin) build_install: build_all installdirs $(INSTALL_SHLIB) $(plugin) $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ $(INSTALL_DATA) ../$(pljava-jar) $(DESTDIR)$(pkglibdir) endif build_uninstall: rm -f $(DESTDIR)$(pkglibdir)/$(plugin) ifndef USE_GCJ rm -f $(DESTDIR)$(pkglibdir)/$(pljava-jar) endif pljava-1.4.3/src/C/pljava/backports.c0000644000014500000120000001447211634451404016515 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include "pljava/backports.h" #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) #include #include #include #include #include #include #include static TypeFuncClass internal_get_result_type(Oid funcid, Node *call_expr, ReturnSetInfo *rsinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc); static Oid get_call_expr_argtype(Node *expr, int argnum) { List *args; Oid argtype; if (expr == NULL) return InvalidOid; if (IsA(expr, FuncExpr)) args = ((FuncExpr *) expr)->args; else if (IsA(expr, OpExpr)) args = ((OpExpr *) expr)->args; else if (IsA(expr, DistinctExpr)) args = ((DistinctExpr *) expr)->args; else if (IsA(expr, ScalarArrayOpExpr)) args = ((ScalarArrayOpExpr *) expr)->args; else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; else return InvalidOid; if (argnum < 0 || argnum >= list_length(args)) return InvalidOid; argtype = exprType((Node *) list_nth(args, argnum)); /* * special hack for ScalarArrayOpExpr: what the underlying function * will actually get passed is the element type of the array. */ if (IsA(expr, ScalarArrayOpExpr) && argnum == 1) argtype = get_element_type(argtype); return argtype; } /* * get_call_result_type * Given a function's call info record, determine the kind of datatype * it is supposed to return. If resultTypeId isn't NULL, *resultTypeId * receives the actual datatype OID (this is mainly useful for scalar * result types). If resultTupleDesc isn't NULL, *resultTupleDesc * receives a pointer to a TupleDesc when the result is of a composite * type, or NULL when it's a scalar result. NB: the tupledesc should * be copied if it is to be accessed over a long period. * * One hard case that this handles is resolution of actual rowtypes for * functions returning RECORD (from either the function's OUT parameter * list, or a ReturnSetInfo context node). TYPEFUNC_RECORD is returned * only when we couldn't resolve the actual rowtype for lack of information. * * The other hard case that this handles is resolution of polymorphism. * We will never return ANYELEMENT or ANYARRAY, either as a scalar result * type or as a component of a rowtype. * * This function is relatively expensive --- in a function returning set, * try to call it only the first time through. */ TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc) { return internal_get_result_type(fcinfo->flinfo->fn_oid, fcinfo->flinfo->fn_expr, (ReturnSetInfo *) fcinfo->resultinfo, resultTypeId, resultTupleDesc); } /* * internal_get_result_type -- workhorse code implementing all the above * * funcid must always be supplied. call_expr and rsinfo can be NULL if not * available. We will return TYPEFUNC_RECORD, and store NULL into * *resultTupleDesc, if we cannot deduce the complete result rowtype from * the available information. */ static TypeFuncClass internal_get_result_type(Oid funcid, Node *call_expr, ReturnSetInfo *rsinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc) { TypeFuncClass result; HeapTuple tp; Form_pg_proc procform; Oid rettype; /* First fetch the function's pg_proc row to inspect its rettype */ tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(tp); rettype = procform->prorettype; /* * If scalar polymorphic result, try to resolve it. */ if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) { Oid newrettype = exprType(call_expr); if (newrettype == InvalidOid) /* this probably should not happen */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not determine actual result type for function \"%s\" declared to return type %s", NameStr(procform->proname), format_type_be(rettype)))); rettype = newrettype; } if (resultTypeId) *resultTypeId = rettype; if (resultTupleDesc) *resultTupleDesc = NULL; /* default result */ /* Classify the result type */ result = get_type_func_class(rettype); switch (result) { case TYPEFUNC_COMPOSITE: if (resultTupleDesc) *resultTupleDesc = lookup_rowtype_tupdesc(rettype, -1); /* Named composite types can't have any polymorphic columns */ break; case TYPEFUNC_SCALAR: break; case TYPEFUNC_RECORD: /* We must get the tupledesc from call context */ if (rsinfo && IsA(rsinfo, ReturnSetInfo) && rsinfo->expectedDesc != NULL) { result = TYPEFUNC_COMPOSITE; if (resultTupleDesc) *resultTupleDesc = rsinfo->expectedDesc; /* Assume no polymorphic columns here, either */ } break; default: break; } ReleaseSysCache(tp); return result; } /* * Given the declared argument types and modes for a function, * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data * types deduced from the input arguments. Returns TRUE if able to deduce * all types, FALSE if not. This is the same logic as * resolve_polymorphic_tupdesc, but with a different argument representation. * * argmodes may be NULL, in which case all arguments are assumed to be IN mode. */ bool resolve_polymorphic_argtypes(int numargs, Oid *argtypes, Node *call_expr) { Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; int i; /* Resolve polymorphic inputs */ for (i = 0; i < numargs; i++) { switch (argtypes[i]) { case ANYELEMENTOID: if (!OidIsValid(anyelement_type)) { anyelement_type = get_call_expr_argtype(call_expr, i); if (!OidIsValid(anyelement_type)) return false; } argtypes[i] = anyelement_type; break; case ANYARRAYOID: if (!OidIsValid(anyarray_type)) { anyarray_type = get_call_expr_argtype(call_expr, i); if (!OidIsValid(anyarray_type)) return false; } argtypes[i] = anyarray_type; break; default: break; } } return true; } #endif pljava-1.4.3/src/C/pljava/Backend.c0000644000014500000120000005243411634451404016054 0ustar johannstaff/* * Copyright (c) 2004, 2005, 2006 TADA AB - Taby Sweden * Distributed under the terms shown in the file COPYRIGHT * found in the root folder of this project or at * http://eng.tada.se/osprojects/COPYRIGHT.html * * @author Thomas Hallgren */ #include #include #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "org_postgresql_pljava_internal_Backend.h" #include "pljava/Invocation.h" #include "pljava/Function.h" #include "pljava/HashMap.h" #include "pljava/Exception.h" #include "pljava/Backend.h" #include "pljava/Session.h" #include "pljava/SPI.h" #include "pljava/type/String.h" /* Example format: "/usr/local/pgsql/lib" */ #ifndef PKGLIBDIR #error "PKGLIBDIR needs to be defined to compile this file." #endif /* Include the 'magic block' that PostgreSQL 8.2 and up will use to ensure * that a module is not loaded into an incompatible server. */ #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif #ifdef PG_GETCONFIGOPTION #error The macro PG_GETCONFIGOPTION needs to be renamed. #endif #if ( PGSQL_MAJOR_VER > 8 ) #if ( PGSQL_MINOR_VER > 0 ) #define PG_GETCONFIGOPTION(key) GetConfigOption(key, false, true) #else #define PG_GETCONFIGOPTION(key) GetConfigOption(key, true) #endif #else #define PG_GETCONFIGOPTION(key) GetConfigOption(key) #endif #define LOCAL_REFERENCE_COUNT 128 jlong mainThreadId; static JavaVM* s_javaVM = 0; static jclass s_Backend_class; static jmethodID s_setTrusted; static char* vmoptions; static char* classpath; static int statementCacheSize; static bool pljavaDebug; static bool pljavaReleaseLingeringSavepoints; static bool s_currentTrust; static int s_javaLogLevel; bool integerDateTimes = false; extern void Invocation_initialize(void); extern void Exception_initialize(void); extern void Exception_initialize2(void); extern void HashMap_initialize(void); extern void SPI_initialize(void); extern void Type_initialize(void); extern void Function_initialize(void); extern void Session_initialize(void); extern void PgSavepoint_initialize(void); extern void XactListener_initialize(void); extern void SubXactListener_initialize(void); extern void SQLInputFromChunk_initialize(void); extern void SQLOutputToChunk_initialize(void); extern void SQLInputFromTuple_initialize(void); extern void SQLOutputToTuple_initialize(void); static void initPLJavaClasses(void) { jfieldID tlField; JNINativeMethod backendMethods[] = { { "isCallingJava", "()Z", Java_org_postgresql_pljava_internal_Backend_isCallingJava }, { "isReleaseLingeringSavepoints", "()Z", Java_org_postgresql_pljava_internal_Backend_isReleaseLingeringSavepoints }, { "_getConfigOption", "(Ljava/lang/String;)Ljava/lang/String;", Java_org_postgresql_pljava_internal_Backend__1getConfigOption }, { "_getStatementCacheSize", "()I", Java_org_postgresql_pljava_internal_Backend__1getStatementCacheSize }, { "_log", "(ILjava/lang/String;)V", Java_org_postgresql_pljava_internal_Backend__1log }, { "_clearFunctionCache", "()V", Java_org_postgresql_pljava_internal_Backend__1clearFunctionCache }, { 0, 0, 0 } }; Exception_initialize(); elog(DEBUG1, "Getting Backend class pljava.jar"); s_Backend_class = PgObject_getJavaClass("org/postgresql/pljava/internal/Backend"); elog(DEBUG1, "Backend class was there"); PgObject_registerNatives2(s_Backend_class, backendMethods); tlField = PgObject_getStaticJavaField(s_Backend_class, "THREADLOCK", "Ljava/lang/Object;"); JNI_setThreadLock(JNI_getStaticObjectField(s_Backend_class, tlField)); Invocation_initialize(); Exception_initialize2(); SPI_initialize(); Type_initialize(); Function_initialize(); Session_initialize(); PgSavepoint_initialize(); XactListener_initialize(); SubXactListener_initialize(); SQLInputFromChunk_initialize(); SQLOutputToChunk_initialize(); SQLInputFromTuple_initialize(); SQLOutputToTuple_initialize(); s_setTrusted = PgObject_getStaticJavaMethod(s_Backend_class, "setTrusted", "(Z)V"); } /** * Initialize security */ void Backend_setJavaSecurity(bool trusted) { if(trusted != s_currentTrust) { /* GCJ has major issues here. Real work on SecurityManager and * related classes has just started in version 4.0.0. */ #ifndef GCJ JNI_callStaticVoidMethod(s_Backend_class, s_setTrusted, (jboolean)trusted); if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize java security"))); } #endif s_currentTrust = trusted; } } int Backend_setJavaLogLevel(int logLevel) { int oldLevel = s_javaLogLevel; s_javaLogLevel = logLevel; return oldLevel; } /** * Special purpose logging function called from JNI when verbose is enabled. */ static jint JNICALL my_vfprintf(FILE* fp, const char* format, va_list args) { char buf[1024]; char* ep; char* bp = buf; vsnprintf(buf, sizeof(buf), format, args); /* Trim off trailing newline and other whitespace. */ ep = bp + strlen(bp) - 1; while(ep >= bp && isspace(*ep)) --ep; ++ep; *ep = 0; elog(s_javaLogLevel, buf); return 0; } /* * Append those parts of path that has not yet been appended. The HashMap unique is * keeping track of what has been appended already. First appended part will be * prefixed with prefix. */ static void appendPathParts(const char* path, StringInfoData* bld, HashMap unique, const char* prefix) { StringInfoData buf; if(path == 0 || strlen(path) == 0) return; for (;;) { char* pathPart; size_t len; if(*path == 0) break; len = strcspn(path, ";:"); if(len == 1 && *(path+1) == ':' && isalnum(*path)) /* * Windows drive designator, leave it "as is". */ len = strcspn(path+2, ";:") + 2; else if(len == 0) { /* Ignore zero length components. */ ++path; continue; } initStringInfo(&buf); if(*path == '$') { if(len == 7 || (strcspn(path, "/\\") == 7 && strncmp(path, "$libdir", 7) == 0)) { len -= 7; path += 7; appendStringInfo(&buf, PKGLIBDIR); } else ereport(ERROR, ( errcode(ERRCODE_INVALID_NAME), errmsg("invalid macro name '%*s' in dynamic library path", (int)len, path))); } if(len > 0) { appendBinaryStringInfo(&buf, path, len); path += len; } pathPart = buf.data; if(HashMap_getByString(unique, pathPart) == 0) { if(HashMap_size(unique) == 0) appendStringInfo(bld, prefix); else #if defined(WIN32) appendStringInfoChar(bld, ';'); #else appendStringInfoChar(bld, ':'); #endif appendStringInfo(bld, pathPart); HashMap_putByString(unique, pathPart, (void*)1); } pfree(pathPart); if(*path == 0) break; ++path; /* Skip ':' */ } } /* * Get the CLASSPATH. Result is always freshly palloc'd. */ static char* getClassPath(const char* prefix) { char* path; HashMap unique = HashMap_create(13, CurrentMemoryContext); StringInfoData buf; initStringInfo(&buf); appendPathParts(classpath, &buf, unique, prefix); appendPathParts(getenv("CLASSPATH"), &buf, unique, prefix); PgObject_free((PgObject)unique); path = buf.data; if(strlen(path) == 0) { pfree(path); path = 0; } return path; } #if !defined(WIN32) static void pljavaStatementCancelHandler(int signum) { if(!proc_exit_inprogress) { /* Never service the interrupt immediately. In order to find out if * its safe, we would need to know what kind of threading mechanism * the VM uses. That would count for a lot of conditional code. */ QueryCancelPending = true; InterruptPending = true; } } static void pljavaDieHandler(int signum) { if(!proc_exit_inprogress) { /* Never service the interrupt immediately. In order to find out if * its safe, we would need to know what kind of threading mechanism * the VM uses. That would count for a lot of conditional code. */ ProcDiePending = true; InterruptPending = true; } } static void pljavaQuickDieHandler(int signum) { /* Just die. No ereporting here since we don't know what thread this is. */ exit(1); } static sigjmp_buf recoverBuf; static void terminationTimeoutHandler(int signum) { kill(MyProcPid, SIGQUIT); /* Some sleep to get the SIGQUIT a chance to generate * the needed output. */ pg_usleep(1); /* JavaVM did not die within the alloted time */ siglongjmp(recoverBuf, 1); } #endif /* * proc_exit callback to tear down the JVM */ static void _destroyJavaVM(int status, Datum dummy) { if(s_javaVM != 0) { Invocation ctx; #if !defined(WIN32) pqsigfunc saveSigAlrm; Invocation_pushInvocation(&ctx, false); if(sigsetjmp(recoverBuf, 1) != 0) { elog(DEBUG1, "JavaVM destroyed with force"); s_javaVM = 0; return; } saveSigAlrm = pqsignal(SIGALRM, terminationTimeoutHandler); enable_sig_alarm(5000, false); elog(DEBUG1, "Destroying JavaVM..."); JNI_destroyVM(s_javaVM); disable_sig_alarm(false); pqsignal(SIGALRM, saveSigAlrm); #else Invocation_pushInvocation(&ctx, false); elog(DEBUG1, "Destroying JavaVM..."); JNI_destroyVM(s_javaVM); #endif elog(DEBUG1, "JavaVM destroyed"); s_javaVM = 0; currentInvocation = 0; } } typedef struct { JavaVMOption* options; unsigned int size; unsigned int capacity; } JVMOptList; static void JVMOptList_init(JVMOptList* jol) { jol->options = (JavaVMOption*)palloc(10 * sizeof(JavaVMOption)); jol->size = 0; jol->capacity = 10; } static void JVMOptList_delete(JVMOptList* jol) { JavaVMOption* opt = jol->options; JavaVMOption* top = opt + jol->size; while(opt < top) { pfree(opt->optionString); opt++; } pfree(jol->options); } static void JVMOptList_add(JVMOptList* jol, const char* optString, void* extraInfo, bool mustCopy) { JavaVMOption* added; int newPos = jol->size; if(newPos >= jol->capacity) { int newCap = jol->capacity * 2; JavaVMOption* newOpts = (JavaVMOption*)palloc(newCap * sizeof(JavaVMOption)); memcpy(newOpts, jol->options, newPos * sizeof(JavaVMOption)); pfree(jol->options); jol->options = newOpts; jol->capacity = newCap; } added = jol->options + newPos; if(mustCopy) optString = pstrdup(optString); added->optionString = (char*)optString; added->extraInfo = extraInfo; jol->size++; elog(DEBUG1, "Added JVM option string \"%s\"", optString); } /* Split JVM options. The string is split on whitespace unless the * whitespace is found within a string or is escaped by backslash. A * backslash escaped quote is not considered a string delimiter. */ static void addUserJVMOptions(JVMOptList* optList) { const char* cp = vmoptions; if(cp != NULL) { StringInfoData buf; char quote = 0; char c; initStringInfo(&buf); for(;;) { c = *cp++; switch(c) { case 0: break; case '"': case '\'': if(quote == c) quote = 0; else quote = c; appendStringInfoChar(&buf, c); continue; case '\\': appendStringInfoChar(&buf, '\\'); c = *cp++; /* Interpret next character verbatim */ if(c == 0) break; appendStringInfoChar(&buf, c); continue; default: if(quote == 0 && isspace((int)c)) { while((c = *cp++) != 0) { if(!isspace((int)c)) break; } if(c == 0) break; if(c != '-') appendStringInfoChar(&buf, ' '); else if(buf.len > 0) { /* Whitespace followed by '-' triggers new * option declaration. */ JVMOptList_add(optList, buf.data, 0, true); buf.len = 0; buf.data[0] = 0; } } appendStringInfoChar(&buf, c); continue; } break; } if(buf.len > 0) JVMOptList_add(optList, buf.data, 0, true); pfree(buf.data); } } /** * Initialize the session */ static void initJavaSession(void) { jclass sessionClass = PgObject_getJavaClass("org/postgresql/pljava/internal/Session"); jmethodID init = PgObject_getStaticJavaMethod(sessionClass, "init", "()J"); mainThreadId = JNI_callStaticLongMethod(sessionClass, init); JNI_deleteLocalRef(sessionClass); if(JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); ereport(ERROR, ( errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize java session"))); } } static void checkIntTimeType(void) { const char* idt = PG_GETCONFIGOPTION("integer_datetimes"); integerDateTimes = (strcmp(idt, "on") == 0); elog(DEBUG1, integerDateTimes ? "Using integer_datetimes" : "Not using integer_datetimes"); } static bool s_firstTimeInit = true; static void initializeJavaVM(void) { jboolean jstat; JavaVMInitArgs vm_args; JVMOptList optList; JVMOptList_init(&optList); if(s_firstTimeInit) { s_firstTimeInit = false; s_javaLogLevel = INFO; checkIntTimeType(); HashMap_initialize(); DefineCustomStringVariable( "pljava.vmoptions", "Options sent to the JVM when it is created", NULL, &vmoptions, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) NULL, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomStringVariable( "pljava.classpath", "Classpath used by the JVM", NULL, &classpath, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) NULL, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomBoolVariable( "pljava.debug", "Stop the backend to attach a debugger", NULL, &pljavaDebug, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) false, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomIntVariable( "pljava.statement_cache_size", "Size of the prepared statement MRU cache", NULL, &statementCacheSize, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 11, #endif 0, 512, PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); DefineCustomBoolVariable( "pljava.release_lingering_savepoints", "If true, lingering savepoints will be released on function exit. If false, the will be rolled back", NULL, &pljavaReleaseLingeringSavepoints, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) false, #endif PGC_USERSET, #if (PGSQL_MAJOR_VER > 8 || (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3)) 0, #endif #if (PGSQL_MAJOR_VER > 9 || (PGSQL_MAJOR_VER == 9 && PGSQL_MINOR_VER > 0)) NULL, #endif NULL, NULL); EmitWarningsOnPlaceholders("pljava"); s_firstTimeInit = false; } #ifdef PLJAVA_DEBUG /* Hard setting for debug. Don't forget to recompile... */ pljavaDebug = 1; #endif addUserJVMOptions(&optList); effectiveClassPath = getClassPath("-Djava.class.path="); if(effectiveClassPath != 0) { JVMOptList_add(&optList, effectiveClassPath, 0, true); } /** * As stipulated by JRT-2003 */ JVMOptList_add(&optList, "-Dsqlj.defaultconnection=jdbc:default:connection", 0, true); JVMOptList_add(&optList, "vfprintf", (void*)my_vfprintf, true); #ifndef GCJ JVMOptList_add(&optList, "-Xrs", 0, true); #endif if(pljavaDebug) { elog(INFO, "Backend pid = %d. Attach the debugger and set pljavaDebug to false to continue", getpid()); while(pljavaDebug) pg_usleep(1000000L); } vm_args.nOptions = optList.size; vm_args.options = optList.options; vm_args.version = JNI_VERSION_1_4; vm_args.ignoreUnrecognized = JNI_FALSE; elog(DEBUG1, "Creating JavaVM"); jstat = JNI_createVM(&s_javaVM, &vm_args); if(jstat == JNI_OK && JNI_exceptionCheck()) { JNI_exceptionDescribe(); JNI_exceptionClear(); jstat = JNI_ERR; } JVMOptList_delete(&optList); if(jstat != JNI_OK) ereport(ERROR, (errmsg("Failed to create Java VM"))); #if !defined(WIN32) pqsignal(SIGINT, pljavaStatementCancelHandler); pqsignal(SIGTERM, pljavaDieHandler); pqsignal(SIGQUIT, pljavaQuickDieHandler); #endif elog(DEBUG1, "JavaVM created"); /* Register an on_proc_exit handler that destroys the VM */ on_proc_exit(_destroyJavaVM, 0); initPLJavaClasses(); initJavaSession(); } static Datum internalCallHandler(bool trusted, PG_FUNCTION_ARGS); extern Datum javau_call_handler(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(javau_call_handler); /* * This is the entry point for all untrusted calls. */ Datum javau_call_handler(PG_FUNCTION_ARGS) { return internalCallHandler(false, fcinfo); } extern Datum java_call_handler(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(java_call_handler); /* * This is the entry point for all trusted calls. */ Datum java_call_handler(PG_FUNCTION_ARGS) { return internalCallHandler(true, fcinfo); } static Datum internalCallHandler(bool trusted, PG_FUNCTION_ARGS) { Invocation ctx; Datum retval = 0; if(s_javaVM == 0) { Invocation_pushBootContext(&ctx); PG_TRY(); { initializeJavaVM(); Invocation_popBootContext(); } PG_CATCH(); { Invocation_popBootContext(); /* JVM initialization failed for some reason. Destroy * the VM if it exists. Perhaps the user will try * fixing the pljava.classpath and make a new attempt. */ _destroyJavaVM(0, 0); /* We can't stay here... */ PG_RE_THROW(); } PG_END_TRY(); /* Force initial setting */ s_currentTrust = !trusted; } Invocation_pushInvocation(&ctx, trusted); PG_TRY(); { Function function = Function_getFunction(fcinfo); if(CALLED_AS_TRIGGER(fcinfo)) { /* Called as a trigger procedure */ retval = Function_invokeTrigger(function, fcinfo); } else { /* Called as a function */ retval = Function_invoke(function, fcinfo); } Invocation_popInvocation(false); } PG_CATCH(); { Invocation_popInvocation(true); PG_RE_THROW(); } PG_END_TRY(); return retval; } /**************************************** * JNI methods ****************************************/ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_VERSION_1_4; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _getConfigOption * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL JNICALL Java_org_postgresql_pljava_internal_Backend__1getConfigOption(JNIEnv* env, jclass cls, jstring jkey) { jstring result = 0; BEGIN_NATIVE char* key = String_createNTS(jkey); if(key != 0) { PG_TRY(); { const char *value = PG_GETCONFIGOPTION(key); pfree(key); if(value != 0) result = String_createJavaStringFromNTS(value); } PG_CATCH(); { Exception_throw_ERROR("GetConfigOption"); } PG_END_TRY(); } END_NATIVE return result; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _getStatementCacheSize * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_postgresql_pljava_internal_Backend__1getStatementCacheSize(JNIEnv* env, jclass cls) { return statementCacheSize; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _log * Signature: (ILjava/lang/String;)V */ JNIEXPORT void JNICALL JNICALL Java_org_postgresql_pljava_internal_Backend__1log(JNIEnv* env, jclass cls, jint logLevel, jstring jstr) { BEGIN_NATIVE_NO_ERRCHECK char* str = String_createNTS(jstr); if(str != 0) { /* elog uses printf formatting but the logger does not so we must escape all * '%' in the string. */ char c; const char* cp; int percentCount = 0; for(cp = str; (c = *cp) != 0; ++cp) { if(c == '%') ++percentCount; } if(percentCount > 0) { /* Make room to expand all "%" to "%%" */ char* str2 = palloc((cp - str) + percentCount + 1); char* cp2 = str2; /* Expand... */ for(cp = str; (c = *cp) != 0; ++cp) { if(c == '%') *cp2++ = c; *cp2++ = c; } *cp2 = 0; pfree(str); str = str2; } PG_TRY(); { elog(logLevel, str); pfree(str); } PG_CATCH(); { Exception_throw_ERROR("ereport"); } PG_END_TRY(); } END_NATIVE } /* * Class: org_postgresql_pljava_internal_Backend * Method: isCallingJava * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Backend_isCallingJava(JNIEnv* env, jclass cls) { return JNI_isCallingJava(); } /* * Class: org_postgresql_pljava_internal_Backend * Method: isReleaseLingeringSavepoints * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_postgresql_pljava_internal_Backend_isReleaseLingeringSavepoints(JNIEnv* env, jclass cls) { return pljavaReleaseLingeringSavepoints ? JNI_TRUE : JNI_FALSE; } /* * Class: org_postgresql_pljava_internal_Backend * Method: _clearFunctionCache * Signature: ()V */ JNIEXPORT void JNICALL Java_org_postgresql_pljava_internal_Backend__1clearFunctionCache(JNIEnv* env, jclass cls) { BEGIN_NATIVE_NO_ERRCHECK Function_clearFunctionCache(); END_NATIVE } pljava-1.4.3/.project0000644000014500000120000001014611634451404013574 0ustar johannstaff org.postgresql.pljava org.eclipse.jdt.core.javabuilder org.eclipse.cdt.make.core.makeBuilder clean,full,incremental, org.eclipse.cdt.make.core.build.arguments -e /bin/make org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.build.location \org.postgresql.pljava org.eclipse.cdt.make.core.buildCommand c:/msys/1.0/bin/make org.eclipse.cdt.make.core.environment JAVA_HOME=C:/Progra~1/Java/jdk1.5.0_06| org.eclipse.cdt.make.core.incrementalBuildTarget all org.eclipse.cdt.make.core.fullBuildTarget clean all org.eclipse.cdt.make.core.build.target.auto all org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.stopOnError false org.eclipse.cdt.make.core.build.target.full clean all org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.build.command c:/msys/bin/rxvt org.eclipse.cdt.make.core.build.target.clean clean org.eclipse.cdt.make.core.enabledIncrementalBuild true org.eclipse.cdt.core.errorOutputParser org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.VCErrorParser; org.eclipse.cdt.make.core.buildLocation org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.useDefaultBuildCmd true org.eclipse.cdt.make.core.buildArguments org.eclipse.cdt.make.core.build.target.inc all org.eclipse.cdt.make.core.ScannerConfigBuilder org.eclipse.cdt.make.core.ScannerConfigDiscoveryEnabled false org.eclipse.jdt.core.javanature org.eclipse.cdt.core.cnature org.eclipse.cdt.make.core.makeNature org.eclipse.cdt.core.ccnature org.eclipse.cdt.make.core.ScannerConfigNature org.postgresql.jdbc 2 POSTGRESQL_HOME/src/interfaces/jdbc pljava-1.4.3/COPYRIGHT0000644000014500000120000000322511634451404013420 0ustar johannstaffPL/Java, a backend Java integration for PostgreSQL Java™ is a registered trademark of Sun Microsystems, Inc. in the United States and other countries. PL/Java Copyright (c) 2003 - 2006 Tada AB - Taby Sweden All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Tada AB nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pljava-1.4.3/packaging/0000755000014500000120000000000011634451404014047 5ustar johannstaffpljava-1.4.3/packaging/Makefile0000644000014500000120000000552611634451404015517 0ustar johannstaff#------------------------------------------------------------------------- # Copyright (c) 2003, 2004 TADA AB - Taby Sweden # Distributed under the terms shown in the file COPYRIGHT. # # @author Thomas Hallgren #------------------------------------------------------------------------- include $(PGXS) SS_VERSION := $(subst ., ,$(subst devel,.devel,$(subst beta,.beta,$(subst rc,.rc,$(subst RC,.RC,$(VERSION)))))) PGSQL_MAJOR_VER = $(word 1,$(SS_VERSION)) PGSQL_MINOR_VER = $(word 2,$(SS_VERSION)) DISTRIB := distrib ifdef USE_GCJ GCJEXTRA=-gcj- else GCJEXTRA=- endif RELNAME := pljava-$(host_tuple)$(GCJEXTRA)pg$(PGSQL_MAJOR_VER).$(PGSQL_MINOR_VER)-$(PLJAVA_VERSION) SRCNAME := pljava-src-$(PLJAVA_VERSION) PKGNAME := pljava-$(PLJAVA_VERSION) release: @-mkdir -p $(RELNAME) @-rm -f $(RELNAME)/* @find . \( \ -name '*.jar' \ -o -name '*.so' \ -o -name '*.dll' \ \) -a -not -path ./$(RELNAME)'/*' -exec cp {} $(RELNAME) \; @cp ../src/sql/*.sql $(RELNAME) @$(TAR) cf $(RELNAME)/docs.tar docs (cd $(RELNAME); $(TAR) zcf ../distrib/$(RELNAME).tar.gz .) source_tarball: @-mkdir -p distrib @-mkdir -p $(PKGNAME) @-rm -rf $(PKGNAME)/* @(cd ..; find . \( \ -name CVS \ -o -name bin \ -o -name build \ -o -name .cvsignore \ \) -prune -o \( -type f -exec cp --parent {} build/$(PKGNAME) \; \) ) @$(TAR) zcf $(DISTRIB)/$(SRCNAME).tar.gz $(PKGNAME) JAR := jar PUBLIC_ROOT := classes/pljava PUBLIC_CLASSES := $(wildcard $(PUBLIC_ROOT)/org/postgresql/pljava/*.*) PUBLIC_REL_CLASSES = $(foreach var,$(PUBLIC_CLASSES),$(subst $(PUBLIC_ROOT)/,,$(var))) PUBLIC_SRC_ROOT := ../src/java/pljava PUBLIC_SRCS := $(wildcard $(PUBLIC_SRC_ROOT)/org/postgresql/pljava/*.*) PUBLIC_REL_SRCS = $(foreach var,$(PUBLIC_SRCS),$(subst $(PUBLIC_SRC_ROOT)/,,$(var))) PUBLIC_NAME := pljava-public-$(PLJAVA_VERSION) PUBLIC_BUNDLE := distrib/$(PUBLIC_NAME)-bundle.jar JIRA_DESC := distrib/jira-desc.txt BUNDLEDIR := maven_bundle PUBLIC_JARFILE := $(PUBLIC_NAME).jar PUBLIC_SRC_JARFILE := $(PUBLIC_NAME)-src.jar maven_bundle: $(PUBLIC_BUNDLE) $(JIRA_DESC) $(PUBLIC_BUNDLE): $(BUNDLEDIR)/LICENSE.txt $(BUNDLEDIR)/project.xml $(BUNDLEDIR)/$(PUBLIC_JARFILE) $(BUNDLEDIR)/$(PUBLIC_SRC_JARFILE) @(cd $(BUNDLEDIR); $(JAR) cf ../$@ *) $(BUNDLEDIR)/LICENSE.txt: $(PROJDIR)/COPYRIGHT @-mkdir -p $(BUNDLEDIR) @cp $< $(BUNDLEDIR)/LICENSE.txt $(BUNDLEDIR)/project.xml: $(PROJDIR)/packaging/project.xml @-mkdir -p $(BUNDLEDIR) @sed -e 's/@PLJAVA_VERSION@/'$(PLJAVA_VERSION)'/' $< > $@ $(BUNDLEDIR)/$(PUBLIC_JARFILE): $(PUBLIC_CLASSES) @-mkdir -p $(BUNDLEDIR) @(cd $(PUBLIC_ROOT); $(JAR) cf ../../$@ $(PUBLIC_REL_CLASSES)) $(BUNDLEDIR)/$(PUBLIC_SRC_JARFILE): $(PUBLIC_SRCS) @-mkdir -p $(BUNDLEDIR) @(cd $(PUBLIC_SRC_ROOT); $(JAR) cf ../../../build/$@ $(PUBLIC_REL_SRCS)) $(JIRA_DESC): $(PROJDIR)/packaging/jira-desc.txt @sed -e 's/@PLJAVA_VERSION@/'$(PLJAVA_VERSION)'/' $< > $@ pljava-1.4.3/packaging/project.xml0000644000014500000120000000303311634451404016236 0ustar johannstaff PL/Java pljava-public postgresql @PLJAVA_VERSION@ Tada AB, Taby Sweden http://www.tada.se 2003 http://gborg.postgresql.org/images/projects/pljava/screenshots/pljava_logo_small.gif Public Java interfaces for PL/Java, a server side Java solution for PostgreSQL Public interfaces for PL/Java http://gborg.postgresql.org/images/projects/pljava pljava-dev http://gborg.postgresql.org/mailman/listinfo/pljava-dev http://gborg.postgresql.org/mailman/listinfo/pljava-dev http://gborg.postgresql.org/pipermail/pljava-dev Thomas Hallgren thhal mailto:thhal@mailblocks.com http://gborg.postgresql.org/member/memprofile?4551 MET BSD repo pljava-1.4.3/packaging/jira-desc.txt0000644000014500000120000000057711634451404016462 0ustar johannstaffftp://gborg.postgresql.org/pub/pljava/stable/pljava-public-@PLJAVA_VERSION@-bundle.zip http://gborg.postgresql.org/project/pljava http://gborg.postgresql.org/project/pljava/genpage.php?thhalcv PL/Java is a server side Java solution for PostgreSQL. The jar supplied in this bundle contains the interfaces and classes needed when writing new Java functions and triggers for PL/Java. pljava-1.4.3/.externalToolBuilders/0000755000014500000120000000000011634451404016353 5ustar johannstaffpljava-1.4.3/.externalToolBuilders/Header generation.launch0000644000014500000120000000123211634451404023051 0ustar johannstaff pljava-1.4.3/.externalToolBuilders/org.postgresql.pljava build.xml [Builder].launch0000644000014500000120000000225711634451404027540 0ustar johannstaff pljava-1.4.3/fixes/0000755000014500000120000000000011634451403013240 5ustar johannstaffpljava-1.4.3/fixes/gcj/0000755000014500000120000000000011634451403014003 5ustar johannstaffpljava-1.4.3/fixes/gcj/java_sql_Types.h0000644000014500000120000000430411634451403017141 0ustar johannstaff/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class java_sql_Types */ #ifndef _Included_java_sql_Types #define _Included_java_sql_Types #ifdef __cplusplus extern "C" { #endif #undef java_sql_Types_BIT #define java_sql_Types_BIT -7L #undef java_sql_Types_TINYINT #define java_sql_Types_TINYINT -6L #undef java_sql_Types_SMALLINT #define java_sql_Types_SMALLINT 5L #undef java_sql_Types_INTEGER #define java_sql_Types_INTEGER 4L #undef java_sql_Types_BIGINT #define java_sql_Types_BIGINT -5L #undef java_sql_Types_FLOAT #define java_sql_Types_FLOAT 6L #undef java_sql_Types_REAL #define java_sql_Types_REAL 7L #undef java_sql_Types_DOUBLE #define java_sql_Types_DOUBLE 8L #undef java_sql_Types_NUMERIC #define java_sql_Types_NUMERIC 2L #undef java_sql_Types_DECIMAL #define java_sql_Types_DECIMAL 3L #undef java_sql_Types_CHAR #define java_sql_Types_CHAR 1L #undef java_sql_Types_VARCHAR #define java_sql_Types_VARCHAR 12L #undef java_sql_Types_LONGVARCHAR #define java_sql_Types_LONGVARCHAR -1L #undef java_sql_Types_DATE #define java_sql_Types_DATE 91L #undef java_sql_Types_TIME #define java_sql_Types_TIME 92L #undef java_sql_Types_TIMESTAMP #define java_sql_Types_TIMESTAMP 93L #undef java_sql_Types_BINARY #define java_sql_Types_BINARY -2L #undef java_sql_Types_VARBINARY #define java_sql_Types_VARBINARY -3L #undef java_sql_Types_LONGVARBINARY #define java_sql_Types_LONGVARBINARY -4L #undef java_sql_Types_NULL #define java_sql_Types_NULL 0L #undef java_sql_Types_OTHER #define java_sql_Types_OTHER 1111L #undef java_sql_Types_JAVA_OBJECT #define java_sql_Types_JAVA_OBJECT 2000L #undef java_sql_Types_DISTINCT #define java_sql_Types_DISTINCT 2001L #undef java_sql_Types_STRUCT #define java_sql_Types_STRUCT 2002L #undef java_sql_Types_ARRAY #define java_sql_Types_ARRAY 2003L #undef java_sql_Types_BLOB #define java_sql_Types_BLOB 2004L #undef java_sql_Types_CLOB #define java_sql_Types_CLOB 2005L #undef java_sql_Types_REF #define java_sql_Types_REF 2006L #undef java_sql_Types_DATALINK #define java_sql_Types_DATALINK 70L #undef java_sql_Types_BOOLEAN #define java_sql_Types_BOOLEAN 16L #ifdef __cplusplus } #endif #endif pljava-1.4.3/.settings/0000755000014500000120000000000011634451404014041 5ustar johannstaffpljava-1.4.3/.settings/org.eclipse.jdt.core.prefs0000644000014500000120000000115311634451404021023 0ustar johannstaff#Fri Feb 11 14:44:25 CET 2005 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.4 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning org.eclipse.jdt.core.compiler.source=1.4 pljava-1.4.3/.settings/org.eclipse.cdt.core.prefs0000644000014500000120000000015011634451404021010 0ustar johannstaff#Fri May 19 16:01:32 CEST 2006 eclipse.preferences.version=1 indexerId=org.eclipse.cdt.core.fastIndexer pljava-1.4.3/.cdtproject0000644000014500000120000000645211634451404014274 0ustar johannstaff make -e /bin/make test_all false true c:/msys/bin/rxvt -e /bin/make clean false true c:/msys/bin/rxvt -e /bin/make all false true c:/msys/bin/rxvt -e /bin/make release false true