mirror of
https://github.com/postgres/postgres.git
synced 2025-07-09 22:41:56 +03:00
of duplicated code between the jdbc1 and jdbc2. This checkin restructures the code so that the duplication is removed so that the jdbc3 support can be added without adding yet another copy of everything. Also many classes were renamed to avoid confusion with multiple different objects having the same name. The timestamp tests were also updated to add support for testing timestamp without time zone in addition to timestamp with time zone Modified Files: jdbc/Makefile jdbc/build.xml jdbc/example/ImageViewer.java jdbc/example/basic.java jdbc/example/blobtest.java jdbc/example/threadsafe.java jdbc/org/postgresql/Driver.java.in jdbc/org/postgresql/Field.java jdbc/org/postgresql/core/QueryExecutor.java jdbc/org/postgresql/fastpath/Fastpath.java jdbc/org/postgresql/jdbc1/CallableStatement.java jdbc/org/postgresql/jdbc1/DatabaseMetaData.java jdbc/org/postgresql/jdbc1/PreparedStatement.java jdbc/org/postgresql/jdbc2/Array.java jdbc/org/postgresql/jdbc2/CallableStatement.java jdbc/org/postgresql/jdbc2/DatabaseMetaData.java jdbc/org/postgresql/jdbc2/PreparedStatement.java jdbc/org/postgresql/jdbc2/UpdateableResultSet.java jdbc/org/postgresql/largeobject/LargeObjectManager.java jdbc/org/postgresql/largeobject/PGblob.java jdbc/org/postgresql/largeobject/PGclob.java jdbc/org/postgresql/test/jdbc2/BlobTest.java jdbc/org/postgresql/test/jdbc2/ConnectionTest.java jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java jdbc/org/postgresql/test/jdbc2/TimestampTest.java jdbc/org/postgresql/test/jdbc2/UpdateableResultTest.java jdbc/org/postgresql/util/Serialize.java Added Files: jdbc/org/postgresql/PGConnection.java jdbc/org/postgresql/PGStatement.java jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java jdbc/org/postgresql/jdbc1/Jdbc1Connection.java jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java jdbc/org/postgresql/jdbc1/Jdbc1Statement.java jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java jdbc/org/postgresql/jdbc2/Jdbc2Connection.java jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java jdbc/org/postgresql/jdbc2/Jdbc2Statement.java Removed Files: jdbc/org/postgresql/Connection.java jdbc/org/postgresql/ResultSet.java jdbc/org/postgresql/Statement.java jdbc/org/postgresql/jdbc1/Connection.java jdbc/org/postgresql/jdbc1/ResultSet.java jdbc/org/postgresql/jdbc1/Statement.java jdbc/org/postgresql/jdbc2/Connection.java jdbc/org/postgresql/jdbc2/ResultSet.java jdbc/org/postgresql/jdbc2/Statement.java
1087 lines
33 KiB
Java
1087 lines
33 KiB
Java
package org.postgresql.jdbc2;
|
|
|
|
// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
|
|
// If you make any modifications to this file, you must make sure that the
|
|
// changes are also made (if relevent) to the related JDBC 1 class in the
|
|
// org.postgresql.jdbc1 package.
|
|
|
|
import java.io.*;
|
|
import java.math.*;
|
|
import java.sql.*;
|
|
import java.text.*;
|
|
import java.util.*;
|
|
import org.postgresql.largeobject.*;
|
|
import org.postgresql.util.*;
|
|
|
|
/*
|
|
* A SQL Statement is pre-compiled and stored in a PreparedStatement object.
|
|
* This object can then be used to efficiently execute this statement multiple
|
|
* times.
|
|
*
|
|
* <p><B>Note:</B> The setXXX methods for setting IN parameter values must
|
|
* specify types that are compatible with the defined SQL type of the input
|
|
* parameter. For instance, if the IN parameter has SQL type Integer, then
|
|
* setInt should be used.
|
|
*
|
|
* <p>If arbitrary parameter type conversions are required, then the setObject
|
|
* method should be used with a target SQL type.
|
|
*
|
|
* @see ResultSet
|
|
* @see java.sql.PreparedStatement
|
|
*/
|
|
public class PreparedStatement extends Jdbc2Statement implements java.sql.PreparedStatement
|
|
{
|
|
String sql;
|
|
String[] templateStrings;
|
|
String[] inStrings;
|
|
Jdbc2Connection connection;
|
|
|
|
// Some performance caches
|
|
private StringBuffer sbuf = new StringBuffer();
|
|
|
|
/*
|
|
* Constructor for the PreparedStatement class.
|
|
* Split the SQL statement into segments - separated by the arguments.
|
|
* When we rebuild the thing with the arguments, we can substitute the
|
|
* args and join the whole thing together.
|
|
*
|
|
* @param conn the instanatiating connection
|
|
* @param sql the SQL statement with ? for IN markers
|
|
* @exception SQLException if something bad occurs
|
|
*/
|
|
public PreparedStatement(Jdbc2Connection connection, String sql) throws SQLException
|
|
{
|
|
super(connection);
|
|
|
|
this.sql = sql;
|
|
this.connection = connection;
|
|
parseSqlStmt (); // this allows Callable stmt to override
|
|
}
|
|
|
|
protected void parseSqlStmt () throws SQLException {
|
|
Vector v = new Vector();
|
|
boolean inQuotes = false;
|
|
int lastParmEnd = 0, i;
|
|
|
|
for (i = 0; i < sql.length(); ++i)
|
|
{
|
|
int c = sql.charAt(i);
|
|
|
|
if (c == '\'')
|
|
inQuotes = !inQuotes;
|
|
if (c == '?' && !inQuotes)
|
|
{
|
|
v.addElement(sql.substring (lastParmEnd, i));
|
|
lastParmEnd = i + 1;
|
|
}
|
|
}
|
|
v.addElement(sql.substring (lastParmEnd, sql.length()));
|
|
|
|
templateStrings = new String[v.size()];
|
|
inStrings = new String[v.size() - 1];
|
|
clearParameters();
|
|
|
|
for (i = 0 ; i < templateStrings.length; ++i)
|
|
templateStrings[i] = (String)v.elementAt(i);
|
|
}
|
|
|
|
/*
|
|
* A Prepared SQL query is executed and its ResultSet is returned
|
|
*
|
|
* @return a ResultSet that contains the data produced by the
|
|
* * query - never null
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public java.sql.ResultSet executeQuery() throws SQLException
|
|
{
|
|
return super.executeQuery(compileQuery()); // in Statement class
|
|
}
|
|
|
|
/*
|
|
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition,
|
|
* SQL statements that return nothing such as SQL DDL statements can
|
|
* be executed.
|
|
*
|
|
* @return either the row count for INSERT, UPDATE or DELETE; or
|
|
* * 0 for SQL statements that return nothing.
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int executeUpdate() throws SQLException
|
|
{
|
|
return super.executeUpdate(compileQuery()); // in Statement class
|
|
}
|
|
|
|
/*
|
|
* Helper - this compiles the SQL query from the various parameters
|
|
* This is identical to toString() except it throws an exception if a
|
|
* parameter is unused.
|
|
*/
|
|
protected synchronized String compileQuery()
|
|
throws SQLException
|
|
{
|
|
sbuf.setLength(0);
|
|
int i;
|
|
|
|
for (i = 0 ; i < inStrings.length ; ++i)
|
|
{
|
|
if (inStrings[i] == null)
|
|
throw new PSQLException("postgresql.prep.param", new Integer(i + 1));
|
|
sbuf.append (templateStrings[i]).append (inStrings[i]);
|
|
}
|
|
sbuf.append(templateStrings[inStrings.length]);
|
|
return sbuf.toString();
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to SQL NULL
|
|
*
|
|
* <p><B>Note:</B> You must specify the parameters SQL type (although
|
|
* PostgreSQL ignores it)
|
|
*
|
|
* @param parameterIndex the first parameter is 1, etc...
|
|
* @param sqlType the SQL type code defined in java.sql.Types
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setNull(int parameterIndex, int sqlType) throws SQLException
|
|
{
|
|
set(parameterIndex, "null");
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java boolean value. The driver converts this
|
|
* to a SQL BIT value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setBoolean(int parameterIndex, boolean x) throws SQLException
|
|
{
|
|
set(parameterIndex, x ? "'t'" : "'f'");
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java byte value. The driver converts this to
|
|
* a SQL TINYINT value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setByte(int parameterIndex, byte x) throws SQLException
|
|
{
|
|
set(parameterIndex, Integer.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java short value. The driver converts this
|
|
* to a SQL SMALLINT value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setShort(int parameterIndex, short x) throws SQLException
|
|
{
|
|
set(parameterIndex, Integer.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java int value. The driver converts this to
|
|
* a SQL INTEGER value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setInt(int parameterIndex, int x) throws SQLException
|
|
{
|
|
set(parameterIndex, Integer.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java long value. The driver converts this to
|
|
* a SQL BIGINT value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setLong(int parameterIndex, long x) throws SQLException
|
|
{
|
|
set(parameterIndex, Long.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java float value. The driver converts this
|
|
* to a SQL FLOAT value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setFloat(int parameterIndex, float x) throws SQLException
|
|
{
|
|
set(parameterIndex, Float.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java double value. The driver converts this
|
|
* to a SQL DOUBLE value when it sends it to the database
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setDouble(int parameterIndex, double x) throws SQLException
|
|
{
|
|
set(parameterIndex, Double.toString(x));
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a java.lang.BigDecimal value. The driver
|
|
* converts this to a SQL NUMERIC value when it sends it to the
|
|
* database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
|
|
{
|
|
if (x == null) {
|
|
setNull(parameterIndex, Types.OTHER);
|
|
} else {
|
|
set(parameterIndex, x.toString());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java String value. The driver converts this
|
|
* to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
|
|
* size relative to the driver's limits on VARCHARs) when it sends it
|
|
* to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setString(int parameterIndex, String x) throws SQLException
|
|
{
|
|
// if the passed string is null, then set this column to null
|
|
if (x == null)
|
|
setNull(parameterIndex, Types.OTHER);
|
|
else
|
|
{
|
|
// use the shared buffer object. Should never clash but this makes
|
|
// us thread safe!
|
|
synchronized (sbuf)
|
|
{
|
|
sbuf.setLength(0);
|
|
int i;
|
|
|
|
sbuf.append('\'');
|
|
for (i = 0 ; i < x.length() ; ++i)
|
|
{
|
|
char c = x.charAt(i);
|
|
if (c == '\\' || c == '\'')
|
|
sbuf.append((char)'\\');
|
|
sbuf.append(c);
|
|
}
|
|
sbuf.append('\'');
|
|
set(parameterIndex, sbuf.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a Java array of bytes. The driver converts this
|
|
* to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
|
|
* size relative to the driver's limits on VARBINARYs) when it sends
|
|
* it to the database.
|
|
*
|
|
* <p>Implementation note:
|
|
* <br>With org.postgresql, this creates a large object, and stores the
|
|
* objects oid in this column.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setBytes(int parameterIndex, byte x[]) throws SQLException
|
|
{
|
|
if (connection.haveMinimumCompatibleVersion("7.2"))
|
|
{
|
|
//Version 7.2 supports the bytea datatype for byte arrays
|
|
if (null == x)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
}
|
|
else
|
|
{
|
|
setString(parameterIndex, PGbytea.toPGString(x));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Version 7.1 and earlier support done as LargeObjects
|
|
LargeObjectManager lom = connection.getLargeObjectAPI();
|
|
int oid = lom.create();
|
|
LargeObject lob = lom.open(oid);
|
|
lob.write(x);
|
|
lob.close();
|
|
setInt(parameterIndex, oid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a java.sql.Date value. The driver converts this
|
|
* to a SQL DATE value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
|
|
{
|
|
if (null == x)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
}
|
|
else
|
|
{
|
|
set(parameterIndex, "'" + x.toString() + "'");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a java.sql.Time value. The driver converts
|
|
* this to a SQL TIME value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...));
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setTime(int parameterIndex, Time x) throws SQLException
|
|
{
|
|
if (null == x)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
}
|
|
else
|
|
{
|
|
set(parameterIndex, "'" + x.toString() + "'");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a java.sql.Timestamp value. The driver converts
|
|
* this to a SQL TIMESTAMP value when it sends it to the database.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
|
|
{
|
|
if (null == x)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
}
|
|
else
|
|
{
|
|
// Use the shared StringBuffer
|
|
synchronized (sbuf)
|
|
{
|
|
sbuf.setLength(0);
|
|
sbuf.append("'");
|
|
//format the timestamp
|
|
//we do our own formating so that we can get a format
|
|
//that works with both timestamp with time zone and
|
|
//timestamp without time zone datatypes.
|
|
//The format is '2002-01-01 23:59:59.123456-0130'
|
|
//we need to include the local time and timezone offset
|
|
//so that timestamp without time zone works correctly
|
|
int l_year = x.getYear() + 1900;
|
|
sbuf.append(l_year);
|
|
sbuf.append('-');
|
|
int l_month = x.getMonth() + 1;
|
|
if (l_month < 10) sbuf.append('0');
|
|
sbuf.append(l_month);
|
|
sbuf.append('-');
|
|
int l_day = x.getDate();
|
|
if (l_day < 10) sbuf.append('0');
|
|
sbuf.append(l_day);
|
|
sbuf.append(' ');
|
|
int l_hours = x.getHours();
|
|
if (l_hours < 10) sbuf.append('0');
|
|
sbuf.append(l_hours);
|
|
sbuf.append(':');
|
|
int l_minutes = x.getMinutes();
|
|
if (l_minutes < 10) sbuf.append('0');
|
|
sbuf.append(l_minutes);
|
|
sbuf.append(':');
|
|
int l_seconds = x.getSeconds();
|
|
if (l_seconds < 10) sbuf.append('0');
|
|
sbuf.append(l_seconds);
|
|
// Make decimal from nanos.
|
|
char[] l_decimal = {'0','0','0','0','0','0','0','0','0'};
|
|
char[] l_nanos = Integer.toString(x.getNanos()).toCharArray();
|
|
System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length);
|
|
sbuf.append('.');
|
|
if (connection.haveMinimumServerVersion("7.2")) {
|
|
sbuf.append(l_decimal,0,6);
|
|
} else {
|
|
// Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00".
|
|
sbuf.append(l_decimal,0,2);
|
|
}
|
|
//add timezone offset
|
|
int l_offset = -(x.getTimezoneOffset());
|
|
int l_houros = l_offset/60;
|
|
if (l_houros >= 0) {
|
|
sbuf.append('+');
|
|
} else {
|
|
sbuf.append('-');
|
|
}
|
|
if (l_houros > -10 && l_houros < 10) sbuf.append('0');
|
|
if (l_houros >= 0) {
|
|
sbuf.append(l_houros);
|
|
} else {
|
|
sbuf.append(-l_houros);
|
|
}
|
|
int l_minos = l_offset - (l_houros *60);
|
|
if (l_minos != 0) {
|
|
if (l_minos < 10) sbuf.append('0');
|
|
sbuf.append(l_minos);
|
|
}
|
|
sbuf.append("'");
|
|
set(parameterIndex, sbuf.toString());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When a very large ASCII value is input to a LONGVARCHAR parameter,
|
|
* it may be more practical to send it via a java.io.InputStream.
|
|
* JDBC will read the data from the stream as needed, until it reaches
|
|
* end-of-file. The JDBC driver will do any necessary conversion from
|
|
* ASCII to the database char format.
|
|
*
|
|
* <P><B>Note:</B> This stream object can either be a standard Java
|
|
* stream object or your own subclass that implements the standard
|
|
* interface.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @param length the number of bytes in the stream
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
|
|
{
|
|
if (connection.haveMinimumCompatibleVersion("7.2"))
|
|
{
|
|
//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
|
|
//As the spec/javadoc for this method indicate this is to be used for
|
|
//large String values (i.e. LONGVARCHAR) PG doesn't have a separate
|
|
//long varchar datatype, but with toast all text datatypes are capable of
|
|
//handling very large values. Thus the implementation ends up calling
|
|
//setString() since there is no current way to stream the value to the server
|
|
try
|
|
{
|
|
InputStreamReader l_inStream = new InputStreamReader(x, "ASCII");
|
|
char[] l_chars = new char[length];
|
|
int l_charsRead = l_inStream.read(l_chars, 0, length);
|
|
setString(parameterIndex, new String(l_chars, 0, l_charsRead));
|
|
}
|
|
catch (UnsupportedEncodingException l_uee)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_uee);
|
|
}
|
|
catch (IOException l_ioe)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_ioe);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Version 7.1 supported only LargeObjects by treating everything
|
|
//as binary data
|
|
setBinaryStream(parameterIndex, x, length);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When a very large Unicode value is input to a LONGVARCHAR parameter,
|
|
* it may be more practical to send it via a java.io.InputStream.
|
|
* JDBC will read the data from the stream as needed, until it reaches
|
|
* end-of-file. The JDBC driver will do any necessary conversion from
|
|
* UNICODE to the database char format.
|
|
*
|
|
* ** DEPRECIATED IN JDBC 2 **
|
|
*
|
|
* <P><B>Note:</B> This stream object can either be a standard Java
|
|
* stream object or your own subclass that implements the standard
|
|
* interface.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
* @deprecated
|
|
*/
|
|
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
|
|
{
|
|
if (connection.haveMinimumCompatibleVersion("7.2"))
|
|
{
|
|
//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
|
|
//As the spec/javadoc for this method indicate this is to be used for
|
|
//large String values (i.e. LONGVARCHAR) PG doesn't have a separate
|
|
//long varchar datatype, but with toast all text datatypes are capable of
|
|
//handling very large values. Thus the implementation ends up calling
|
|
//setString() since there is no current way to stream the value to the server
|
|
try
|
|
{
|
|
InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8");
|
|
char[] l_chars = new char[length];
|
|
int l_charsRead = l_inStream.read(l_chars, 0, length);
|
|
setString(parameterIndex, new String(l_chars, 0, l_charsRead));
|
|
}
|
|
catch (UnsupportedEncodingException l_uee)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_uee);
|
|
}
|
|
catch (IOException l_ioe)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_ioe);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Version 7.1 supported only LargeObjects by treating everything
|
|
//as binary data
|
|
setBinaryStream(parameterIndex, x, length);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When a very large binary value is input to a LONGVARBINARY parameter,
|
|
* it may be more practical to send it via a java.io.InputStream.
|
|
* JDBC will read the data from the stream as needed, until it reaches
|
|
* end-of-file.
|
|
*
|
|
* <P><B>Note:</B> This stream object can either be a standard Java
|
|
* stream object or your own subclass that implements the standard
|
|
* interface.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the parameter value
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
|
|
{
|
|
if (connection.haveMinimumCompatibleVersion("7.2"))
|
|
{
|
|
//Version 7.2 supports BinaryStream for for the PG bytea type
|
|
//As the spec/javadoc for this method indicate this is to be used for
|
|
//large binary values (i.e. LONGVARBINARY) PG doesn't have a separate
|
|
//long binary datatype, but with toast the bytea datatype is capable of
|
|
//handling very large values. Thus the implementation ends up calling
|
|
//setBytes() since there is no current way to stream the value to the server
|
|
byte[] l_bytes = new byte[length];
|
|
int l_bytesRead;
|
|
try
|
|
{
|
|
l_bytesRead = x.read(l_bytes, 0, length);
|
|
}
|
|
catch (IOException l_ioe)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_ioe);
|
|
}
|
|
if (l_bytesRead == length)
|
|
{
|
|
setBytes(parameterIndex, l_bytes);
|
|
}
|
|
else
|
|
{
|
|
//the stream contained less data than they said
|
|
byte[] l_bytes2 = new byte[l_bytesRead];
|
|
System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead);
|
|
setBytes(parameterIndex, l_bytes2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Version 7.1 only supported streams for LargeObjects
|
|
//but the jdbc spec indicates that streams should be
|
|
//available for LONGVARBINARY instead
|
|
LargeObjectManager lom = connection.getLargeObjectAPI();
|
|
int oid = lom.create();
|
|
LargeObject lob = lom.open(oid);
|
|
OutputStream los = lob.getOutputStream();
|
|
try
|
|
{
|
|
// could be buffered, but then the OutputStream returned by LargeObject
|
|
// is buffered internally anyhow, so there would be no performance
|
|
// boost gained, if anything it would be worse!
|
|
int c = x.read();
|
|
int p = 0;
|
|
while (c > -1 && p < length)
|
|
{
|
|
los.write(c);
|
|
c = x.read();
|
|
p++;
|
|
}
|
|
los.close();
|
|
}
|
|
catch (IOException se)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", se);
|
|
}
|
|
// lob is closed by the stream so don't call lob.close()
|
|
setInt(parameterIndex, oid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* In general, parameter values remain in force for repeated used of a
|
|
* Statement. Setting a parameter value automatically clears its
|
|
* previous value. However, in coms cases, it is useful to immediately
|
|
* release the resources used by the current parameter values; this
|
|
* can be done by calling clearParameters
|
|
*
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void clearParameters() throws SQLException
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < inStrings.length ; i++)
|
|
inStrings[i] = null;
|
|
}
|
|
|
|
/*
|
|
* Set the value of a parameter using an object; use the java.lang
|
|
* equivalent objects for integral values.
|
|
*
|
|
* <P>The given Java object will be converted to the targetSqlType before
|
|
* being sent to the database.
|
|
*
|
|
* <P>note that this method may be used to pass database-specific
|
|
* abstract data types. This is done by using a Driver-specific
|
|
* Java type and using a targetSqlType of java.sql.Types.OTHER
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the object containing the input parameter value
|
|
* @param targetSqlType The SQL type to be send to the database
|
|
* @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
|
|
* types this is the number of digits after the decimal. For
|
|
* all other types this value will be ignored.
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
|
|
{
|
|
if (x == null)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
return;
|
|
}
|
|
switch (targetSqlType)
|
|
{
|
|
case Types.TINYINT:
|
|
case Types.SMALLINT:
|
|
case Types.INTEGER:
|
|
case Types.BIGINT:
|
|
case Types.REAL:
|
|
case Types.FLOAT:
|
|
case Types.DOUBLE:
|
|
case Types.DECIMAL:
|
|
case Types.NUMERIC:
|
|
if (x instanceof Boolean)
|
|
set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0");
|
|
else
|
|
set(parameterIndex, x.toString());
|
|
break;
|
|
case Types.CHAR:
|
|
case Types.VARCHAR:
|
|
case Types.LONGVARCHAR:
|
|
setString(parameterIndex, x.toString());
|
|
break;
|
|
case Types.DATE:
|
|
setDate(parameterIndex, (java.sql.Date)x);
|
|
break;
|
|
case Types.TIME:
|
|
setTime(parameterIndex, (Time)x);
|
|
break;
|
|
case Types.TIMESTAMP:
|
|
setTimestamp(parameterIndex, (Timestamp)x);
|
|
break;
|
|
case Types.BIT:
|
|
if (x instanceof Boolean)
|
|
{
|
|
set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE");
|
|
}
|
|
else
|
|
{
|
|
throw new PSQLException("postgresql.prep.type");
|
|
}
|
|
break;
|
|
case Types.BINARY:
|
|
case Types.VARBINARY:
|
|
setObject(parameterIndex, x);
|
|
break;
|
|
case Types.OTHER:
|
|
setString(parameterIndex, ((PGobject)x).getValue());
|
|
break;
|
|
default:
|
|
throw new PSQLException("postgresql.prep.type");
|
|
}
|
|
}
|
|
|
|
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
|
|
{
|
|
setObject(parameterIndex, x, targetSqlType, 0);
|
|
}
|
|
|
|
/*
|
|
* This stores an Object into a parameter.
|
|
* <p>New for 6.4, if the object is not recognised, but it is
|
|
* Serializable, then the object is serialised using the
|
|
* org.postgresql.util.Serialize class.
|
|
*/
|
|
public void setObject(int parameterIndex, Object x) throws SQLException
|
|
{
|
|
if (x == null)
|
|
{
|
|
setNull(parameterIndex, Types.OTHER);
|
|
return;
|
|
}
|
|
if (x instanceof String)
|
|
setString(parameterIndex, (String)x);
|
|
else if (x instanceof BigDecimal)
|
|
setBigDecimal(parameterIndex, (BigDecimal)x);
|
|
else if (x instanceof Short)
|
|
setShort(parameterIndex, ((Short)x).shortValue());
|
|
else if (x instanceof Integer)
|
|
setInt(parameterIndex, ((Integer)x).intValue());
|
|
else if (x instanceof Long)
|
|
setLong(parameterIndex, ((Long)x).longValue());
|
|
else if (x instanceof Float)
|
|
setFloat(parameterIndex, ((Float)x).floatValue());
|
|
else if (x instanceof Double)
|
|
setDouble(parameterIndex, ((Double)x).doubleValue());
|
|
else if (x instanceof byte[])
|
|
setBytes(parameterIndex, (byte[])x);
|
|
else if (x instanceof java.sql.Date)
|
|
setDate(parameterIndex, (java.sql.Date)x);
|
|
else if (x instanceof Time)
|
|
setTime(parameterIndex, (Time)x);
|
|
else if (x instanceof Timestamp)
|
|
setTimestamp(parameterIndex, (Timestamp)x);
|
|
else if (x instanceof Boolean)
|
|
setBoolean(parameterIndex, ((Boolean)x).booleanValue());
|
|
else if (x instanceof PGobject)
|
|
setString(parameterIndex, ((PGobject)x).getValue());
|
|
else
|
|
// Try to store java object in database
|
|
setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() );
|
|
}
|
|
|
|
/*
|
|
* Some prepared statements return multiple results; the execute method
|
|
* handles these complex statements as well as the simpler form of
|
|
* statements handled by executeQuery and executeUpdate
|
|
*
|
|
* @return true if the next result is a ResultSet; false if it is an
|
|
* update count or there are no more results
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public boolean execute() throws SQLException
|
|
{
|
|
return super.execute(compileQuery()); // in Statement class
|
|
}
|
|
|
|
/*
|
|
* Returns the SQL statement with the current template values
|
|
* substituted.
|
|
* NB: This is identical to compileQuery() except instead of throwing
|
|
* SQLException if a parameter is null, it places ? instead.
|
|
*/
|
|
public String toString()
|
|
{
|
|
synchronized (sbuf)
|
|
{
|
|
sbuf.setLength(0);
|
|
int i;
|
|
|
|
for (i = 0 ; i < inStrings.length ; ++i)
|
|
{
|
|
if (inStrings[i] == null)
|
|
sbuf.append( '?' );
|
|
else
|
|
sbuf.append (templateStrings[i]);
|
|
sbuf.append (inStrings[i]);
|
|
}
|
|
sbuf.append(templateStrings[inStrings.length]);
|
|
return sbuf.toString();
|
|
}
|
|
}
|
|
|
|
// **************************************************************
|
|
// END OF PUBLIC INTERFACE
|
|
// **************************************************************
|
|
|
|
/*
|
|
* There are a lot of setXXX classes which all basically do
|
|
* the same thing. We need a method which actually does the
|
|
* set for us.
|
|
*
|
|
* @param paramIndex the index into the inString
|
|
* @param s a string to be stored
|
|
* @exception SQLException if something goes wrong
|
|
*/
|
|
protected void set(int paramIndex, String s) throws SQLException
|
|
{
|
|
if (paramIndex < 1 || paramIndex > inStrings.length)
|
|
throw new PSQLException("postgresql.prep.range");
|
|
inStrings[paramIndex - 1] = s;
|
|
}
|
|
|
|
/*
|
|
* Set a parameter to a tablerow-type oid reference.
|
|
*
|
|
* @param parameterIndex the first parameter is 1...
|
|
* @param x the oid of the object from org.postgresql.util.Serialize.store
|
|
* @param classname the classname of the java object x
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
private void setSerialize(int parameterIndex, long x, String classname) throws SQLException
|
|
{
|
|
// converts . to _, toLowerCase, and ensures length<32
|
|
String tablename = Serialize.toPostgreSQL( classname );
|
|
DriverManager.println("setSerialize: setting " + x + "::" + tablename );
|
|
|
|
// OID reference to tablerow-type must be cast like: <oid>::<tablename>
|
|
// Note that postgres support for tablerow data types is incomplete/broken.
|
|
// This cannot be just a plain OID because then there would be ambiguity
|
|
// between when you want the oid itself and when you want the object
|
|
// an oid references.
|
|
set(parameterIndex, Long.toString(x) + "::" + tablename );
|
|
}
|
|
|
|
|
|
// ** JDBC 2 Extensions **
|
|
|
|
/*
|
|
* This parses the query and adds it to the current batch
|
|
*/
|
|
public void addBatch() throws SQLException
|
|
{
|
|
super.addBatch(compileQuery());
|
|
}
|
|
|
|
/*
|
|
* Not sure what this one does, so I'm saying this returns the MetaData for
|
|
* the last ResultSet returned!
|
|
*/
|
|
public java.sql.ResultSetMetaData getMetaData() throws SQLException
|
|
{
|
|
java.sql.ResultSet rs = getResultSet();
|
|
if (rs != null)
|
|
return rs.getMetaData();
|
|
|
|
// Does anyone really know what this method does?
|
|
return null;
|
|
}
|
|
|
|
public void setArray(int i, java.sql.Array x) throws SQLException
|
|
{
|
|
setString(i, x.toString());
|
|
}
|
|
|
|
/*
|
|
* Sets a Blob
|
|
*/
|
|
public void setBlob(int i, Blob x) throws SQLException
|
|
{
|
|
InputStream l_inStream = x.getBinaryStream();
|
|
int l_length = (int) x.length();
|
|
LargeObjectManager lom = connection.getLargeObjectAPI();
|
|
int oid = lom.create();
|
|
LargeObject lob = lom.open(oid);
|
|
OutputStream los = lob.getOutputStream();
|
|
try
|
|
{
|
|
// could be buffered, but then the OutputStream returned by LargeObject
|
|
// is buffered internally anyhow, so there would be no performance
|
|
// boost gained, if anything it would be worse!
|
|
int c = l_inStream.read();
|
|
int p = 0;
|
|
while (c > -1 && p < l_length)
|
|
{
|
|
los.write(c);
|
|
c = l_inStream.read();
|
|
p++;
|
|
}
|
|
los.close();
|
|
}
|
|
catch (IOException se)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", se);
|
|
}
|
|
// lob is closed by the stream so don't call lob.close()
|
|
setInt(i, oid);
|
|
}
|
|
|
|
/*
|
|
* This is similar to setBinaryStream except it uses a Reader instead of
|
|
* InputStream.
|
|
*/
|
|
public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException
|
|
{
|
|
if (connection.haveMinimumCompatibleVersion("7.2"))
|
|
{
|
|
//Version 7.2 supports CharacterStream for for the PG text types
|
|
//As the spec/javadoc for this method indicate this is to be used for
|
|
//large text values (i.e. LONGVARCHAR) PG doesn't have a separate
|
|
//long varchar datatype, but with toast all the text datatypes are capable of
|
|
//handling very large values. Thus the implementation ends up calling
|
|
//setString() since there is no current way to stream the value to the server
|
|
char[] l_chars = new char[length];
|
|
int l_charsRead;
|
|
try
|
|
{
|
|
l_charsRead = x.read(l_chars, 0, length);
|
|
}
|
|
catch (IOException l_ioe)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", l_ioe);
|
|
}
|
|
setString(i, new String(l_chars, 0, l_charsRead));
|
|
}
|
|
else
|
|
{
|
|
//Version 7.1 only supported streams for LargeObjects
|
|
//but the jdbc spec indicates that streams should be
|
|
//available for LONGVARCHAR instead
|
|
LargeObjectManager lom = connection.getLargeObjectAPI();
|
|
int oid = lom.create();
|
|
LargeObject lob = lom.open(oid);
|
|
OutputStream los = lob.getOutputStream();
|
|
try
|
|
{
|
|
// could be buffered, but then the OutputStream returned by LargeObject
|
|
// is buffered internally anyhow, so there would be no performance
|
|
// boost gained, if anything it would be worse!
|
|
int c = x.read();
|
|
int p = 0;
|
|
while (c > -1 && p < length)
|
|
{
|
|
los.write(c);
|
|
c = x.read();
|
|
p++;
|
|
}
|
|
los.close();
|
|
}
|
|
catch (IOException se)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", se);
|
|
}
|
|
// lob is closed by the stream so don't call lob.close()
|
|
setInt(i, oid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* New in 7.1
|
|
*/
|
|
public void setClob(int i, Clob x) throws SQLException
|
|
{
|
|
InputStream l_inStream = x.getAsciiStream();
|
|
int l_length = (int) x.length();
|
|
LargeObjectManager lom = connection.getLargeObjectAPI();
|
|
int oid = lom.create();
|
|
LargeObject lob = lom.open(oid);
|
|
OutputStream los = lob.getOutputStream();
|
|
try
|
|
{
|
|
// could be buffered, but then the OutputStream returned by LargeObject
|
|
// is buffered internally anyhow, so there would be no performance
|
|
// boost gained, if anything it would be worse!
|
|
int c = l_inStream.read();
|
|
int p = 0;
|
|
while (c > -1 && p < l_length)
|
|
{
|
|
los.write(c);
|
|
c = l_inStream.read();
|
|
p++;
|
|
}
|
|
los.close();
|
|
}
|
|
catch (IOException se)
|
|
{
|
|
throw new PSQLException("postgresql.unusual", se);
|
|
}
|
|
// lob is closed by the stream so don't call lob.close()
|
|
setInt(i, oid);
|
|
}
|
|
|
|
/*
|
|
* At least this works as in PostgreSQL null represents anything null ;-)
|
|
*
|
|
* New in 7,1
|
|
*/
|
|
public void setNull(int i, int t, String s) throws SQLException
|
|
{
|
|
setNull(i, t);
|
|
}
|
|
|
|
public void setRef(int i, Ref x) throws SQLException
|
|
{
|
|
throw org.postgresql.Driver.notImplemented();
|
|
}
|
|
|
|
/*
|
|
* New in 7,1
|
|
*/
|
|
public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException
|
|
{
|
|
if (cal == null)
|
|
setDate(i, d);
|
|
else
|
|
{
|
|
cal.setTime(d);
|
|
setDate(i, new java.sql.Date(cal.getTime().getTime()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* New in 7,1
|
|
*/
|
|
public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException
|
|
{
|
|
if (cal == null)
|
|
setTime(i, t);
|
|
else
|
|
{
|
|
cal.setTime(t);
|
|
setTime(i, new java.sql.Time(cal.getTime().getTime()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* New in 7,1
|
|
*/
|
|
public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException
|
|
{
|
|
if (cal == null)
|
|
setTimestamp(i, t);
|
|
else
|
|
{
|
|
cal.setTime(t);
|
|
setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime()));
|
|
}
|
|
}
|
|
|
|
}
|
|
|