/*
* Copyright (c) 2004-2016 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
* which accompanies this distribution, and is available at
* http://opensource.org/licenses/BSD-3-Clause
*
* Contributors:
* Tada AB
* PostgreSQL Global Development Group
* Chapman Flack
*/
package org.postgresql.pljava.jdbc;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.ClientInfoStatus;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.BitSet;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.postgresql.pljava.internal.Oid;
import org.postgresql.pljava.internal.PgSavepoint;
/**
* Provides access to the current connection (session) the Java stored
* procedure is running in. It is returned from the driver manager
* with
* <code>DriverManager.getConnection("jdbc:default:connection");</code>
* and cannot be managed in any way since it's already running inside
* a transaction. This means the following methods cannot be used.
* <ul>
* <li><code>commit()</code></li>
* <li><code>rollback()</code></li>
* <li><code>setAutoCommit()</code></li>
* <li><code>setTransactionIsolation()</code></li>
* </ul>
* @author Thomas Hallgren
*/
public class SPIConnection implements Connection
{
/**
* A map from Java classes to java.sql.Types integers.
*/
private static final HashMap s_sqlType2Class = new HashMap(30);
/**
* The version number of the currently executing PostgreSQL
* server.
*/
private int[] VERSION_NUMBER = null;
/**
* Client info properties for JDBC 4.
*/
private Properties _clientInfo;
static
{
addType(String.class, Types.VARCHAR);
addType(Byte.class, Types.TINYINT);
addType(Short.class, Types.SMALLINT);
addType(Integer.class, Types.INTEGER);
addType(Long.class, Types.BIGINT);
addType(Float.class, Types.FLOAT);
addType(Double.class, Types.DOUBLE);
addType(BigDecimal.class, Types.DECIMAL);
addType(BigInteger.class, Types.NUMERIC);
addType(Boolean.class, Types.BOOLEAN);
addType(Blob.class, Types.BLOB);
addType(Clob.class, Types.CLOB);
addType(Date.class, Types.DATE);
addType(Time.class, Types.TIME);
addType(Timestamp.class, Types.TIMESTAMP);
addType(java.util.Date.class, Types.TIMESTAMP);
addType(byte[].class, Types.VARBINARY);
addType(BitSet.class, Types.BIT);
addType(URL.class, Types.DATALINK);
}
private static final void addType(Class clazz, int sqlType)
{
s_sqlType2Class.put(clazz, new Integer(sqlType));
}
/**
* Returns a default connection instance. It is normally the caller's
* responsibility to close this instance, but as {@code close} is a no-op
* for this connection, that isn't critical.
*/
public static Connection getDefault()
throws SQLException
{
return new SPIConnection();
}
/**
* Returns {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. Cursors are actually
* closed when a function returns to SQL.
*/
public int getHoldability()
{
return ResultSet.CLOSE_CURSORS_AT_COMMIT;
}
/**
* Returns {@link Connection#TRANSACTION_READ_COMMITTED}.
*/
public int getTransactionIsolation()
{
return TRANSACTION_READ_COMMITTED;
}
/**
* Warnings are not yet supported.
* @throws SQLException indicating that this feature is not supported.
*/
public void clearWarnings()
throws SQLException
{
throw new UnsupportedFeatureException("Connection.clearWarnings");
}
/**
* This is a no-op. The default connection never closes.
*/
public void close()
{
}
/**
* It's not legal to do a commit within a call from SQL.
* @throws SQLException indicating that this feature is not supported.
*/
public void commit()
throws SQLException
{
throw new UnsupportedFeatureException("Connection.commit");
}
/**
* It's not legal to do a rollback within a call from SQL.
* @throws SQLException indicating that this feature is not supported.
*/
public void rollback()
throws SQLException
{
throw new UnsupportedFeatureException("Connection.rollback");
}
/**
* It is assumed that an SPI call is under transaction control. This method
* will always return <code>false</code>.
*/
public boolean getAutoCommit()
{
return false;
}
/**
* Will always return false.
*/
public boolean isClosed()
{
return false;
}
/**
* Returns <code>false</code>. The SPIConnection is not real-only.
*/
public boolean isReadOnly()
{
return false;
}
/**
* Change of holdability is not supported.
* @throws SQLException indicating that this feature is not supported.
*/
public void setHoldability(int holdability)
throws SQLException
{
throw new UnsupportedFeatureException("Connection.setHoldability");
}
/**
* Change of transaction isolation level is not supported.
* @throws SQLException indicating that this feature is not supported.
*/
public void setTransactionIsolation(int level)
throws SQLException
{
throw new UnsupportedFeatureException("Connection.setTransactionIsolation");
}
/**
* It is assumed that an SPI call is under transaction control. Changing
* that is not supported.
* @throws SQLException indicating that this feature is not supported.
*/
public void setAutoCommit(boolean autoCommit)
throws SQLException
{
throw new UnsupportedFeatureException("Connection.setAutoCommit");
}
/**
* It is assumed that an inserts and updates can be performed using and
* SPIConnection. Changing that is not supported.
* @throws SQLException indicating that this feature is not supported.
*/
public void setReadOnly(boolean readOnly)
throws SQLException
{
throw new UnsupportedFeatureException("Connection.setReadOnly");
}
/**
* Returns the database in which we are running.
*/
public String getCatalog()
throws SQLException
{
ResultSet rs = createStatement().executeQuery("SELECT pg_catalog.current_database()");
try {
rs.next();
return rs.getString(1);
} finally {
rs.close();
}
}
/**
* The catalog name cannot be set.
* @throws SQLException indicating that this feature is not supported.
*/
public void setCatalog(String catalog)
throws SQLException
{
throw new UnsupportedFeatureException("Connection.setCatalog");
}
/**
* Retrieves an instance of {@link SPIDatabaseMetaData}
* representing this <code>Connection</code> object. The
* metadata includes information about the SQL grammar
* supported by PostgreSQL, the capabilities of PL/Java, as
* well as the tables and stored procedures for this
* connection and so on.
*
* @return an SPIDatabaseMetaData object for this
* <code>Connection</code> object
*/
public DatabaseMetaData getMetaData()
{
return new SPIDatabaseMetaData(this);
}
/**
* Warnings are not yet supported.
* @throws SQLException indicating that this feature is not supported.
*/
public SQLWarning getWarnings()
throws SQLException
{
throw new UnsupportedFeatureException("Connection.getWarnings");
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException
{
if(!(savepoint instanceof PgSavepoint))
throw new IllegalArgumentException("Not a PL/Java Savepoint");
PgSavepoint sp = (PgSavepoint)savepoint;
sp.release();
forgetSavepoint(sp);
}
public void rollback(Savepoint savepoint) throws SQLException
{
if(!(savepoint instanceof PgSavepoint))
throw new IllegalArgumentException("Not a PL/Java Savepoint");
PgSavepoint sp = (PgSavepoint)savepoint;
Invocation.clearErrorCondition();
sp.rollback();
forgetSavepoint(sp);
}
/**
* Creates a new instance of <code>SPIStatement</code>.
*/
public Statement createStatement()
throws SQLException
{
if(this.isClosed())
throw new SQLException("Connection is closed");
return new SPIStatement(this);
}
/**
* Creates a new instance of <code>SPIStatement</code>.
*
* @throws SQLException
*
* if the <code>resultSetType</code> differs from
* {@link ResultSet#TYPE_FORWARD_ONLY} or if the
* <code>resultSetConcurrencty</code> differs from
* {@link ResultSet#CONCUR_READ_ONLY}.
*/
public Statement createStatement(
int resultSetType,
int resultSetConcurrency)
throws SQLException
{
if(resultSetType != ResultSet.TYPE_FORWARD_ONLY)
throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type");
if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY)
throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency");
return this.createStatement();
}
/**
* Creates a new instance of <code>SPIStatement</code>.
*
* @throws SQLException
* if the <code>resultSetType</code> differs from {@link
* ResultSet#TYPE_FORWARD_ONLY}, if the <code>resultSetConcurrencty</code>
* differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the
* resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
*/
public Statement createStatement(
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
{
if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT)
throw new UnsupportedOperationException(
"CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability");
return this.createStatement(resultSetType, resultSetConcurrency);
}
/**
* Returns <code>null</code>. Type map is not yet imlemented.
*/
public Map getTypeMap()
throws SQLException
{
return null;
}
/**
* Type map is not yet implemented.
* @throws SQLException indicating that this feature is not supported.
*/
public void setTypeMap(Map map)
throws SQLException
{
throw new UnsupportedOperationException("Type map is not yet implemented");
}
/**
* Parse the JDBC SQL into PostgreSQL.
*/
public String nativeSQL(String sql)
throws SQLException
{
return this.nativeSQL(sql, null);
}
public String nativeSQL(String sql, int[] paramCountRet)
{
StringBuffer buf = new StringBuffer();
int len = sql.length();
char inQuote = 0;
int paramIndex = 1;
for(int idx = 0; idx < len; ++idx)
{
char c = sql.charAt(idx);
switch(c)
{
case '\\':
// Next character is escaped. Keep both
// escape and the character.
//
buf.append(c);
if(++idx == len)
break;
c = sql.charAt(idx);
break;
case '\'':
case '"':
// Strings within quotes should not be subject
// to '?' -> '$n' substitution.
//
if(inQuote == c)
inQuote = 0;
else
inQuote = c;
break;
case '?':
if(inQuote == 0)
{
buf.append('$');
buf.append(paramIndex++);
continue;
}
break;
default:
if(inQuote == 0 && Character.isWhitespace(c))
{
// Strip of multiple whitespace outside of
// strings.
//
++idx;
while(idx < len && Character.isWhitespace(sql.charAt(idx)))
++idx;
--idx;
c = ' ';
}
}
buf.append(c);
}
if(paramCountRet != null)
paramCountRet[0] = paramIndex - 1;
return buf.toString();
}
/**
* Procedure calls are not yet implemented.
* @throws SQLException indicating that this feature is not supported.
*/
public CallableStatement prepareCall(String sql) throws SQLException
{
throw new UnsupportedOperationException("Procedure calls are not yet implemented");
}
/**
* Procedure calls are not yet implemented.
* @throws SQLException indicating that this feature is not supported.
*/
public CallableStatement prepareCall(
String sql,
int resultSetType,
int resultSetConcurrency)
throws SQLException
{
throw new UnsupportedOperationException("Procedure calls are not yet implemented");
}
/**
* Procedure calls are not yet implemented.
* @throws SQLException indicating that this feature is not supported.
*/
public CallableStatement prepareCall(
String sql,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
{
throw new UnsupportedOperationException("Procedure calls are not yet implemented");
}
/**
* Creates a new instance of <code>SPIPreparedStatement</code>.
*/
public PreparedStatement prepareStatement(String sql)
throws SQLException
{
if(this.isClosed())
throw new SQLException("Connection is closed");
int[] pcount = new int[] { 0 };
sql = this.nativeSQL(sql, pcount);
PreparedStatement stmt = new SPIPreparedStatement(this, sql, pcount[0]);
Invocation.current().manageStatement(stmt);
return stmt;
}
/**
* Return of auto generated keys is not yet supported.
* @throws SQLException indicating that this feature is not supported.
*/
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException
{
throw new UnsupportedFeatureException("Auto generated key support not yet implemented");
}
/**
* Creates a new instance of <code>SPIPreparedStatement</code>.
*
* @throws SQLException
* if the <code>resultSetType</code> differs from {@link
* ResultSet#TYPE_FORWARD_ONLY} or if the <code>resultSetConcurrencty</code>
* differs from {@link ResultSet#CONCUR_READ_ONLY}.
*/
public PreparedStatement prepareStatement(
String sql,
int resultSetType,
int resultSetConcurrency)
throws SQLException
{
if(resultSetType != ResultSet.TYPE_FORWARD_ONLY)
throw new UnsupportedOperationException("TYPE_FORWARD_ONLY supported ResultSet type");
if(resultSetConcurrency != ResultSet.CONCUR_READ_ONLY)
throw new UnsupportedOperationException("CONCUR_READ_ONLY is the supported ResultSet concurrency");
return prepareStatement(sql);
}
/**
* Creates a new instance of <code>SPIPreparedStatement</code>.
*
* @throws SQLException
* if the <code>resultSetType</code> differs from {@link
* ResultSet#TYPE_FORWARD_ONLY}, if the <code>resultSetConcurrencty</code>
* differs from {@link ResultSet#CONCUR_READ_ONLY}, or if the
* resultSetHoldability differs from {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}.
*/
public PreparedStatement prepareStatement(
String sql,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException
{
if(resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT)
throw new UnsupportedOperationException(
"CLOSE_CURSORS_AT_COMMIT is the only supported ResultSet holdability");
return this.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
/**
* Return of auto generated keys is not yet supported.
* @throws SQLException indicating that this feature is not supported.
*/
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException
{
throw new UnsupportedFeatureException("Auto generated key support not yet implemented");
}
/**
* Return of auto generated keys is not yet supported.
* @throws SQLException indicating that this feature is not supported.
*/
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException
{
throw new UnsupportedFeatureException("Auto generated key support not yet implemented");
}
public Savepoint setSavepoint()
throws SQLException
{
return this.rememberSavepoint(PgSavepoint.set("anonymous_savepoint"));
}
public Savepoint setSavepoint(String name)
throws SQLException
{
return this.rememberSavepoint(PgSavepoint.set(name));
}
static int getTypeForClass(Class c)
{
if(c.isArray() && !c.equals(byte[].class))
return Types.ARRAY;
Integer sqt = (Integer)s_sqlType2Class.get(c);
if(sqt != null)
return sqt.intValue();
/*
* This is not a well known JDBC type.
*/
return Types.OTHER;
}
private Savepoint rememberSavepoint(PgSavepoint sp)
throws SQLException
{
// Remember the first savepoint for each call-level so
// that it can be released when the function call ends. Releasing
// the first savepoint will release all subsequent savepoints.
//
Invocation invocation = Invocation.current();
Savepoint old = invocation.getSavepoint();
if(old == null)
invocation.setSavepoint(sp);
return sp;
}
private static void forgetSavepoint(PgSavepoint sp)
throws SQLException
{
Invocation invocation = Invocation.current();
if(invocation.getSavepoint() == sp)
invocation.setSavepoint(null);
}
public int[] getVersionNumber() throws SQLException
{
if (VERSION_NUMBER != null)
return VERSION_NUMBER;
ResultSet rs = createStatement().executeQuery(
"SELECT version()");
try
{
if (!rs.next())
throw new SQLException(
"Cannot retrieve product version number");
String ver = rs.getString(1);
Pattern p = Pattern.compile(
"^PostgreSQL\\s+(\\d+)\\.(\\d+)(.\\d+)?.*");
Matcher m = p.matcher(ver);
if(m.matches())
{
VERSION_NUMBER = new int[3];
VERSION_NUMBER[0] = Integer.parseInt(m.group(1));
VERSION_NUMBER[1] = Integer.parseInt(m.group(2));
String bugfix = m.group(3);
if(bugfix != null && bugfix.length() > 1)
VERSION_NUMBER[2] = Integer.parseInt(bugfix.substring(1));
return VERSION_NUMBER;
}
throw new SQLException(
"Unexpected product version string format: " +
ver);
}
catch (PatternSyntaxException e)
{
throw new SQLException(
"Error in product version string parsing: " +
e.getMessage());
}
finally
{
rs.close();
}
}
/*
* This implemetation uses the jdbc3Types array to support the jdbc3
* datatypes. Basically jdbc2 and jdbc3 are the same, except that
* jdbc3 adds some
*/
public int getSQLType(String pgTypeName)
{
if (pgTypeName == null)
return Types.OTHER;
for (int i = 0;i < JDBC3_TYPE_NAMES.length;i++)
if (pgTypeName.equals(JDBC3_TYPE_NAMES[i]))
return JDBC_TYPE_NUMBERS[i];
return Types.OTHER;
}
/*
* This returns the java.sql.Types type for a PG type oid
*
* @param oid PostgreSQL type oid
* @return the java.sql.Types type
* @exception SQLException if a database access error occurs
*/
public int getSQLType(Oid oid) throws SQLException
{
return getSQLType(getPGType(oid));
}
public String getPGType(Oid oid) throws SQLException
{
String typeName = null;
PreparedStatement query = null;
ResultSet rs = null;
try
{
query = prepareStatement("SELECT typname FROM pg_catalog.pg_type WHERE oid=?");
query.setObject(1, oid);
rs = query.executeQuery();
if (rs.next())
{
typeName = rs.getString(1);
}
else
{
throw new SQLException("Cannot find PG type with oid=" + oid);
}
}
finally
{
if (query != null)
{
query.close();
}
}
return typeName;
}
static Object basicCoersion(Class cls, Object value)
throws SQLException
{
if(value == null || cls.isInstance(value))
return value;
if(cls == String.class)
{
if(value instanceof Number
|| value instanceof Boolean
|| value instanceof Timestamp
|| value instanceof Date
|| value instanceof Time)
return value.toString();
}
else if(cls == URL.class && value instanceof String)
{
try
{
return new URL((String)value);
}
catch(MalformedURLException e)
{
throw new SQLException(e.toString());
}
}
throw new SQLException("Cannot derive a value of class " +
cls.getName() + " from an object of class " + value.getClass().getName());
}
static Number basicNumericCoersion(Class cls, Object value)
throws SQLException
{
if(value == null || value instanceof Number)
return (Number)value;
if(cls == int.class || cls == long.class || cls == short.class || cls == byte.class)
{
if(value instanceof String)
return Long.valueOf((String)value);
if(value instanceof Boolean)
return new Long(((Boolean)value).booleanValue() ? 1 : 0);
}
else if(cls == BigDecimal.class)
{
if(value instanceof String)
return new BigDecimal((String)value);
if(value instanceof Boolean)
return new BigDecimal(((Boolean)value).booleanValue() ? 1 : 0);
}
if(cls == double.class || cls == float.class)
{
if(value instanceof String)
return Double.valueOf((String)value);
if(value instanceof Boolean)
return new Double(((Boolean)value).booleanValue() ? 1 : 0);
}
throw new SQLException("Cannot derive a Number from an object of class " + value.getClass().getName());
}
static Object basicCalendricalCoersion(Class cls, Object value, Calendar cal)
throws SQLException
{
if(value == null)
return value;
if(cls.isInstance(value))
return value;
if(cls == Timestamp.class)
{
if(value instanceof Date)
{
cal.setTime((Date)value);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return new Timestamp(cal.getTimeInMillis());
}
else if(value instanceof Time)
{
cal.setTime((Date)value);
cal.set(1970, 0, 1);
return new Timestamp(cal.getTimeInMillis());
}
else if(value instanceof String)
{
return Timestamp.valueOf((String)value);
}
}
else if(cls == Date.class)
{
if(value instanceof Timestamp)
{
Timestamp ts = (Timestamp)value;
cal.setTime(ts);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return new Date(cal.getTimeInMillis());
}
else if(value instanceof String)
{
return Date.valueOf((String)value);
}
}
else if(cls == Time.class)
{
if(value instanceof Timestamp)
{
Timestamp ts = (Timestamp)value;
cal.setTime(ts);
cal.set(1970, 0, 1);
return new Time(cal.getTimeInMillis());
}
else if(value instanceof String)
{
return Time.valueOf((String)value);
}
}
throw new SQLException("Cannot derive a value of class " +
cls.getName() + " from an object of class " + value.getClass().getName());
}
/*
* This table holds the org.postgresql names for the types supported.
* Any types that map to Types.OTHER (eg POINT) don't go into this table.
* They default automatically to Types.OTHER
*
* Note: This must be in the same order as below.
*
* Tip: keep these grouped together by the Types. value
*/
public static final String JDBC3_TYPE_NAMES[] = {
"int2",
"int4", "oid",
"int8",
"cash", "money",
"numeric",
"float4",
"float8",
"bpchar", "char", "char2", "char4", "char8", "char16",
"varchar", "text", "name", "filename",
"bytea",
"bool",
"bit",
"date",
"time", "timetz",
"abstime", "timestamp", "timestamptz",
"_bool", "_char", "_int2", "_int4", "_text",
"_oid", "_varchar", "_int8", "_float4", "_float8",
"_abstime", "_date", "_time", "_timestamp", "_numeric",
"_bytea"
};
/*
* This table holds the JDBC type for each entry above.
*
* Note: This must be in the same order as above
*
* Tip: keep these grouped together by the Types. value
*/
public static final int JDBC_TYPE_NUMBERS[] =
{
Types.SMALLINT,
Types.INTEGER, Types.INTEGER,
Types.BIGINT,
Types.DOUBLE, Types.DOUBLE,
Types.NUMERIC,
Types.REAL,
Types.DOUBLE,
Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR,
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
Types.BINARY,
Types.BOOLEAN,
Types.BIT,
Types.DATE,
Types.TIME, Types.TIME,
Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP,
Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
Types.ARRAY
};
// ************************************************************
// Non-implementation of JDBC 4 methods.
// ************************************************************
public Struct createStruct( String typeName, Object[] attributes )
throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.createStruct( String, Object[] ) not implemented yet.", "0A000" );
}
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.createArrayOf( String, Object[] ) not implemented yet.", "0A000" );
}
public boolean isValid( int timeout )
throws SQLException
{
return true; // The connection is always alive and
// ready, right?
}
public SQLXML createSQLXML()
throws SQLException
{
throw new SQLFeatureNotSupportedException( "SPIConnection.createSQLXML() not implemented yet.",
"0A000" );
}
public NClob createNClob()
throws SQLException
{
throw new SQLFeatureNotSupportedException( "SPIConnection.createNClob() not implemented yet.",
"0A000" );
}
public Blob createBlob()
throws SQLException
{
throw new SQLFeatureNotSupportedException( "SPIConnection.createBlob() not implemented yet.",
"0A000" );
}
public Clob createClob()
throws SQLException
{
throw new SQLFeatureNotSupportedException( "SPIConnection.createClob() not implemented yet.",
"0A000" );
}
public boolean isWrapperFor(Class<?> iface)
throws SQLException
{
throw new SQLFeatureNotSupportedException
( this.getClass()
+ ".isWrapperFor( Class<?> ) not implemented yet.",
"0A000" );
}
public <T> T unwrap(Class<T> iface)
throws SQLException
{
throw new SQLFeatureNotSupportedException
( this.getClass()
+ ".unwrapClass( Class<?> ) not implemented yet.",
"0A000" );
}
public void setClientInfo(String name, String value)
throws SQLClientInfoException
{
Map<String, ClientInfoStatus> failures = new HashMap<>();
failures.put(name, ClientInfoStatus.REASON_UNKNOWN_PROPERTY);
throw new SQLClientInfoException(
"ClientInfo property not supported.", failures);
}
public void setClientInfo(Properties properties)
throws SQLClientInfoException
{
if (properties == null || properties.size() == 0)
return;
Map<String, ClientInfoStatus> failures = new HashMap<>();
Iterator<String> i = properties.stringPropertyNames().iterator();
while (i.hasNext()) {
failures.put(i.next(), ClientInfoStatus.REASON_UNKNOWN_PROPERTY);
}
throw new SQLClientInfoException(
"ClientInfo property not supported.", failures);
}
public String getClientInfo(String name) throws SQLException
{
return null;
}
public Properties getClientInfo() throws SQLException
{
if (_clientInfo == null) {
_clientInfo = new Properties();
}
return _clientInfo;
}
public void abort(Executor executor) throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.abort(Executor) not implemented yet.", "0A000" );
}
public int getNetworkTimeout() throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.getNetworkTimeout() not implemented yet.", "0A000" );
}
public void setNetworkTimeout(Executor executor, int milliseconds)
throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.setNetworkTimeout(Executor,int) not implemented yet.", "0A000" );
}
public String getSchema() throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.getSchema() not implemented yet.", "0A000" );
}
public void setSchema(String schema) throws SQLException
{
throw new SQLFeatureNotSupportedException(
"SPIConnection.setSchema(String) not implemented yet.", "0A000" );
}
}