mirror of https://github.com/postgres/postgres
parent
2a9bf5b33d
commit
b195c10df7
@ -1,6 +0,0 @@ |
||||
.classpath |
||||
.project |
||||
.externalToolBuilders |
||||
build |
||||
build.properties |
||||
jars |
@ -1 +0,0 @@ |
||||
Driver.java |
@ -1,265 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.naming.*; |
||||
import java.io.PrintWriter; |
||||
import java.sql.*; |
||||
|
||||
/** |
||||
* Base class for data sources and related classes. |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @version $Revision: 1.3 $ |
||||
*/ |
||||
public abstract class BaseDataSource implements Referenceable |
||||
{ |
||||
// Load the normal driver, since we'll use it to actually connect to the
|
||||
// database. That way we don't have to maintain the connecting code in
|
||||
// multiple places.
|
||||
static { |
||||
try |
||||
{ |
||||
Class.forName("org.postgresql.Driver"); |
||||
} |
||||
catch (ClassNotFoundException e) |
||||
{ |
||||
System.err.println("PostgreSQL DataSource unable to load PostgreSQL JDBC Driver"); |
||||
} |
||||
} |
||||
|
||||
// Needed to implement the DataSource/ConnectionPoolDataSource interfaces
|
||||
private transient PrintWriter logger; |
||||
// Don't track loginTimeout, since we'd just ignore it anyway
|
||||
|
||||
// Standard properties, defined in the JDBC 2.0 Optional Package spec
|
||||
private String serverName = "localhost"; |
||||
private String databaseName; |
||||
private String user; |
||||
private String password; |
||||
private int portNumber; |
||||
|
||||
/** |
||||
* Gets a connection to the PostgreSQL database. The database is identified by the |
||||
* DataSource properties serverName, databaseName, and portNumber. The user to |
||||
* connect as is identified by the DataSource properties user and password. |
||||
* |
||||
* @return A valid database connection. |
||||
* @throws SQLException |
||||
* Occurs when the database connection cannot be established. |
||||
*/ |
||||
public Connection getConnection() throws SQLException |
||||
{ |
||||
return getConnection(user, password); |
||||
} |
||||
|
||||
/** |
||||
* Gets a connection to the PostgreSQL database. The database is identified by the |
||||
* DataAource properties serverName, databaseName, and portNumber. The user to |
||||
* connect as is identified by the arguments user and password, which override |
||||
* the DataSource properties by the same name. |
||||
* |
||||
* @return A valid database connection. |
||||
* @throws SQLException |
||||
* Occurs when the database connection cannot be established. |
||||
*/ |
||||
public Connection getConnection(String user, String password) throws SQLException |
||||
{ |
||||
try |
||||
{ |
||||
Connection con = DriverManager.getConnection(getUrl(), user, password); |
||||
if (logger != null) |
||||
{ |
||||
logger.println("Created a non-pooled connection for " + user + " at " + getUrl()); |
||||
} |
||||
return con; |
||||
} |
||||
catch (SQLException e) |
||||
{ |
||||
if (logger != null) |
||||
{ |
||||
logger.println("Failed to create a non-pooled connection for " + user + " at " + getUrl() + ": " + e); |
||||
} |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This DataSource does not support a configurable login timeout. |
||||
* @return 0 |
||||
*/ |
||||
public int getLoginTimeout() throws SQLException |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* This DataSource does not support a configurable login timeout. Any value |
||||
* provided here will be ignored. |
||||
*/ |
||||
public void setLoginTimeout(int i) throws SQLException |
||||
{} |
||||
|
||||
/** |
||||
* Gets the log writer used to log connections opened. |
||||
*/ |
||||
public PrintWriter getLogWriter() throws SQLException |
||||
{ |
||||
return logger; |
||||
} |
||||
|
||||
/** |
||||
* The DataSource will note every connection opened to the provided log writer. |
||||
*/ |
||||
public void setLogWriter(PrintWriter printWriter) throws SQLException |
||||
{ |
||||
logger = printWriter; |
||||
} |
||||
|
||||
/** |
||||
* Gets the name of the host the PostgreSQL database is running on. |
||||
*/ |
||||
public String getServerName() |
||||
{ |
||||
return serverName; |
||||
} |
||||
|
||||
/** |
||||
* Sets the name of the host the PostgreSQL database is running on. If this |
||||
* is changed, it will only affect future calls to getConnection. The default |
||||
* value is <tt>localhost</tt>. |
||||
*/ |
||||
public void setServerName(String serverName) |
||||
{ |
||||
if (serverName == null || serverName.equals("")) |
||||
{ |
||||
this.serverName = "localhost"; |
||||
} |
||||
else |
||||
{ |
||||
this.serverName = serverName; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Gets the name of the PostgreSQL database, running on the server identified |
||||
* by the serverName property. |
||||
*/ |
||||
public String getDatabaseName() |
||||
{ |
||||
return databaseName; |
||||
} |
||||
|
||||
/** |
||||
* Sets the name of the PostgreSQL database, running on the server identified |
||||
* by the serverName property. If this is changed, it will only affect |
||||
* future calls to getConnection. |
||||
*/ |
||||
public void setDatabaseName(String databaseName) |
||||
{ |
||||
this.databaseName = databaseName; |
||||
} |
||||
|
||||
/** |
||||
* Gets a description of this DataSource-ish thing. Must be customized by |
||||
* subclasses. |
||||
*/ |
||||
public abstract String getDescription(); |
||||
|
||||
/** |
||||
* Gets the user to connect as by default. If this is not specified, you must |
||||
* use the getConnection method which takes a user and password as parameters. |
||||
*/ |
||||
public String getUser() |
||||
{ |
||||
return user; |
||||
} |
||||
|
||||
/** |
||||
* Sets the user to connect as by default. If this is not specified, you must |
||||
* use the getConnection method which takes a user and password as parameters. |
||||
* If this is changed, it will only affect future calls to getConnection. |
||||
*/ |
||||
public void setUser(String user) |
||||
{ |
||||
this.user = user; |
||||
} |
||||
|
||||
/** |
||||
* Gets the password to connect with by default. If this is not specified but a |
||||
* password is needed to log in, you must use the getConnection method which takes |
||||
* a user and password as parameters. |
||||
*/ |
||||
public String getPassword() |
||||
{ |
||||
return password; |
||||
} |
||||
|
||||
/** |
||||
* Sets the password to connect with by default. If this is not specified but a |
||||
* password is needed to log in, you must use the getConnection method which takes |
||||
* a user and password as parameters. If this is changed, it will only affect |
||||
* future calls to getConnection. |
||||
*/ |
||||
public void setPassword(String password) |
||||
{ |
||||
this.password = password; |
||||
} |
||||
|
||||
/** |
||||
* Gets the port which the PostgreSQL server is listening on for TCP/IP |
||||
* connections. |
||||
* |
||||
* @return The port, or 0 if the default port will be used. |
||||
*/ |
||||
public int getPortNumber() |
||||
{ |
||||
return portNumber; |
||||
} |
||||
|
||||
/** |
||||
* Gets the port which the PostgreSQL server is listening on for TCP/IP |
||||
* connections. Be sure the -i flag is passed to postmaster when PostgreSQL |
||||
* is started. If this is not set, or set to 0, the default port will be used. |
||||
*/ |
||||
public void setPortNumber(int portNumber) |
||||
{ |
||||
this.portNumber = portNumber; |
||||
} |
||||
|
||||
/** |
||||
* Generates a DriverManager URL from the other properties supplied. |
||||
*/ |
||||
private String getUrl() |
||||
{ |
||||
return "jdbc:postgresql://" + serverName + (portNumber == 0 ? "" : ":" + portNumber) + "/" + databaseName; |
||||
} |
||||
|
||||
/** |
||||
* Generates a reference using the appropriate object factory. This |
||||
* implementation uses the JDBC 2 optional package object factory. |
||||
*/ |
||||
protected Reference createReference() |
||||
{ |
||||
return new Reference(getClass().getName(), PGObjectFactory.class.getName(), null); |
||||
} |
||||
|
||||
public Reference getReference() throws NamingException |
||||
{ |
||||
Reference ref = createReference(); |
||||
ref.add(new StringRefAddr("serverName", serverName)); |
||||
if (portNumber != 0) |
||||
{ |
||||
ref.add(new StringRefAddr("portNumber", Integer.toString(portNumber))); |
||||
} |
||||
ref.add(new StringRefAddr("databaseName", databaseName)); |
||||
if (user != null) |
||||
{ |
||||
ref.add(new StringRefAddr("user", user)); |
||||
} |
||||
if (password != null) |
||||
{ |
||||
ref.add(new StringRefAddr("password", password)); |
||||
} |
||||
return ref; |
||||
} |
||||
|
||||
} |
@ -1,82 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.sql.ConnectionPoolDataSource; |
||||
import javax.sql.PooledConnection; |
||||
import java.sql.SQLException; |
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* PostgreSQL implementation of ConnectionPoolDataSource. The app server or |
||||
* middleware vendor should provide a DataSource implementation that takes advantage |
||||
* of this ConnectionPoolDataSource. If not, you can use the PostgreSQL implementation |
||||
* known as PoolingDataSource, but that should only be used if your server or middleware |
||||
* vendor does not provide their own. Why? The server may want to reuse the same |
||||
* Connection across all EJBs requesting a Connection within the same Transaction, or |
||||
* provide other similar advanced features. |
||||
* |
||||
* <p>In any case, in order to use this ConnectionPoolDataSource, you must set the property |
||||
* databaseName. The settings for serverName, portNumber, user, and password are |
||||
* optional. Note: these properties are declared in the superclass.</p> |
||||
* |
||||
* <p>This implementation supports JDK 1.3 and higher.</p> |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @version $Revision: 1.2 $ |
||||
*/ |
||||
public class ConnectionPool extends BaseDataSource implements Serializable, ConnectionPoolDataSource |
||||
{ |
||||
private boolean defaultAutoCommit = false; |
||||
|
||||
/** |
||||
* Gets a description of this DataSource. |
||||
*/ |
||||
public String getDescription() |
||||
{ |
||||
return "ConnectionPoolDataSource from " + org.postgresql.Driver.getVersion(); |
||||
} |
||||
|
||||
/** |
||||
* Gets a connection which may be pooled by the app server or middleware |
||||
* implementation of DataSource. |
||||
* |
||||
* @throws java.sql.SQLException |
||||
* Occurs when the physical database connection cannot be established. |
||||
*/ |
||||
public PooledConnection getPooledConnection() throws SQLException |
||||
{ |
||||
return new PooledConnectionImpl(getConnection(), defaultAutoCommit); |
||||
} |
||||
|
||||
/** |
||||
* Gets a connection which may be pooled by the app server or middleware |
||||
* implementation of DataSource. |
||||
* |
||||
* @throws java.sql.SQLException |
||||
* Occurs when the physical database connection cannot be established. |
||||
*/ |
||||
public PooledConnection getPooledConnection(String user, String password) throws SQLException |
||||
{ |
||||
return new PooledConnectionImpl(getConnection(user, password), defaultAutoCommit); |
||||
} |
||||
|
||||
/** |
||||
* Gets whether connections supplied by this pool will have autoCommit |
||||
* turned on by default. The default value is <tt>false</tt>, so that |
||||
* autoCommit will be turned off by default. |
||||
*/ |
||||
public boolean isDefaultAutoCommit() |
||||
{ |
||||
return defaultAutoCommit; |
||||
} |
||||
|
||||
/** |
||||
* Sets whether connections supplied by this pool will have autoCommit |
||||
* turned on by default. The default value is <tt>false</tt>, so that |
||||
* autoCommit will be turned off by default. |
||||
*/ |
||||
public void setDefaultAutoCommit(boolean defaultAutoCommit) |
||||
{ |
||||
this.defaultAutoCommit = defaultAutoCommit; |
||||
} |
||||
|
||||
} |
@ -1,108 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.naming.spi.ObjectFactory; |
||||
import javax.naming.*; |
||||
import java.util.Hashtable; |
||||
|
||||
/** |
||||
* Returns a DataSource-ish thing based on a JNDI reference. In the case of a |
||||
* SimpleDataSource or ConnectionPool, a new instance is created each time, as |
||||
* there is no connection state to maintain. In the case of a PoolingDataSource, |
||||
* the same DataSource will be returned for every invocation within the same |
||||
* VM/ClassLoader, so that the state of the connections in the pool will be |
||||
* consistent. |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @version $Revision: 1.3 $ |
||||
*/ |
||||
public class PGObjectFactory implements ObjectFactory |
||||
{ |
||||
/** |
||||
* Dereferences a PostgreSQL DataSource. Other types of references are |
||||
* ignored. |
||||
*/ |
||||
public Object getObjectInstance(Object obj, Name name, Context nameCtx, |
||||
Hashtable environment) throws Exception |
||||
{ |
||||
Reference ref = (Reference)obj; |
||||
if (ref.getClassName().equals(SimpleDataSource.class.getName())) |
||||
{ |
||||
return loadSimpleDataSource(ref); |
||||
} |
||||
else if (ref.getClassName().equals(ConnectionPool.class.getName())) |
||||
{ |
||||
return loadConnectionPool(ref); |
||||
} |
||||
else if (ref.getClassName().equals(PoolingDataSource.class.getName())) |
||||
{ |
||||
return loadPoolingDataSource(ref); |
||||
} |
||||
else |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
private Object loadPoolingDataSource(Reference ref) |
||||
{ |
||||
// If DataSource exists, return it
|
||||
String name = getProperty(ref, "dataSourceName"); |
||||
PoolingDataSource pds = PoolingDataSource.getDataSource(name); |
||||
if (pds != null) |
||||
{ |
||||
return pds; |
||||
} |
||||
// Otherwise, create a new one
|
||||
pds = new PoolingDataSource(); |
||||
pds.setDataSourceName(name); |
||||
loadBaseDataSource(pds, ref); |
||||
String min = getProperty(ref, "initialConnections"); |
||||
if (min != null) |
||||
{ |
||||
pds.setInitialConnections(Integer.parseInt(min)); |
||||
} |
||||
String max = getProperty(ref, "maxConnections"); |
||||
if (max != null) |
||||
{ |
||||
pds.setMaxConnections(Integer.parseInt(max)); |
||||
} |
||||
return pds; |
||||
} |
||||
|
||||
private Object loadSimpleDataSource(Reference ref) |
||||
{ |
||||
SimpleDataSource ds = new SimpleDataSource(); |
||||
return loadBaseDataSource(ds, ref); |
||||
} |
||||
|
||||
private Object loadConnectionPool(Reference ref) |
||||
{ |
||||
ConnectionPool cp = new ConnectionPool(); |
||||
return loadBaseDataSource(cp, ref); |
||||
} |
||||
|
||||
protected Object loadBaseDataSource(BaseDataSource ds, Reference ref) |
||||
{ |
||||
ds.setDatabaseName(getProperty(ref, "databaseName")); |
||||
ds.setPassword(getProperty(ref, "password")); |
||||
String port = getProperty(ref, "portNumber"); |
||||
if (port != null) |
||||
{ |
||||
ds.setPortNumber(Integer.parseInt(port)); |
||||
} |
||||
ds.setServerName(getProperty(ref, "serverName")); |
||||
ds.setUser(getProperty(ref, "user")); |
||||
return ds; |
||||
} |
||||
|
||||
protected String getProperty(Reference ref, String s) |
||||
{ |
||||
RefAddr addr = ref.get(s); |
||||
if (addr == null) |
||||
{ |
||||
return null; |
||||
} |
||||
return (String)addr.getContent(); |
||||
} |
||||
|
||||
} |
@ -1,392 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.sql.*; |
||||
import java.sql.*; |
||||
import java.util.*; |
||||
import java.lang.reflect.*; |
||||
import org.postgresql.PGConnection; |
||||
|
||||
/** |
||||
* PostgreSQL implementation of the PooledConnection interface. This shouldn't |
||||
* be used directly, as the pooling client should just interact with the |
||||
* ConnectionPool instead. |
||||
* @see ConnectionPool |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @author Csaba Nagy (ncsaba@yahoo.com) |
||||
* @version $Revision: 1.8 $ |
||||
*/ |
||||
public class PooledConnectionImpl implements PooledConnection |
||||
{ |
||||
private List listeners = new LinkedList(); |
||||
private Connection con; |
||||
private ConnectionHandler last; |
||||
private boolean autoCommit; |
||||
|
||||
/** |
||||
* Creates a new PooledConnection representing the specified physical |
||||
* connection. |
||||
*/ |
||||
protected PooledConnectionImpl(Connection con, boolean autoCommit) |
||||
{ |
||||
this.con = con; |
||||
this.autoCommit = autoCommit; |
||||
} |
||||
|
||||
/** |
||||
* Adds a listener for close or fatal error events on the connection |
||||
* handed out to a client. |
||||
*/ |
||||
public void addConnectionEventListener(ConnectionEventListener connectionEventListener) |
||||
{ |
||||
listeners.add(connectionEventListener); |
||||
} |
||||
|
||||
/** |
||||
* Removes a listener for close or fatal error events on the connection |
||||
* handed out to a client. |
||||
*/ |
||||
public void removeConnectionEventListener(ConnectionEventListener connectionEventListener) |
||||
{ |
||||
listeners.remove(connectionEventListener); |
||||
} |
||||
|
||||
/** |
||||
* Closes the physical database connection represented by this |
||||
* PooledConnection. If any client has a connection based on |
||||
* this PooledConnection, it is forcibly closed as well. |
||||
*/ |
||||
public void close() throws SQLException |
||||
{ |
||||
if (last != null) |
||||
{ |
||||
last.close(); |
||||
if (!con.getAutoCommit()) |
||||
{ |
||||
try |
||||
{ |
||||
con.rollback(); |
||||
} |
||||
catch (SQLException e) |
||||
{} |
||||
} |
||||
} |
||||
try |
||||
{ |
||||
con.close(); |
||||
} |
||||
finally |
||||
{ |
||||
con = null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Gets a handle for a client to use. This is a wrapper around the |
||||
* physical connection, so the client can call close and it will just |
||||
* return the connection to the pool without really closing the |
||||
* pgysical connection. |
||||
* |
||||
* <p>According to the JDBC 2.0 Optional Package spec (6.2.3), only one |
||||
* client may have an active handle to the connection at a time, so if |
||||
* there is a previous handle active when this is called, the previous |
||||
* one is forcibly closed and its work rolled back.</p> |
||||
*/ |
||||
public Connection getConnection() throws SQLException |
||||
{ |
||||
if (con == null) |
||||
{ |
||||
// Before throwing the exception, let's notify the registered listeners about the error
|
||||
final SQLException sqlException = new SQLException("This PooledConnection has already been closed!"); |
||||
fireConnectionFatalError(sqlException); |
||||
throw sqlException; |
||||
} |
||||
// If any error occures while opening a new connection, the listeners
|
||||
// have to be notified. This gives a chance to connection pools to
|
||||
// elliminate bad pooled connections.
|
||||
try |
||||
{ |
||||
// Only one connection can be open at a time from this PooledConnection. See JDBC 2.0 Optional Package spec section 6.2.3
|
||||
if (last != null) |
||||
{ |
||||
last.close(); |
||||
if (!con.getAutoCommit()) |
||||
{ |
||||
try |
||||
{ |
||||
con.rollback(); |
||||
} |
||||
catch (SQLException e) |
||||
{} |
||||
} |
||||
con.clearWarnings(); |
||||
} |
||||
con.setAutoCommit(autoCommit); |
||||
} |
||||
catch (SQLException sqlException) |
||||
{ |
||||
fireConnectionFatalError(sqlException); |
||||
throw (SQLException)sqlException.fillInStackTrace(); |
||||
} |
||||
ConnectionHandler handler = new ConnectionHandler(con); |
||||
last = handler; |
||||
Connection con = (Connection)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class, PGConnection.class}, handler); |
||||
last.setProxy(con); |
||||
return con; |
||||
} |
||||
|
||||
/** |
||||
* Used to fire a connection closed event to all listeners. |
||||
*/ |
||||
void fireConnectionClosed() |
||||
{ |
||||
ConnectionEvent evt = null; |
||||
// Copy the listener list so the listener can remove itself during this method call
|
||||
ConnectionEventListener[] local = (ConnectionEventListener[]) listeners.toArray(new ConnectionEventListener[listeners.size()]); |
||||
for (int i = 0; i < local.length; i++) |
||||
{ |
||||
ConnectionEventListener listener = local[i]; |
||||
if (evt == null) |
||||
{ |
||||
evt = new ConnectionEvent(this); |
||||
} |
||||
listener.connectionClosed(evt); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Used to fire a connection error event to all listeners. |
||||
*/ |
||||
void fireConnectionFatalError(SQLException e) |
||||
{ |
||||
ConnectionEvent evt = null; |
||||
// Copy the listener list so the listener can remove itself during this method call
|
||||
ConnectionEventListener[] local = (ConnectionEventListener[])listeners.toArray(new ConnectionEventListener[listeners.size()]); |
||||
for (int i = 0; i < local.length; i++) |
||||
{ |
||||
ConnectionEventListener listener = local[i]; |
||||
if (evt == null) |
||||
{ |
||||
evt = new ConnectionEvent(this, e); |
||||
} |
||||
listener.connectionErrorOccurred(evt); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Instead of declaring a class implementing Connection, which would have |
||||
* to be updated for every JDK rev, use a dynamic proxy to handle all |
||||
* calls through the Connection interface. This is the part that |
||||
* requires JDK 1.3 or higher, though JDK 1.2 could be supported with a |
||||
* 3rd-party proxy package. |
||||
*/ |
||||
private class ConnectionHandler implements InvocationHandler |
||||
{ |
||||
private Connection con; |
||||
private Connection proxy; // the Connection the client is currently using, which is a proxy
|
||||
private boolean automatic = false; |
||||
|
||||
public ConnectionHandler(Connection con) |
||||
{ |
||||
this.con = con; |
||||
} |
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) |
||||
throws Throwable |
||||
{ |
||||
// From Object
|
||||
if (method.getDeclaringClass().getName().equals("java.lang.Object")) |
||||
{ |
||||
if (method.getName().equals("toString")) |
||||
{ |
||||
return "Pooled connection wrapping physical connection " + con; |
||||
} |
||||
if (method.getName().equals("hashCode")) |
||||
{ |
||||
return new Integer(con.hashCode()); |
||||
} |
||||
if (method.getName().equals("equals")) |
||||
{ |
||||
if (args[0] == null) |
||||
{ |
||||
return Boolean.FALSE; |
||||
} |
||||
try |
||||
{ |
||||
return Proxy.isProxyClass(args[0].getClass()) && ((ConnectionHandler) Proxy.getInvocationHandler(args[0])).con == con ? Boolean.TRUE : Boolean.FALSE; |
||||
} |
||||
catch (ClassCastException e) |
||||
{ |
||||
return Boolean.FALSE; |
||||
} |
||||
} |
||||
try |
||||
{ |
||||
return method.invoke(con, args); |
||||
} |
||||
catch (InvocationTargetException e) |
||||
{ |
||||
throw e.getTargetException(); |
||||
} |
||||
} |
||||
// All the rest is from the Connection or PGConnection interface
|
||||
if (method.getName().equals("isClosed")) |
||||
{ |
||||
return con == null ? Boolean.TRUE : Boolean.FALSE; |
||||
} |
||||
if (con == null) |
||||
{ |
||||
throw new SQLException(automatic ? "Connection has been closed automatically because a new connection was opened for the same PooledConnection or the PooledConnection has been closed" : "Connection has been closed"); |
||||
} |
||||
if (method.getName().equals("close")) |
||||
{ |
||||
SQLException ex = null; |
||||
if (!con.getAutoCommit()) |
||||
{ |
||||
try |
||||
{ |
||||
con.rollback(); |
||||
} |
||||
catch (SQLException e) |
||||
{ |
||||
ex = e; |
||||
} |
||||
} |
||||
con.clearWarnings(); |
||||
con = null; |
||||
proxy = null; |
||||
last = null; |
||||
fireConnectionClosed(); |
||||
if (ex != null) |
||||
{ |
||||
throw ex; |
||||
} |
||||
return null; |
||||
} |
||||
else if(method.getName().equals("createStatement")) |
||||
{ |
||||
Statement st = (Statement)method.invoke(con, args); |
||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Statement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st)); |
||||
} |
||||
else if(method.getName().equals("prepareCall")) |
||||
{ |
||||
Statement st = (Statement)method.invoke(con, args); |
||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{CallableStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st)); |
||||
} |
||||
else if(method.getName().equals("prepareStatement")) |
||||
{ |
||||
Statement st = (Statement)method.invoke(con, args); |
||||
return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{PreparedStatement.class, org.postgresql.PGStatement.class}, new StatementHandler(this, st)); |
||||
} |
||||
else |
||||
{ |
||||
return method.invoke(con, args); |
||||
} |
||||
} |
||||
|
||||
Connection getProxy() { |
||||
return proxy; |
||||
} |
||||
|
||||
void setProxy(Connection proxy) { |
||||
this.proxy = proxy; |
||||
} |
||||
|
||||
public void close() |
||||
{ |
||||
if (con != null) |
||||
{ |
||||
automatic = true; |
||||
} |
||||
con = null; |
||||
proxy = null; |
||||
// No close event fired here: see JDBC 2.0 Optional Package spec section 6.3
|
||||
} |
||||
|
||||
public boolean isClosed() { |
||||
return con == null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Instead of declaring classes implementing Statement, PreparedStatement, |
||||
* and CallableStatement, which would have to be updated for every JDK rev, |
||||
* use a dynamic proxy to handle all calls through the Statement |
||||
* interfaces. This is the part that requires JDK 1.3 or higher, though |
||||
* JDK 1.2 could be supported with a 3rd-party proxy package. |
||||
* |
||||
* The StatementHandler is required in order to return the proper |
||||
* Connection proxy for the getConnection method. |
||||
*/ |
||||
private static class StatementHandler implements InvocationHandler { |
||||
private ConnectionHandler con; |
||||
private Statement st; |
||||
|
||||
public StatementHandler(ConnectionHandler con, Statement st) { |
||||
this.con = con; |
||||
this.st = st; |
||||
} |
||||
public Object invoke(Object proxy, Method method, Object[] args) |
||||
throws Throwable |
||||
{ |
||||
// From Object
|
||||
if (method.getDeclaringClass().getName().equals("java.lang.Object")) |
||||
{ |
||||
if (method.getName().equals("toString")) |
||||
{ |
||||
return "Pooled statement wrapping physical statement " + st; |
||||
} |
||||
if (method.getName().equals("hashCode")) |
||||
{ |
||||
return new Integer(st.hashCode()); |
||||
} |
||||
if (method.getName().equals("equals")) |
||||
{ |
||||
if (args[0] == null) |
||||
{ |
||||
return Boolean.FALSE; |
||||
} |
||||
try |
||||
{ |
||||
return Proxy.isProxyClass(args[0].getClass()) && ((StatementHandler) Proxy.getInvocationHandler(args[0])).st == st ? Boolean.TRUE : Boolean.FALSE; |
||||
} |
||||
catch (ClassCastException e) |
||||
{ |
||||
return Boolean.FALSE; |
||||
} |
||||
} |
||||
return method.invoke(st, args); |
||||
} |
||||
// All the rest is from the Statement interface
|
||||
if (st == null || con.isClosed()) |
||||
{ |
||||
throw new SQLException("Statement has been closed"); |
||||
} |
||||
if (method.getName().equals("close")) |
||||
{ |
||||
try { |
||||
st.close(); |
||||
} finally { |
||||
con = null; |
||||
st = null; |
||||
return null; |
||||
} |
||||
} |
||||
else if (method.getName().equals("getConnection")) |
||||
{ |
||||
return con.getProxy(); // the proxied connection, not a physical connection
|
||||
} |
||||
else |
||||
{ |
||||
try |
||||
{ |
||||
return method.invoke(st, args); |
||||
} |
||||
catch (InvocationTargetException e) |
||||
{ |
||||
throw e.getTargetException(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,494 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.sql.*; |
||||
import javax.naming.*; |
||||
import java.util.*; |
||||
import java.sql.Connection; |
||||
import java.sql.SQLException; |
||||
|
||||
/** |
||||
* DataSource which uses connection pooling. <font color="red">Don't use this if |
||||
* your server/middleware vendor provides a connection pooling implementation |
||||
* which interfaces with the PostgreSQL ConnectionPoolDataSource implementation!</font> |
||||
* This class is provided as a convenience, but the JDBC Driver is really not |
||||
* supposed to handle the connection pooling algorithm. Instead, the server or |
||||
* middleware product is supposed to handle the mechanics of connection pooling, |
||||
* and use the PostgreSQL implementation of ConnectionPoolDataSource to provide |
||||
* the connections to pool. |
||||
* |
||||
* <p>If you're sure you want to use this, then you must set the properties |
||||
* dataSourceName, databaseName, user, and password (if required for the user). |
||||
* The settings for serverName, portNumber, initialConnections, and |
||||
* maxConnections are optional. Note that <i>only connections |
||||
* for the default user will be pooled!</i> Connections for other users will |
||||
* be normal non-pooled connections, and will not count against the maximum pool |
||||
* size limit.</p> |
||||
* |
||||
* <p>If you put this DataSource in JNDI, and access it from different JVMs (or |
||||
* otherwise load this class from different ClassLoaders), you'll end up with one |
||||
* pool per ClassLoader or VM. This is another area where a server-specific |
||||
* implementation may provide advanced features, such as using a single pool |
||||
* across all VMs in a cluster.</p> |
||||
* |
||||
* <p>This implementation supports JDK 1.3 and higher.</p> |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @version $Revision: 1.3 $ |
||||
*/ |
||||
public class PoolingDataSource extends BaseDataSource implements DataSource |
||||
{ |
||||
private static Map dataSources = new HashMap(); |
||||
|
||||
static PoolingDataSource getDataSource(String name) |
||||
{ |
||||
return (PoolingDataSource)dataSources.get(name); |
||||
} |
||||
|
||||
// Additional Data Source properties
|
||||
protected String dataSourceName; // Must be protected for subclasses to sync updates to it
|
||||
private int initialConnections = 0; |
||||
private int maxConnections = 0; |
||||
// State variables
|
||||
private boolean initialized = false; |
||||
private Stack available = new Stack(); |
||||
private Stack used = new Stack(); |
||||
private Object lock = new Object(); |
||||
private ConnectionPool source; |
||||
|
||||
/** |
||||
* Gets a description of this DataSource. |
||||
*/ |
||||
public String getDescription() |
||||
{ |
||||
return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.Driver.getVersion(); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the DataSource properties are not changed after the DataSource has |
||||
* been used. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Server Name cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setServerName(String serverName) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
super.setServerName(serverName); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the DataSource properties are not changed after the DataSource has |
||||
* been used. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Database Name cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setDatabaseName(String databaseName) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
super.setDatabaseName(databaseName); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the DataSource properties are not changed after the DataSource has |
||||
* been used. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The User cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setUser(String user) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
super.setUser(user); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the DataSource properties are not changed after the DataSource has |
||||
* been used. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Password cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setPassword(String password) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
super.setPassword(password); |
||||
} |
||||
|
||||
/** |
||||
* Ensures the DataSource properties are not changed after the DataSource has |
||||
* been used. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Port Number cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setPortNumber(int portNumber) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
super.setPortNumber(portNumber); |
||||
} |
||||
|
||||
/** |
||||
* Gets the number of connections that will be created when this DataSource |
||||
* is initialized. If you do not call initialize explicitly, it will be |
||||
* initialized the first time a connection is drawn from it. |
||||
*/ |
||||
public int getInitialConnections() |
||||
{ |
||||
return initialConnections; |
||||
} |
||||
|
||||
/** |
||||
* Sets the number of connections that will be created when this DataSource |
||||
* is initialized. If you do not call initialize explicitly, it will be |
||||
* initialized the first time a connection is drawn from it. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Initial Connections cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setInitialConnections(int initialConnections) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
this.initialConnections = initialConnections; |
||||
} |
||||
|
||||
/** |
||||
* Gets the maximum number of connections that the pool will allow. If a request |
||||
* comes in and this many connections are in use, the request will block until a |
||||
* connection is available. Note that connections for a user other than the |
||||
* default user will not be pooled and don't count against this limit. |
||||
* |
||||
* @return The maximum number of pooled connection allowed, or 0 for no maximum. |
||||
*/ |
||||
public int getMaxConnections() |
||||
{ |
||||
return maxConnections; |
||||
} |
||||
|
||||
/** |
||||
* Sets the maximum number of connections that the pool will allow. If a request |
||||
* comes in and this many connections are in use, the request will block until a |
||||
* connection is available. Note that connections for a user other than the |
||||
* default user will not be pooled and don't count against this limit. |
||||
* |
||||
* @param maxConnections The maximum number of pooled connection to allow, or |
||||
* 0 for no maximum. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Maximum Connections cannot be changed after the DataSource has been |
||||
* used. |
||||
*/ |
||||
public void setMaxConnections(int maxConnections) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
this.maxConnections = maxConnections; |
||||
} |
||||
|
||||
/** |
||||
* Gets the name of this DataSource. This uniquely identifies the DataSource. |
||||
* You cannot use more than one DataSource in the same VM with the same name. |
||||
*/ |
||||
public String getDataSourceName() |
||||
{ |
||||
return dataSourceName; |
||||
} |
||||
|
||||
/** |
||||
* Sets the name of this DataSource. This is required, and uniquely identifies |
||||
* the DataSource. You cannot create or use more than one DataSource in the |
||||
* same VM with the same name. |
||||
* |
||||
* @throws java.lang.IllegalStateException |
||||
* The Data Source Name cannot be changed after the DataSource has been |
||||
* used. |
||||
* @throws java.lang.IllegalArgumentException |
||||
* Another PoolingDataSource with the same dataSourceName already |
||||
* exists. |
||||
*/ |
||||
public void setDataSourceName(String dataSourceName) |
||||
{ |
||||
if (initialized) |
||||
{ |
||||
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used"); |
||||
} |
||||
if (this.dataSourceName != null && dataSourceName != null && dataSourceName.equals(this.dataSourceName)) |
||||
{ |
||||
return ; |
||||
} |
||||
synchronized (dataSources) |
||||
{ |
||||
if (getDataSource(dataSourceName) != null) |
||||
{ |
||||
throw new IllegalArgumentException("DataSource with name '" + dataSourceName + "' already exists!"); |
||||
} |
||||
if (this.dataSourceName != null) |
||||
{ |
||||
dataSources.remove(this.dataSourceName); |
||||
} |
||||
this.dataSourceName = dataSourceName; |
||||
dataSources.put(dataSourceName, this); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Initializes this DataSource. If the initialConnections is greater than zero, |
||||
* that number of connections will be created. After this method is called, |
||||
* the DataSource properties cannot be changed. If you do not call this |
||||
* explicitly, it will be called the first time you get a connection from the |
||||
* DataSource. |
||||
* @throws java.sql.SQLException |
||||
* Occurs when the initialConnections is greater than zero, but the |
||||
* DataSource is not able to create enough physical connections. |
||||
*/ |
||||
public void initialize() throws SQLException |
||||
{ |
||||
synchronized (lock) |
||||
{ |
||||
source = createConnectionPool(); |
||||
source.setDatabaseName(getDatabaseName()); |
||||
source.setPassword(getPassword()); |
||||
source.setPortNumber(getPortNumber()); |
||||
source.setServerName(getServerName()); |
||||
source.setUser(getUser()); |
||||
while (available.size() < initialConnections) |
||||
{ |
||||
available.push(source.getPooledConnection()); |
||||
} |
||||
initialized = true; |
||||
} |
||||
} |
||||
|
||||
protected boolean isInitialized() { |
||||
return initialized; |
||||
} |
||||
|
||||
/** |
||||
* Creates the appropriate ConnectionPool to use for this DataSource. |
||||
*/ |
||||
protected ConnectionPool createConnectionPool() { |
||||
return new ConnectionPool(); |
||||
} |
||||
|
||||
/** |
||||
* Gets a <b>non-pooled</b> connection, unless the user and password are the |
||||
* same as the default values for this connection pool. |
||||
* |
||||
* @return A pooled connection. |
||||
* @throws SQLException |
||||
* Occurs when no pooled connection is available, and a new physical |
||||
* connection cannot be created. |
||||
*/ |
||||
public Connection getConnection(String user, String password) throws SQLException |
||||
{ |
||||
// If this is for the default user/password, use a pooled connection
|
||||
if (user == null || |
||||
(user.equals(getUser()) && ((password == null && getPassword() == null) || (password != null && password.equals(getPassword()))))) |
||||
{ |
||||
return getConnection(); |
||||
} |
||||
// Otherwise, use a non-pooled connection
|
||||
if (!initialized) |
||||
{ |
||||
initialize(); |
||||
} |
||||
return super.getConnection(user, password); |
||||
} |
||||
|
||||
/** |
||||
* Gets a connection from the connection pool. |
||||
* |
||||
* @return A pooled connection. |
||||
* @throws SQLException |
||||
* Occurs when no pooled connection is available, and a new physical |
||||
* connection cannot be created. |
||||
*/ |
||||
public Connection getConnection() throws SQLException |
||||
{ |
||||
if (!initialized) |
||||
{ |
||||
initialize(); |
||||
} |
||||
return getPooledConnection(); |
||||
} |
||||
|
||||
/** |
||||
* Closes this DataSource, and all the pooled connections, whether in use or not. |
||||
*/ |
||||
public void close() |
||||
{ |
||||
synchronized (lock) |
||||
{ |
||||
while (available.size() > 0) |
||||
{ |
||||
PooledConnectionImpl pci = (PooledConnectionImpl)available.pop(); |
||||
try |
||||
{ |
||||
pci.close(); |
||||
} |
||||
catch (SQLException e) |
||||
{} |
||||
} |
||||
available = null; |
||||
while (used.size() > 0) |
||||
{ |
||||
PooledConnectionImpl pci = (PooledConnectionImpl)used.pop(); |
||||
pci.removeConnectionEventListener(connectionEventListener); |
||||
try |
||||
{ |
||||
pci.close(); |
||||
} |
||||
catch (SQLException e) |
||||
{} |
||||
} |
||||
used = null; |
||||
} |
||||
removeStoredDataSource(); |
||||
} |
||||
|
||||
protected void removeStoredDataSource() { |
||||
synchronized (dataSources) |
||||
{ |
||||
dataSources.remove(dataSourceName); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Gets a connection from the pool. Will get an available one if |
||||
* present, or create a new one if under the max limit. Will |
||||
* block if all used and a new one would exceed the max. |
||||
*/ |
||||
private Connection getPooledConnection() throws SQLException |
||||
{ |
||||
PooledConnection pc = null; |
||||
synchronized (lock) |
||||
{ |
||||
if (available == null) |
||||
{ |
||||
throw new SQLException("DataSource has been closed."); |
||||
} |
||||
while (true) |
||||
{ |
||||
if (available.size() > 0) |
||||
{ |
||||
pc = (PooledConnection)available.pop(); |
||||
used.push(pc); |
||||
break; |
||||
} |
||||
if (maxConnections == 0 || used.size() < maxConnections) |
||||
{ |
||||
pc = source.getPooledConnection(); |
||||
used.push(pc); |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
try |
||||
{ |
||||
// Wake up every second at a minimum
|
||||
lock.wait(1000L); |
||||
} |
||||
catch (InterruptedException e) |
||||
{} |
||||
} |
||||
} |
||||
} |
||||
pc.addConnectionEventListener(connectionEventListener); |
||||
return pc.getConnection(); |
||||
} |
||||
|
||||
/** |
||||
* Notified when a pooled connection is closed, or a fatal error occurs |
||||
* on a pooled connection. This is the only way connections are marked |
||||
* as unused. |
||||
*/ |
||||
private ConnectionEventListener connectionEventListener = new ConnectionEventListener() |
||||
{ |
||||
public void connectionClosed(ConnectionEvent event) |
||||
{ |
||||
((PooledConnection)event.getSource()).removeConnectionEventListener(this); |
||||
synchronized (lock) |
||||
{ |
||||
if (available == null) |
||||
{ |
||||
return ; // DataSource has been closed
|
||||
} |
||||
boolean removed = used.remove(event.getSource()); |
||||
if (removed) |
||||
{ |
||||
available.push(event.getSource()); |
||||
// There's now a new connection available
|
||||
lock.notify(); |
||||
} |
||||
else |
||||
{ |
||||
// a connection error occured
|
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This is only called for fatal errors, where the physical connection is |
||||
* useless afterward and should be removed from the pool. |
||||
*/ |
||||
public void connectionErrorOccurred(ConnectionEvent event) |
||||
{ |
||||
((PooledConnection) event.getSource()).removeConnectionEventListener(this); |
||||
synchronized (lock) |
||||
{ |
||||
if (available == null) |
||||
{ |
||||
return ; // DataSource has been closed
|
||||
} |
||||
used.remove(event.getSource()); |
||||
// We're now at least 1 connection under the max
|
||||
lock.notify(); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
/** |
||||
* Adds custom properties for this DataSource to the properties defined in |
||||
* the superclass. |
||||
*/ |
||||
public Reference getReference() throws NamingException |
||||
{ |
||||
Reference ref = super.getReference(); |
||||
ref.add(new StringRefAddr("dataSourceName", dataSourceName)); |
||||
if (initialConnections > 0) |
||||
{ |
||||
ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections))); |
||||
} |
||||
if (maxConnections > 0) |
||||
{ |
||||
ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections))); |
||||
} |
||||
return ref; |
||||
} |
||||
} |
@ -1,24 +0,0 @@ |
||||
package org.postgresql.jdbc2.optional; |
||||
|
||||
import javax.sql.DataSource; |
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* Simple DataSource which does not perform connection pooling. In order to use |
||||
* the DataSource, you must set the property databaseName. The settings for |
||||
* serverName, portNumber, user, and password are optional. Note: these properties |
||||
* are declared in the superclass. |
||||
* |
||||
* @author Aaron Mulder (ammulder@chariotsolutions.com) |
||||
* @version $Revision: 1.2 $ |
||||
*/ |
||||
public class SimpleDataSource extends BaseDataSource implements Serializable, DataSource |
||||
{ |
||||
/** |
||||
* Gets a description of this DataSource. |
||||
*/ |
||||
public String getDescription() |
||||
{ |
||||
return "Non-Pooling DataSource from " + org.postgresql.Driver.getVersion(); |
||||
} |
||||
} |
@ -1,194 +0,0 @@ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* BlobInputStream.java |
||||
* This is an implementation of an InputStream from a large object. |
||||
* |
||||
* Copyright (c) 2003, PostgreSQL Global Development Group |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/BlobInputStream.java,v 1.6 2003/11/29 19:52:11 pgsql Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
package org.postgresql.largeobject; |
||||
|
||||
import java.io.InputStream; |
||||
import java.io.IOException; |
||||
import java.sql.SQLException; |
||||
|
||||
public class BlobInputStream extends InputStream |
||||
{ |
||||
/* |
||||
* The parent LargeObject |
||||
*/ |
||||
private LargeObject lo; |
||||
|
||||
/* |
||||
* Buffer used to improve performance |
||||
*/ |
||||
private byte[] buffer; |
||||
|
||||
/* |
||||
* Position within buffer |
||||
*/ |
||||
private int bpos; |
||||
|
||||
/* |
||||
* The buffer size |
||||
*/ |
||||
private int bsize; |
||||
|
||||
/* |
||||
* The mark position |
||||
*/ |
||||
private int mpos = 0; |
||||
|
||||
/* |
||||
* @param lo LargeObject to read from |
||||
*/ |
||||
public BlobInputStream(LargeObject lo) |
||||
{ |
||||
this(lo, 1024); |
||||
} |
||||
|
||||
/* |
||||
* @param lo LargeObject to read from |
||||
* @param bsize buffer size |
||||
*/ |
||||
public BlobInputStream(LargeObject lo, int bsize) |
||||
{ |
||||
this.lo = lo; |
||||
buffer = null; |
||||
bpos = 0; |
||||
this.bsize = bsize; |
||||
} |
||||
|
||||
/* |
||||
* The minimum required to implement input stream |
||||
*/ |
||||
public int read() throws java.io.IOException |
||||
{ |
||||
try |
||||
{ |
||||
if (buffer == null || bpos >= buffer.length) |
||||
{ |
||||
buffer = lo.read(bsize); |
||||
bpos = 0; |
||||
} |
||||
|
||||
// Handle EOF
|
||||
if (bpos >= buffer.length) |
||||
{ |
||||
return -1; |
||||
} |
||||
|
||||
int ret = (buffer[bpos] & 0x7F); |
||||
if ((buffer[bpos] &0x80) == 0x80) |
||||
{ |
||||
ret |= 0x80; |
||||
} |
||||
|
||||
bpos++; |
||||
|
||||
return ret; |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
|
||||
/* |
||||
* Closes this input stream and releases any system resources associated |
||||
* with the stream. |
||||
* |
||||
* <p> The <code>close</code> method of <code>InputStream</code> does |
||||
* nothing. |
||||
* |
||||
* @exception IOException if an I/O error occurs. |
||||
*/ |
||||
public void close() throws IOException |
||||
{ |
||||
try |
||||
{ |
||||
lo.close(); |
||||
lo = null; |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Marks the current position in this input stream. A subsequent call to |
||||
* the <code>reset</code> method repositions this stream at the last marked |
||||
* position so that subsequent reads re-read the same bytes. |
||||
* |
||||
* <p> The <code>readlimit</code> arguments tells this input stream to |
||||
* allow that many bytes to be read before the mark position gets |
||||
* invalidated. |
||||
* |
||||
* <p> The general contract of <code>mark</code> is that, if the method |
||||
* <code>markSupported</code> returns <code>true</code>, the stream somehow |
||||
* remembers all the bytes read after the call to <code>mark</code> and |
||||
* stands ready to supply those same bytes again if and whenever the method |
||||
* <code>reset</code> is called. However, the stream is not required to |
||||
* remember any data at all if more than <code>readlimit</code> bytes are |
||||
* read from the stream before <code>reset</code> is called. |
||||
* |
||||
* <p> The <code>mark</code> method of <code>InputStream</code> does |
||||
* nothing. |
||||
* |
||||
* @param readlimit the maximum limit of bytes that can be read before |
||||
* the mark position becomes invalid. |
||||
* @see java.io.InputStream#reset() |
||||
*/ |
||||
public synchronized void mark(int readlimit) |
||||
{ |
||||
try |
||||
{ |
||||
mpos = lo.tell(); |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
//throw new IOException(se.toString());
|
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Repositions this stream to the position at the time the |
||||
* <code>mark</code> method was last called on this input stream. |
||||
* NB: If mark is not called we move to the begining. |
||||
* @see java.io.InputStream#mark(int) |
||||
* @see java.io.IOException |
||||
*/ |
||||
public synchronized void reset() |
||||
throws IOException |
||||
{ |
||||
try |
||||
{ |
||||
lo.seek(mpos); |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Tests if this input stream supports the <code>mark</code> and |
||||
* <code>reset</code> methods. The <code>markSupported</code> method of |
||||
* <code>InputStream</code> returns <code>false</code>. |
||||
* |
||||
* @return <code>true</code> if this true type supports the mark and reset |
||||
* method; <code>false</code> otherwise. |
||||
* @see java.io.InputStream#mark(int) |
||||
* @see java.io.InputStream#reset() |
||||
*/ |
||||
public boolean markSupported() |
||||
{ |
||||
return true; |
||||
} |
||||
} |
@ -1,148 +0,0 @@ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* BlobOutputStream.java |
||||
* This implements a basic output stream that writes to a LargeObject |
||||
* |
||||
* Copyright (c) 2003, PostgreSQL Global Development Group |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java,v 1.7 2003/11/29 19:52:11 pgsql Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
package org.postgresql.largeobject; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.sql.SQLException; |
||||
|
||||
public class BlobOutputStream extends OutputStream |
||||
{ |
||||
/* |
||||
* The parent LargeObject |
||||
*/ |
||||
private LargeObject lo; |
||||
|
||||
/* |
||||
* Buffer |
||||
*/ |
||||
private byte buf[]; |
||||
|
||||
/* |
||||
* Size of the buffer (default 1K) |
||||
*/ |
||||
private int bsize; |
||||
|
||||
/* |
||||
* Position within the buffer |
||||
*/ |
||||
private int bpos; |
||||
|
||||
/* |
||||
* Create an OutputStream to a large object |
||||
* @param lo LargeObject |
||||
*/ |
||||
public BlobOutputStream(LargeObject lo) |
||||
{ |
||||
this(lo, 1024); |
||||
} |
||||
|
||||
/* |
||||
* Create an OutputStream to a large object |
||||
* @param lo LargeObject |
||||
* @param bsize The size of the buffer used to improve performance |
||||
*/ |
||||
public BlobOutputStream(LargeObject lo, int bsize) |
||||
{ |
||||
this.lo = lo; |
||||
this.bsize = bsize; |
||||
buf = new byte[bsize]; |
||||
bpos = 0; |
||||
} |
||||
|
||||
public void write(int b) throws java.io.IOException |
||||
{ |
||||
try |
||||
{ |
||||
if (bpos >= bsize) |
||||
{ |
||||
lo.write(buf); |
||||
bpos = 0; |
||||
} |
||||
buf[bpos++] = (byte)b; |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
public void write(byte[] buf, int off, int len) throws java.io.IOException |
||||
{ |
||||
try |
||||
{ |
||||
// If we have any internally buffered data, send it first
|
||||
if ( bpos > 0 ) |
||||
flush(); |
||||
|
||||
if ( off == 0 && len == buf.length ) |
||||
lo.write(buf); // save a buffer creation and copy since full buffer written
|
||||
else |
||||
lo.write(buf,off,len); |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
|
||||
/* |
||||
* Flushes this output stream and forces any buffered output bytes |
||||
* to be written out. The general contract of <code>flush</code> is |
||||
* that calling it is an indication that, if any bytes previously |
||||
* written have been buffered by the implementation of the output |
||||
* stream, such bytes should immediately be written to their |
||||
* intended destination. |
||||
* |
||||
* @exception IOException if an I/O error occurs. |
||||
*/ |
||||
public void flush() throws IOException |
||||
{ |
||||
try |
||||
{ |
||||
if (bpos > 0) |
||||
lo.write(buf, 0, bpos); |
||||
bpos = 0; |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Closes this output stream and releases any system resources |
||||
* associated with this stream. The general contract of <code>close</code> |
||||
* is that it closes the output stream. A closed stream cannot perform |
||||
* output operations and cannot be reopened. |
||||
* <p> |
||||
* The <code>close</code> method of <code>OutputStream</code> does nothing. |
||||
* |
||||
* @exception IOException if an I/O error occurs. |
||||
*/ |
||||
public void close() throws IOException |
||||
{ |
||||
try |
||||
{ |
||||
flush(); |
||||
lo.close(); |
||||
lo = null; |
||||
} |
||||
catch (SQLException se) |
||||
{ |
||||
throw new IOException(se.toString()); |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,328 +0,0 @@ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* LargeObject.java |
||||
* This class implements the large object interface to org.postgresql. |
||||
* |
||||
* Copyright (c) 2003, PostgreSQL Global Development Group |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java,v 1.11 2003/11/29 19:52:11 pgsql Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
package org.postgresql.largeobject; |
||||
|
||||
import java.io.InputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.sql.SQLException; |
||||
import org.postgresql.fastpath.Fastpath; |
||||
import org.postgresql.fastpath.FastpathArg; |
||||
|
||||
/* |
||||
* This class provides the basic methods required to run the interface, plus |
||||
* a pair of methods that provide InputStream and OutputStream classes |
||||
* for this object. |
||||
* |
||||
* <p>Normally, client code would use the getAsciiStream, getBinaryStream, |
||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream, |
||||
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to |
||||
* access Large Objects. |
||||
* |
||||
* <p>However, sometimes lower level access to Large Objects are required, |
||||
* that are not supported by the JDBC specification. |
||||
* |
||||
* <p>Refer to org.postgresql.largeobject.LargeObjectManager on how to gain access |
||||
* to a Large Object, or how to create one. |
||||
* |
||||
* @see org.postgresql.largeobject.LargeObjectManager |
||||
* @see java.sql.ResultSet#getAsciiStream |
||||
* @see java.sql.ResultSet#getBinaryStream |
||||
* @see java.sql.ResultSet#getUnicodeStream |
||||
* @see java.sql.PreparedStatement#setAsciiStream |
||||
* @see java.sql.PreparedStatement#setBinaryStream |
||||
* @see java.sql.PreparedStatement#setUnicodeStream |
||||
* |
||||
*/ |
||||
public class LargeObject |
||||
{ |
||||
/* |
||||
* Indicates a seek from the begining of a file |
||||
*/ |
||||
public static final int SEEK_SET = 0; |
||||
|
||||
/* |
||||
* Indicates a seek from the current position |
||||
*/ |
||||
public static final int SEEK_CUR = 1; |
||||
|
||||
/* |
||||
* Indicates a seek from the end of a file |
||||
*/ |
||||
public static final int SEEK_END = 2; |
||||
|
||||
private Fastpath fp; // Fastpath API to use
|
||||
private int oid; // OID of this object
|
||||
private int fd; // the descriptor of the open large object
|
||||
|
||||
private BlobOutputStream os; // The current output stream
|
||||
|
||||
private boolean closed = false; // true when we are closed
|
||||
|
||||
/* |
||||
* This opens a large object. |
||||
* |
||||
* <p>If the object does not exist, then an SQLException is thrown. |
||||
* |
||||
* @param fp FastPath API for the connection to use |
||||
* @param oid of the Large Object to open |
||||
* @param mode Mode of opening the large object |
||||
* (defined in LargeObjectManager) |
||||
* @exception SQLException if a database-access error occurs. |
||||
* @see org.postgresql.largeobject.LargeObjectManager |
||||
*/ |
||||
protected LargeObject(Fastpath fp, int oid, int mode) throws SQLException |
||||
{ |
||||
this.fp = fp; |
||||
this.oid = oid; |
||||
|
||||
FastpathArg args[] = new FastpathArg[2]; |
||||
args[0] = new FastpathArg(oid); |
||||
args[1] = new FastpathArg(mode); |
||||
this.fd = fp.getInteger("lo_open", args); |
||||
} |
||||
|
||||
/* Release large object resources during garbage cleanup */ |
||||
protected void finalize() throws SQLException |
||||
{ |
||||
//This code used to call close() however that was problematic
|
||||
//because the scope of the fd is a transaction, thus if commit
|
||||
//or rollback was called before garbage collection ran then
|
||||
//the call to close would error out with an invalid large object
|
||||
//handle. So this method now does nothing and lets the server
|
||||
//handle cleanup when it ends the transaction.
|
||||
} |
||||
|
||||
/* |
||||
* @return the OID of this LargeObject |
||||
*/ |
||||
public int getOID() |
||||
{ |
||||
return oid; |
||||
} |
||||
|
||||
/* |
||||
* This method closes the object. You must not call methods in this |
||||
* object after this is called. |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public void close() throws SQLException |
||||
{ |
||||
if (!closed) |
||||
{ |
||||
// flush any open output streams
|
||||
if (os != null) |
||||
{ |
||||
try |
||||
{ |
||||
// we can't call os.close() otherwise we go into an infinite loop!
|
||||
os.flush(); |
||||
} |
||||
catch (IOException ioe) |
||||
{ |
||||
throw new SQLException(ioe.getMessage()); |
||||
} |
||||
finally |
||||
{ |
||||
os = null; |
||||
} |
||||
} |
||||
|
||||
// finally close
|
||||
FastpathArg args[] = new FastpathArg[1]; |
||||
args[0] = new FastpathArg(fd); |
||||
fp.fastpath("lo_close", false, args); // true here as we dont care!!
|
||||
closed = true; |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Reads some data from the object, and return as a byte[] array |
||||
* |
||||
* @param len number of bytes to read |
||||
* @return byte[] array containing data read |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public byte[] read(int len) throws SQLException |
||||
{ |
||||
// This is the original method, where the entire block (len bytes)
|
||||
// is retrieved in one go.
|
||||
FastpathArg args[] = new FastpathArg[2]; |
||||
args[0] = new FastpathArg(fd); |
||||
args[1] = new FastpathArg(len); |
||||
return fp.getData("loread", args); |
||||
|
||||
// This version allows us to break this down into 4k blocks
|
||||
//if (len<=4048) {
|
||||
//// handle as before, return the whole block in one go
|
||||
//FastpathArg args[] = new FastpathArg[2];
|
||||
//args[0] = new FastpathArg(fd);
|
||||
//args[1] = new FastpathArg(len);
|
||||
//return fp.getData("loread",args);
|
||||
//} else {
|
||||
//// return in 4k blocks
|
||||
//byte[] buf=new byte[len];
|
||||
//int off=0;
|
||||
//while (len>0) {
|
||||
//int bs=4048;
|
||||
//len-=bs;
|
||||
//if (len<0) {
|
||||
//bs+=len;
|
||||
//len=0;
|
||||
//}
|
||||
//read(buf,off,bs);
|
||||
//off+=bs;
|
||||
//}
|
||||
//return buf;
|
||||
//}
|
||||
} |
||||
|
||||
/* |
||||
* Reads some data from the object into an existing array |
||||
* |
||||
* @param buf destination array |
||||
* @param off offset within array |
||||
* @param len number of bytes to read |
||||
* @return the number of bytes actually read |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public int read(byte buf[], int off, int len) throws SQLException |
||||
{ |
||||
byte b[] = read(len); |
||||
if (b.length < len) |
||||
len = b.length; |
||||
System.arraycopy(b, 0, buf, off, len); |
||||
return len; |
||||
} |
||||
|
||||
/* |
||||
* Writes an array to the object |
||||
* |
||||
* @param buf array to write |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public void write(byte buf[]) throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[2]; |
||||
args[0] = new FastpathArg(fd); |
||||
args[1] = new FastpathArg(buf); |
||||
fp.fastpath("lowrite", false, args); |
||||
} |
||||
|
||||
/* |
||||
* Writes some data from an array to the object |
||||
* |
||||
* @param buf destination array |
||||
* @param off offset within array |
||||
* @param len number of bytes to write |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public void write(byte buf[], int off, int len) throws SQLException |
||||
{ |
||||
byte data[] = new byte[len]; |
||||
System.arraycopy(buf, off, data, 0, len); |
||||
write(data); |
||||
} |
||||
|
||||
/* |
||||
* Sets the current position within the object. |
||||
* |
||||
* <p>This is similar to the fseek() call in the standard C library. It |
||||
* allows you to have random access to the large object. |
||||
* |
||||
* @param pos position within object |
||||
* @param ref Either SEEK_SET, SEEK_CUR or SEEK_END |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public void seek(int pos, int ref) throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[3]; |
||||
args[0] = new FastpathArg(fd); |
||||
args[1] = new FastpathArg(pos); |
||||
args[2] = new FastpathArg(ref); |
||||
fp.fastpath("lo_lseek", false, args); |
||||
} |
||||
|
||||
/* |
||||
* Sets the current position within the object. |
||||
* |
||||
* <p>This is similar to the fseek() call in the standard C library. It |
||||
* allows you to have random access to the large object. |
||||
* |
||||
* @param pos position within object from begining |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public void seek(int pos) throws SQLException |
||||
{ |
||||
seek(pos, SEEK_SET); |
||||
} |
||||
|
||||
/* |
||||
* @return the current position within the object |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public int tell() throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[1]; |
||||
args[0] = new FastpathArg(fd); |
||||
return fp.getInteger("lo_tell", args); |
||||
} |
||||
|
||||
/* |
||||
* This method is inefficient, as the only way to find out the size of |
||||
* the object is to seek to the end, record the current position, then |
||||
* return to the original position. |
||||
* |
||||
* <p>A better method will be found in the future. |
||||
* |
||||
* @return the size of the large object |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public int size() throws SQLException |
||||
{ |
||||
int cp = tell(); |
||||
seek(0, SEEK_END); |
||||
int sz = tell(); |
||||
seek(cp, SEEK_SET); |
||||
return sz; |
||||
} |
||||
|
||||
/* |
||||
* Returns an InputStream from this object. |
||||
* |
||||
* <p>This InputStream can then be used in any method that requires an |
||||
* InputStream. |
||||
* |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public InputStream getInputStream() throws SQLException |
||||
{ |
||||
return new BlobInputStream(this, 4096); |
||||
} |
||||
|
||||
/* |
||||
* Returns an OutputStream to this object |
||||
* |
||||
* <p>This OutputStream can then be used in any method that requires an |
||||
* OutputStream. |
||||
* |
||||
* @exception SQLException if a database-access error occurs. |
||||
*/ |
||||
public OutputStream getOutputStream() throws SQLException |
||||
{ |
||||
if (os == null) |
||||
os = new BlobOutputStream(this, 4096); |
||||
return os; |
||||
} |
||||
|
||||
} |
@ -1,229 +0,0 @@ |
||||
/*------------------------------------------------------------------------- |
||||
* |
||||
* LargeObjectManager.java |
||||
* This class implements the large object interface to org.postgresql. |
||||
* |
||||
* It provides methods that allow client code to create, open and delete |
||||
* large objects from the database. When opening an object, an instance of |
||||
* org.postgresql.largeobject.LargeObject is returned, and its methods |
||||
* then allow access to the object. |
||||
* |
||||
* Copyright (c) 2003, PostgreSQL Global Development Group |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java,v 1.12 2003/12/17 15:38:42 davec Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
package org.postgresql.largeobject; |
||||
|
||||
|
||||
import java.sql.ResultSet; |
||||
import java.sql.SQLException; |
||||
import org.postgresql.Driver; |
||||
import org.postgresql.core.BaseConnection; |
||||
import org.postgresql.fastpath.Fastpath; |
||||
import org.postgresql.fastpath.FastpathArg; |
||||
import org.postgresql.util.PSQLException; |
||||
|
||||
/* |
||||
* This class implements the large object interface to org.postgresql. |
||||
* |
||||
* <p>It provides methods that allow client code to create, open and delete |
||||
* large objects from the database. When opening an object, an instance of |
||||
* org.postgresql.largeobject.LargeObject is returned, and its methods then allow |
||||
* access to the object. |
||||
* |
||||
* <p>This class can only be created by org.postgresql.Connection |
||||
* |
||||
* <p>To get access to this class, use the following segment of code: |
||||
* <br><pre> |
||||
* import org.postgresql.largeobject.*; |
||||
* |
||||
* Connection conn; |
||||
* LargeObjectManager lobj; |
||||
* |
||||
* ... code that opens a connection ... |
||||
* |
||||
* lobj = ((org.postgresql.PGConnection)myconn).getLargeObjectAPI(); |
||||
* </pre> |
||||
* |
||||
* <p>Normally, client code would use the getAsciiStream, getBinaryStream, |
||||
* or getUnicodeStream methods in ResultSet, or setAsciiStream, |
||||
* setBinaryStream, or setUnicodeStream methods in PreparedStatement to |
||||
* access Large Objects. |
||||
* |
||||
* <p>However, sometimes lower level access to Large Objects are required, |
||||
* that are not supported by the JDBC specification. |
||||
* |
||||
* <p>Refer to org.postgresql.largeobject.LargeObject on how to manipulate the |
||||
* contents of a Large Object. |
||||
* |
||||
* @see java.sql.ResultSet#getAsciiStream |
||||
* @see java.sql.ResultSet#getBinaryStream |
||||
* @see java.sql.ResultSet#getUnicodeStream |
||||
* @see java.sql.PreparedStatement#setAsciiStream |
||||
* @see java.sql.PreparedStatement#setBinaryStream |
||||
* @see java.sql.PreparedStatement#setUnicodeStream |
||||
*/ |
||||
public class LargeObjectManager |
||||
{ |
||||
// the fastpath api for this connection
|
||||
private Fastpath fp; |
||||
|
||||
/* |
||||
* This mode indicates we want to write to an object |
||||
*/ |
||||
public static final int WRITE = 0x00020000; |
||||
|
||||
/* |
||||
* This mode indicates we want to read an object |
||||
*/ |
||||
public static final int READ = 0x00040000; |
||||
|
||||
/* |
||||
* This mode is the default. It indicates we want read and write access to |
||||
* a large object |
||||
*/ |
||||
public static final int READWRITE = READ | WRITE; |
||||
|
||||
/* |
||||
* This prevents us being created by mere mortals |
||||
*/ |
||||
private LargeObjectManager() |
||||
{} |
||||
|
||||
/* |
||||
* Constructs the LargeObject API. |
||||
* |
||||
* <p><b>Important Notice</b> |
||||
* <br>This method should only be called by org.postgresql.Connection |
||||
* |
||||
* <p>There should only be one LargeObjectManager per Connection. The |
||||
* org.postgresql.Connection class keeps track of the various extension API's |
||||
* and it's advised you use those to gain access, and not going direct. |
||||
*/ |
||||
public LargeObjectManager(BaseConnection conn) throws SQLException |
||||
{ |
||||
// We need Fastpath to do anything
|
||||
this.fp = conn.getFastpathAPI(); |
||||
|
||||
// Now get the function oid's for the api
|
||||
//
|
||||
// This is an example of Fastpath.addFunctions();
|
||||
//
|
||||
String sql; |
||||
if (conn.getMetaData().supportsSchemasInTableDefinitions()) { |
||||
sql = "SELECT p.proname,p.oid "+ |
||||
" FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "+ |
||||
" WHERE p.pronamespace=n.oid AND n.nspname='pg_catalog' AND ("; |
||||
} else { |
||||
sql = "SELECT proname,oid FROM pg_proc WHERE "; |
||||
} |
||||
sql += " proname = 'lo_open'" + |
||||
" or proname = 'lo_close'" + |
||||
" or proname = 'lo_creat'" + |
||||
" or proname = 'lo_unlink'" + |
||||
" or proname = 'lo_lseek'" + |
||||
" or proname = 'lo_tell'" + |
||||
" or proname = 'loread'" + |
||||
" or proname = 'lowrite'"; |
||||
|
||||
if (conn.getMetaData().supportsSchemasInTableDefinitions()) { |
||||
sql += ")"; |
||||
} |
||||
|
||||
ResultSet res = conn.createStatement().executeQuery(sql); |
||||
|
||||
if (res == null) |
||||
throw new PSQLException("postgresql.lo.init"); |
||||
|
||||
fp.addFunctions(res); |
||||
res.close(); |
||||
if (Driver.logDebug) |
||||
Driver.debug("Large Object initialised"); |
||||
} |
||||
|
||||
/* |
||||
* This opens an existing large object, based on its OID. This method |
||||
* assumes that READ and WRITE access is required (the default). |
||||
* |
||||
* @param oid of large object |
||||
* @return LargeObject instance providing access to the object |
||||
* @exception SQLException on error |
||||
*/ |
||||
public LargeObject open(int oid) throws SQLException |
||||
{ |
||||
return new LargeObject(fp, oid, READWRITE); |
||||
} |
||||
|
||||
/* |
||||
* This opens an existing large object, based on its OID |
||||
* |
||||
* @param oid of large object |
||||
* @param mode mode of open |
||||
* @return LargeObject instance providing access to the object |
||||
* @exception SQLException on error |
||||
*/ |
||||
public LargeObject open(int oid, int mode) throws SQLException |
||||
{ |
||||
return new LargeObject(fp, oid, mode); |
||||
} |
||||
|
||||
/* |
||||
* This creates a large object, returning its OID. |
||||
* |
||||
* <p>It defaults to READWRITE for the new object's attributes. |
||||
* |
||||
* @return oid of new object |
||||
* @exception SQLException on error |
||||
*/ |
||||
public int create() throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[1]; |
||||
args[0] = new FastpathArg(READWRITE); |
||||
return fp.getInteger("lo_creat", args); |
||||
} |
||||
|
||||
/* |
||||
* This creates a large object, returning its OID |
||||
* |
||||
* @param mode a bitmask describing different attributes of the new object |
||||
* @return oid of new object |
||||
* @exception SQLException on error |
||||
*/ |
||||
public int create(int mode) throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[1]; |
||||
args[0] = new FastpathArg(mode); |
||||
return fp.getInteger("lo_creat", args); |
||||
} |
||||
|
||||
/* |
||||
* This deletes a large object. |
||||
* |
||||
* @param oid describing object to delete |
||||
* @exception SQLException on error |
||||
*/ |
||||
public void delete(int oid) throws SQLException |
||||
{ |
||||
FastpathArg args[] = new FastpathArg[1]; |
||||
args[0] = new FastpathArg(oid); |
||||
fp.fastpath("lo_unlink", false, args); |
||||
} |
||||
|
||||
/* |
||||
* This deletes a large object. |
||||
* |
||||
* <p>It is identical to the delete method, and is supplied as the C API uses |
||||
* unlink. |
||||
* |
||||
* @param oid describing object to delete |
||||
* @exception SQLException on error |
||||
*/ |
||||
public void unlink(int oid) throws SQLException |
||||
{ |
||||
delete(oid); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue