Pouze v ./org.postgresql.pljava/: build Pouze v ./pljava: .cdtproject Pouze v ./pljava: .classpath Pouze v ./pljava: .cvsignore Pouze v ./pljava: .project diff -c --recursive ./org.postgresql.pljava/src/C/include/pljava/pljava.h ./pljava/src/C/include/pljava/pljava.h *** ./org.postgresql.pljava/src/C/include/pljava/pljava.h 2007-09-12 19:59:42.000000000 +0200 --- ./pljava/src/C/include/pljava/pljava.h 2010-04-16 11:25:10.000000000 +0200 *************** *** 53,59 **** #define STACK_BASE_POP() #else ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3) extern PGDLLIMPORT char* stack_base_ptr; #else extern DLLIMPORT char* stack_base_ptr; --- 53,59 ---- #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; diff -c --recursive ./org.postgresql.pljava/src/C/pljava/Backend.c ./pljava/src/C/pljava/Backend.c *** ./org.postgresql.pljava/src/C/pljava/Backend.c 2009-02-10 02:20:03.000000000 +0100 --- ./pljava/src/C/pljava/Backend.c 2010-04-16 11:47:10.000000000 +0200 *************** *** 533,539 **** --- 533,544 ---- 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"); } *************** *** 561,571 **** "Options sent to the JVM when it is created", NULL, &vmoptions, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) NULL, #endif PGC_USERSET, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 0, #endif NULL, NULL); --- 566,576 ---- "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 NULL, NULL); *************** *** 575,585 **** "Classpath used by the JVM", NULL, &classpath, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) NULL, #endif PGC_USERSET, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 0, #endif NULL, NULL); --- 580,590 ---- "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 NULL, NULL); *************** *** 589,599 **** "Stop the backend to attach a debugger", NULL, &pljavaDebug, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) false, #endif PGC_USERSET, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 0, #endif NULL, NULL); --- 594,604 ---- "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 NULL, NULL); *************** *** 603,614 **** "Size of the prepared statement MRU cache", NULL, &statementCacheSize, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 11, #endif 0, 512, PGC_USERSET, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 0, #endif NULL, NULL); --- 608,619 ---- "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 NULL, NULL); *************** *** 618,628 **** "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_MINOR_VER > 3) false, #endif PGC_USERSET, ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER > 3) 0, #endif NULL, NULL); --- 623,633 ---- "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 NULL, NULL); *************** *** 805,811 **** --- 810,821 ---- { 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); diff -c --recursive ./org.postgresql.pljava/src/C/pljava/JNICalls.c ./pljava/src/C/pljava/JNICalls.c *** ./org.postgresql.pljava/src/C/pljava/JNICalls.c 2008-10-09 21:00:37.000000000 +0200 --- ./pljava/src/C/pljava/JNICalls.c 2010-04-16 11:25:10.000000000 +0200 *************** *** 15,21 **** JNIEnv* jniEnv; ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3) extern PGDLLIMPORT int log_min_messages; extern PGDLLIMPORT int client_min_messages; #else --- 15,21 ---- 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 diff -c --recursive ./org.postgresql.pljava/src/C/pljava/Makefile ./pljava/src/C/pljava/Makefile *** ./org.postgresql.pljava/src/C/pljava/Makefile 2009-06-22 10:37:27.000000000 +0200 --- ./pljava/src/C/pljava/Makefile 2010-04-16 11:48:08.000000000 +0200 *************** *** 26,32 **** # include $(PGXS) ! SS_VERSION := $(subst ., ,$(subst devel,.99,$(subst beta,.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)) --- 26,32 ---- # 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)) diff -c --recursive ./org.postgresql.pljava/src/C/pljava/Session.c ./pljava/src/C/pljava/Session.c *** ./org.postgresql.pljava/src/C/pljava/Session.c 2008-01-11 05:10:06.000000000 +0100 --- ./pljava/src/C/pljava/Session.c 2010-04-16 11:25:10.000000000 +0200 *************** *** 42,48 **** */ BEGIN_NATIVE_NO_ERRCHECK #if ( \ ! (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) \ --- 42,49 ---- */ 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) \ diff -c --recursive ./org.postgresql.pljava/src/C/pljava/type/Timestamp.c ./pljava/src/C/pljava/type/Timestamp.c *** ./org.postgresql.pljava/src/C/pljava/type/Timestamp.c 2008-02-01 06:07:22.000000000 +0100 --- ./pljava/src/C/pljava/type/Timestamp.c 2010-04-16 11:47:10.000000000 +0200 *************** *** 155,161 **** } #if !(PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER == 0) ! #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER >= 3) extern PGDLLIMPORT pg_tz* session_timezone; #else extern DLLIMPORT pg_tz* global_timezone; --- 155,161 ---- } #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; diff -c --recursive ./org.postgresql.pljava/src/C/pljava/type/UDT.c ./pljava/src/C/pljava/type/UDT.c *** ./org.postgresql.pljava/src/C/pljava/type/UDT.c 2009-02-19 22:03:02.000000000 +0100 --- ./pljava/src/C/pljava/type/UDT.c 2010-04-16 11:52:10.000000000 +0200 *************** *** 12,17 **** --- 12,21 ---- #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" diff -c --recursive ./org.postgresql.pljava/src/java/Makefile.global ./pljava/src/java/Makefile.global *** ./org.postgresql.pljava/src/java/Makefile.global 2010-04-29 01:05:49.000000000 +0200 --- ./pljava/src/java/Makefile.global 2006-02-11 14:57:51.000000000 +0100 *************** *** 14,21 **** GCJ := gcj JAVAC := $(GCJ) -C else ! JAVAC := javac ! #-source 1.6 -target 1.6 endif JARFILE := $(TARGETDIR)/$(NAME).jar --- 14,20 ---- GCJ := gcj JAVAC := $(GCJ) -C else ! JAVAC := javac -source 1.4 -target 1.4 endif JARFILE := $(TARGETDIR)/$(NAME).jar Pouze v ./pljava/src/java: org diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/ExecutionPlan.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/ExecutionPlan.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/ExecutionPlan.java 2010-04-29 00:44:08.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/ExecutionPlan.java 2006-03-26 16:10:45.000000000 +0200 *************** *** 8,201 **** import java.sql.SQLException; import java.util.Collections; - import java.util.LinkedHashMap; import java.util.Map; /** ! * 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; ! } ! ! @Override ! 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; ! } ! ! @Override ! 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; ! } ! ! @Override ! 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 = 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); } --- 8,234 ---- 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); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/ObjectPoolImpl.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/ObjectPoolImpl.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/ObjectPoolImpl.java 2010-04-29 00:47:10.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/ObjectPoolImpl.java 2006-02-11 14:57:51.000000000 +0100 *************** *** 14,140 **** 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 = 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(); ! } } --- 14,175 ---- 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(); ! } } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/Oid.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/Oid.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/Oid.java 2010-04-29 00:45:37.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/Oid.java 2006-05-06 13:41:13.000000000 +0200 *************** *** 10,217 **** 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; ! } ! ! @Override ! 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. ! */ ! @Override ! public boolean equals(Object o) { ! return (o == this) || ((o instanceof Oid) && ((Oid) o).m_native == m_native); ! } ! ! @Override ! 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. ! */ ! @Override ! public int hashCode() { ! return m_native; ! } ! ! @Override ! public int intValue() { ! return m_native; ! } ! ! @Override ! public long longValue() { ! return m_native; ! } ! ! /** ! * Returns a string representation of this OID. ! */ ! @Override ! 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; } --- 10,237 ---- 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; } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/PgSavepoint.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/PgSavepoint.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/PgSavepoint.java 2010-04-29 00:46:09.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/PgSavepoint.java 2006-02-11 14:57:51.000000000 +0100 *************** *** 15,110 **** /** * @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; ! } ! ! @Override ! public int hashCode() { ! return this.getSavepointId(); ! } ! ! @Override ! 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); } --- 15,139 ---- /** * @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); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/Session.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/Session.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/Session.java 2010-04-29 00:49:07.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/Session.java 2006-05-06 13:41:13.000000000 +0200 *************** *** 17,119 **** 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); } --- 17,146 ---- 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); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/SubXactListener.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/SubXactListener.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/SubXactListener.java 2010-04-29 00:48:02.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/SubXactListener.java 2006-02-11 14:57:51.000000000 +0100 *************** *** 11,59 **** 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); } --- 11,69 ---- 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); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/TransactionalMap.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/TransactionalMap.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/TransactionalMap.java 2010-04-29 00:54:25.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/TransactionalMap.java 2006-02-11 14:57:51.000000000 +0100 *************** *** 6,349 **** */ package org.postgresql.pljava.internal; - import java.util.AbstractCollection; - import java.util.AbstractSet; - import java.util.Collection; import java.util.HashMap; - import java.util.Iterator; import java.util.Map; - import java.util.NoSuchElementException; import java.util.Set; /** ! * 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). ! */ ! @Override ! 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(); ! } ! ! @Override ! 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); ! } ! ! @Override ! 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; ! } ! ! @Override ! 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; ! } ! ! @Override ! 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; ! } ! ! @Override ! 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; ! } ! ! @Override ! public Set> entrySet() { ! if (m_entrySet == null) { ! m_entrySet = new AbstractSet() { ! @Override ! public Iterator iterator() { ! return TransactionalMap.this.getEntryIterator(); ! } ! ! @Override ! public int size() { ! return TransactionalMap.this.size(); ! } ! ! @Override ! public boolean contains(Object k) { ! return TransactionalMap.this.containsKey(k); ! } ! }; ! } ! return m_entrySet; ! } ! ! @Override ! public boolean isEmpty() { ! return (this.size() == 0); ! } ! ! @Override ! public Set keySet() { ! if (m_keySet == null) { ! m_keySet = new AbstractSet() { ! @Override ! public Iterator iterator() { ! return TransactionalMap.this.getKeyIterator(); ! } ! ! @Override ! public int size() { ! return TransactionalMap.this.size(); ! } ! ! @Override ! public boolean contains(Object k) { ! return TransactionalMap.this.containsKey(k); ! } ! }; ! } ! return m_keySet; ! } ! ! @Override ! public Object put(Object key, Object value) { ! Object old = this.get(key); ! super.put(key, value); ! return old; ! } ! ! @Override ! public void putAll(Map t) { ! super.putAll(t); ! } ! ! @Override ! public Collection values() { ! if (m_valueColl == null) { ! m_valueColl = new AbstractCollection() { ! @Override ! public Iterator iterator() { ! return TransactionalMap.this.getValueIterator(); ! } ! ! @Override ! public int size() { ! return TransactionalMap.this.size(); ! } ! ! @Override ! 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 final 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 { ! @Override ! public Object next() { ! return new BackedEntry(super.next()); ! } ! } ! ! protected class ValueIterator extends KeyIterator { ! @Override ! public Object next() { ! return TransactionalMap.this.get(super.next()); ! } ! } } \ Chybí znak konce řádku na konci souboru --- 6,389 ---- */ 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()); ! } ! } } \ Chybí znak konce řádku na konci souboru diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/XactListener.java ./pljava/src/java/pljava/org/postgresql/pljava/internal/XactListener.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/internal/XactListener.java 2010-04-29 00:47:39.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/internal/XactListener.java 2006-02-11 14:57:51.000000000 +0100 *************** *** 11,59 **** 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); } --- 11,69 ---- 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); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSet.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSet.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSet.java 2010-04-29 00:16:08.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSet.java 2006-02-11 14:57:50.000000000 +0100 *************** *** 8,459 **** 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.NClob; import java.sql.Ref; import java.sql.ResultSet; - import java.sql.RowId; import java.sql.SQLException; - import java.sql.SQLXML; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; 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 ! */ ! @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 columnLabel, Map> map) throws SQLException { ! return this.getObject(this.findColumn(columnLabel), 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 ! */ ! @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); ! } ! ! public int getHoldability() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Reader getNCharacterStream(int columnIndex) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Reader getNCharacterStream(String columnLabel) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public NClob getNClob(int columnIndex) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public NClob getNClob(String columnLabel) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public String getNString(int columnIndex) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public String getNString(String columnLabel) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Object getObject(int columnIndex, Map> map) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public RowId getRowId(int columnIndex) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public RowId getRowId(String columnLabel) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public SQLXML getSQLXML(int columnIndex) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public SQLXML getSQLXML(String columnLabel) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean isClosed() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateClob(int columnIndex, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateClob(String columnLabel, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(int columnIndex, NClob nClob) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(String columnLabel, NClob nClob) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(int columnIndex, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(String columnLabel, Reader reader) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNString(int columnIndex, String nString) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateNString(String columnLabel, String nString) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateRowId(int columnIndex, RowId x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateRowId(String columnLabel, RowId x) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean isWrapperFor(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public T unwrap(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } } --- 8,360 ---- 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); ! } } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSetMetaData.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSetMetaData.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSetMetaData.java 2010-04-29 00:20:24.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/AbstractResultSetMetaData.java 2006-05-06 13:41:13.000000000 +0200 *************** *** 7,432 **** package org.postgresql.pljava.jdbc; - import java.sql.Connection; - import java.sql.DriverManager; import java.sql.ResultSetMetaData; import java.sql.SQLException; 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; ! } ! public boolean isWrapperFor(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } - public T unwrap(Class iface) throws SQLException { - throw new UnsupportedOperationException("This method is not implemented yet!"); - } } --- 7,419 ---- 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; ! } } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/BlobValue.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/BlobValue.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/BlobValue.java 2010-04-29 00:14:05.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/BlobValue.java 2006-05-06 13:41:13.000000000 +0200 *************** *** 7,14 **** package org.postgresql.pljava.jdbc; import java.io.ByteArrayInputStream; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.sql.Blob; --- 7,14 ---- 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; *************** *** 17,222 **** /** * @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 ! // *************************************** ! @Override ! 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; ! } ! ! @Override ! public synchronized void mark(int readLimit) { ! m_stream.mark(readLimit); ! m_markPos = m_streamPos; ! } ! ! @Override ! 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(); ! } ! ! @Override ! public synchronized int read() throws IOException { ! int rs = m_stream.read(); ! m_streamPos++; ! return rs; ! } ! ! @Override ! public synchronized int read(byte[] b) throws IOException { ! int rs = m_stream.read(b); ! m_streamPos += rs; ! return rs; ! } ! ! @Override ! 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; ! } ! ! @Override ! 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(); ! } ! ! @Override ! 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(); ! } ! ! public void free() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public InputStream getBinaryStream(long pos, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } } --- 17,246 ---- /** * @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(); ! } } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ClobValue.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ClobValue.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ClobValue.java 2010-04-29 00:12:43.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ClobValue.java 2006-05-06 13:41:13.000000000 +0200 *************** *** 8,15 **** import java.io.BufferedInputStream; import java.io.CharConversionException; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; --- 8,15 ---- 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; *************** *** 20,217 **** /** * @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()); ! } ! ! @Override ! public void close() throws IOException { ! m_reader.close(); ! m_readerPos = 0; ! m_markPos = 0; ! } ! ! public InputStream getAsciiStream() { ! return new BufferedInputStream(new InputStream() { ! @Override ! 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; ! } ! ! @Override ! public synchronized void mark(int readLimit) throws IOException { ! m_reader.mark(readLimit); ! m_markPos = m_readerPos; ! } ! ! @Override ! 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(); ! } ! ! @Override ! public synchronized int read() throws IOException { ! int rs = m_reader.read(); ! m_readerPos++; ! return rs; ! } ! ! @Override ! public synchronized int read(char[] b) throws IOException { ! int rs = m_reader.read(b); ! m_readerPos += rs; ! return rs; ! } ! ! @Override ! 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; ! } ! ! @Override ! public synchronized boolean ready() throws IOException { ! return m_reader.ready(); ! } ! ! @Override ! 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(); ! } ! ! @Override ! 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(); ! } ! ! public void free() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! ! } ! ! public Reader getCharacterStream(long pos, long length) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } } --- 20,231 ---- /** * @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(); ! } } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/Invocation.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/Invocation.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/Invocation.java 2010-04-29 00:34:35.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/Invocation.java 2006-02-11 14:57:50.000000000 +0100 *************** *** 17,165 **** /** * @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(); } --- 17,190 ---- /** * @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(); } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ObjectResultSet.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ObjectResultSet.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ObjectResultSet.java 2010-04-29 00:13:35.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ObjectResultSet.java 2006-02-11 14:57:50.000000000 +0100 *************** *** 6,17 **** */ 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; --- 6,11 ---- *************** *** 22,314 **** import java.sql.SQLWarning; import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; import java.util.Map; /** * @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 ! */ ! @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; ! } ! ! @Override ! 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 ! */ ! @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; } --- 16,430 ---- 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, 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, 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; } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetField.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetField.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetField.java 2010-04-29 00:34:56.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/ResultSetField.java 2006-02-11 14:57:50.000000000 +0100 *************** *** 11,21 **** 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; --- 11,22 ---- 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; *************** *** 23,36 **** /* * 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; --- 24,37 ---- /* * 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; *************** *** 39,94 **** /* * 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; } ! } \ Chybí znak konce řádku na konci souboru --- 40,103 ---- /* * 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; } ! } \ Chybí znak konce řádku na konci souboru diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java 2010-04-29 00:35:26.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIConnection.java 2009-06-18 02:01:03.000000000 +0200 *************** *** 10,32 **** import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; - import java.sql.Array; 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.NClob; import java.sql.PreparedStatement; import java.sql.ResultSet; - import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLWarning; - import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; - import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; --- 10,27 ---- *************** *** 34,40 **** import java.util.Calendar; import java.util.HashMap; import java.util.Map; - import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; --- 29,34 ---- *************** *** 45,557 **** /** * @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]; --- 39,650 ---- /** * @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]; *************** *** 560,593 **** /* * 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(); } } --- 653,693 ---- /* * 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(); } } *************** *** 595,761 **** 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 }; ! ! public Array createArrayOf(String typeName, Object[] elements) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Blob createBlob() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Clob createClob() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public NClob createNClob() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public SQLXML createSQLXML() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Struct createStruct(String typeName, Object[] attributes) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public Properties getClientInfo() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public String getClientInfo(String name) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean isValid(int timeout) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void setClientInfo(Properties properties) throws SQLClientInfoException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public void setClientInfo(String name, String value) throws SQLClientInfoException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean isWrapperFor(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public T unwrap(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! } --- 695,887 ---- 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 ! }; ! } diff -c --recursive ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java *** ./org.postgresql.pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java 2010-04-29 00:34:13.000000000 +0200 --- ./pljava/src/java/pljava/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java 2006-09-25 08:28:31.000000000 +0200 *************** *** 7,13 **** import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; - import java.sql.RowIdLifetime; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; --- 7,12 ---- *************** *** 17,2502 **** 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 = permissions.get(permNames[i]); ! for (int j = 0; j < grantees.size(); j++) { ! String grantee = 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 = permissions.get(permNames[i]); ! for (int j = 0; j < grantees.size(); j++) { ! String grantee = 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 = 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); ! } ! ! public boolean autoCommitFailureClosesAllResultSets() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public ResultSet getClientInfoProperties() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public RowIdLifetime getRowIdLifetime() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public boolean isWrapperFor(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } ! ! public T unwrap(Class iface) throws SQLException { ! throw new UnsupportedOperationException("This method is not implemented yet!"); ! } } --- 16,3451 ---- 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