/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.jdbc.statements; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URI; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.NClob; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; import java.util.TimeZone; import org.apache.jena.datatypes.xsd.XSDDatatype ; import org.apache.jena.graph.Node ; import org.apache.jena.graph.NodeFactory ; import org.apache.jena.iri.IRI; import org.apache.jena.jdbc.connections.JenaConnection; import org.apache.jena.jdbc.statements.metadata.JenaParameterMetadata; import org.apache.jena.jdbc.utils.JdbcNodeUtils; import org.apache.jena.query.ParameterizedSparqlString ; import org.apache.jena.rdf.model.RDFNode ; import org.apache.jena.sparql.util.NodeFactoryExtra ; /** * Abstract Jena JDBC implementation of a prepared statement * */ public abstract class JenaPreparedStatement extends JenaStatement implements PreparedStatement { private ParameterizedSparqlString sparqlStr = new ParameterizedSparqlString(); private ParameterMetaData paramMetadata; /** * Creates a new prepared statement * * @param sparql * @param connection * Connection * @param type * Result Set type for result sets produced by this statement * @param fetchDir * Fetch Direction * @param fetchSize * Fetch Size * @param holdability * Result Set holdability * @param autoCommit * Auto-commit * @param transactionLevel * Transaction Isolation level * @throws SQLException * Thrown if there is a problem preparing the statement */ public JenaPreparedStatement(String sparql, JenaConnection connection, int type, int fetchDir, int fetchSize, int holdability, boolean autoCommit, int transactionLevel) throws SQLException { super(connection, type, fetchDir, fetchSize, holdability, autoCommit, transactionLevel); this.sparqlStr.setCommandText(sparql); this.paramMetadata = new JenaParameterMetadata(this.sparqlStr); } @Override public void addBatch() { this.addBatch(this.sparqlStr.toString()); } @Override public void clearParameters() { this.sparqlStr.clearParams(); } @Override public boolean execute() throws SQLException { return this.execute(this.sparqlStr.toString()); } @Override public ResultSet executeQuery() throws SQLException { return this.executeQuery(this.sparqlStr.toString()); } @Override public int executeUpdate() throws SQLException { return this.executeUpdate(this.sparqlStr.toString()); } @Override public ResultSetMetaData getMetaData() { // Return null because we don't know in advance the column types return null; } @Override public ParameterMetaData getParameterMetaData() { return this.paramMetadata; } protected final void setParameter(int parameterIndex, Node n) throws SQLException { // Remember that JDBC used a 1 based index if (parameterIndex < 1 || parameterIndex > this.paramMetadata.getParameterCount()) throw new SQLException("Parameter Index is out of bounds"); this.sparqlStr.setParam(parameterIndex - 1, n); } @Override public void setArray(int parameterIndex, Array value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setAsciiStream(int parameterIndex, InputStream value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setAsciiStream(int parameterIndex, InputStream value, int arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setAsciiStream(int parameterIndex, InputStream value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBigDecimal(int parameterIndex, BigDecimal value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(value.toPlainString(), XSDDatatype.XSDdecimal)); } @Override public void setBinaryStream(int parameterIndex, InputStream value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBinaryStream(int parameterIndex, InputStream value, int arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBinaryStream(int parameterIndex, InputStream value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBlob(int parameterIndex, Blob value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBlob(int parameterIndex, InputStream value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBlob(int parameterIndex, InputStream value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setBoolean(int parameterIndex, boolean value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString(value), XSDDatatype.XSDboolean)); } @Override public void setByte(int parameterIndex, byte value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(Byte.toString(value), XSDDatatype.XSDbyte)); } @Override public void setBytes(int parameterIndex, byte[] value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setCharacterStream(int parameterIndex, Reader value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setCharacterStream(int parameterIndex, Reader value, int arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setCharacterStream(int parameterIndex, Reader value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setClob(int parameterIndex, Clob value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setClob(int parameterIndex, Reader value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setClob(int parameterIndex, Reader value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setDate(int parameterIndex, Date value) throws SQLException { Calendar c = Calendar.getInstance(); c.setTimeInMillis(value.getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode(c)); } @Override public void setDate(int parameterIndex, Date value, Calendar arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setDouble(int parameterIndex, double value) throws SQLException { this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode(value)); } @Override public void setFloat(int parameterIndex, float value) throws SQLException { this.setParameter(parameterIndex, NodeFactoryExtra.floatToNode(value)); } @Override public void setInt(int parameterIndex, int value) throws SQLException { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode(value)); } @Override public void setLong(int parameterIndex, long value) throws SQLException { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode(value)); } @Override public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setNCharacterStream(int parameterIndex, Reader value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setNClob(int parameterIndex, NClob value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setNClob(int parameterIndex, Reader value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setNClob(int parameterIndex, Reader value, long arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setNString(int parameterIndex, String value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(value)); } @Override public void setNull(int parameterIndex, int value) throws SQLException { throw new SQLFeatureNotSupportedException("Parameters for SPARQL statements are not nullable"); } @Override public void setNull(int parameterIndex, int value, String arg2) throws SQLException { throw new SQLFeatureNotSupportedException("Parameters for SPARQL statements are not nullable"); } @Override public void setObject(int parameterIndex, Object value) throws SQLException { if (value == null) throw new SQLException("Setting a null value is not permitted"); if (value instanceof Node) { this.setParameter(parameterIndex, (Node) value); } else if (value instanceof RDFNode) { this.setParameter(parameterIndex, ((RDFNode) value).asNode()); } else if (value instanceof String) { this.setParameter(parameterIndex, NodeFactory.createLiteral((String) value)); } else if (value instanceof Boolean) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString((Boolean) value), XSDDatatype.XSDboolean)); } else if (value instanceof Long) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Long) value)); } else if (value instanceof Integer) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Integer) value)); } else if (value instanceof Short) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString((Short) value), XSDDatatype.XSDshort)); } else if (value instanceof Byte) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Byte.toString((Byte) value), XSDDatatype.XSDbyte)); } else if (value instanceof BigDecimal) { this.setParameter(parameterIndex, NodeFactory.createLiteral(((BigDecimal) value).toPlainString(), XSDDatatype.XSDdecimal)); } else if (value instanceof Float) { this.setParameter(parameterIndex, NodeFactoryExtra.floatToNode((Float) value)); } else if (value instanceof Double) { this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode((Double) value)); } else if (value instanceof Date) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Date) value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode(c)); } else if (value instanceof Time) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Time) value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Calendar) { this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode((Calendar) value)); } else if (value instanceof URL) { this.setParameter(parameterIndex, NodeFactory.createURI(value.toString())); } else if (value instanceof URI) { this.setParameter(parameterIndex, NodeFactory.createURI(value.toString())); } else if (value instanceof IRI) { this.setParameter(parameterIndex, NodeFactory.createURI(value.toString())); } else { throw new SQLException( "setObject() received a value that could not be converted to a RDF node for use in a SPARQL query"); } } @Override public void setObject(int parameterIndex, Object value, int targetSqlType) throws SQLException { if (value == null) throw new SQLException("Setting a null value is not permitted"); try { switch (targetSqlType) { case Types.ARRAY: case Types.BINARY: case Types.BIT: case Types.BLOB: case Types.CLOB: case Types.DATALINK: case Types.DISTINCT: case Types.LONGNVARCHAR: case Types.LONGVARBINARY: case Types.LONGVARCHAR: case Types.NCHAR: case Types.NCLOB: case Types.NULL: case Types.NUMERIC: case Types.OTHER: case Types.REAL: case Types.REF: case Types.ROWID: case Types.SQLXML: case Types.STRUCT: case Types.VARBINARY: throw new SQLException("The provided SQL Target Type cannot be translated into an appropriate RDF term type"); case Types.BIGINT: if (value instanceof Long) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Long) value)); } else if (value instanceof Integer) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((long)(Integer)value)); } else if (value instanceof Short) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((long)(Short)value)); } else if (value instanceof Byte) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((long)(Byte)value)); } else if (value instanceof Node) { long l = JdbcNodeUtils.toLong((Node) value); this.setParameter(parameterIndex, NodeFactoryExtra.intToNode(l)); } else if (value instanceof String) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode(Long.parseLong((String)value))); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.BOOLEAN: if (value instanceof Boolean) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString((Boolean) value), XSDDatatype.XSDboolean)); } else if (value instanceof Node) { boolean b = JdbcNodeUtils.toBoolean((Node) value); this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString(b), XSDDatatype.XSDboolean)); } else if (value instanceof String) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString(Boolean.parseBoolean((String)value)), XSDDatatype.XSDboolean)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.DATE: if (value instanceof Date) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Date) value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode(c)); } else if (value instanceof Node) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(JdbcNodeUtils.toDate((Node)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode(c)); } else if (value instanceof Time) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Time) value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Calendar) { this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode((Calendar) value)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.DECIMAL: if (value instanceof BigDecimal) { this.setParameter(parameterIndex, NodeFactory.createLiteral(((BigDecimal)value).toPlainString(), XSDDatatype.XSDdecimal)); } else if (value instanceof Node) { BigDecimal d = JdbcNodeUtils.toDecimal((Node)value); this.setParameter(parameterIndex, NodeFactory.createLiteral(d.toPlainString(), XSDDatatype.XSDdecimal)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.DOUBLE: if (value instanceof Double) { this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode((Double)value)); } else if (value instanceof Float) { this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode((Float)value)); } else if (value instanceof Node) { Double d = JdbcNodeUtils.toDouble((Node)value); this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode(d)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.FLOAT: if (value instanceof Float) { this.setParameter(parameterIndex, NodeFactoryExtra.floatToNode((Float)value)); } else if (value instanceof Node) { Float f = JdbcNodeUtils.toFloat((Node)value); this.setParameter(parameterIndex, NodeFactoryExtra.floatToNode(f)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.INTEGER: if (value instanceof Integer) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Integer)value)); } else if (value instanceof Short) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Short)value)); } else if (value instanceof Byte) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Byte)value)); } else if (value instanceof Node) { Integer i = JdbcNodeUtils.toInt((Node)value); this.setParameter(parameterIndex, NodeFactoryExtra.intToNode(i)); } else { throw new SQLException("The given value is not marshallable to the desired target type"); } break; case Types.JAVA_OBJECT: if (value instanceof Node) { this.setParameter(parameterIndex, (Node) value); } else if (value instanceof RDFNode) { this.setParameter(parameterIndex, ((RDFNode) value).asNode()); } else if (value instanceof String) { this.setParameter(parameterIndex, NodeFactory.createLiteral((String) value)); } else if (value instanceof URL) { this.setParameter(parameterIndex, NodeFactory.createURI(((URL) value).toString())); } else if (value instanceof URI) { this.setParameter(parameterIndex, NodeFactory.createURI(((URI)value).toString())); } else if (value instanceof IRI) { this.setParameter(parameterIndex, NodeFactory.createURI(((IRI)value).toString())); } else if (value instanceof BigDecimal) { this.setParameter(parameterIndex, NodeFactory.createLiteral(((BigDecimal)value).toPlainString(), XSDDatatype.XSDdecimal)); } else if (value instanceof Boolean) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Boolean.toString(((Boolean)value)), XSDDatatype.XSDboolean)); } else if (value instanceof Byte) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Byte.toString((Byte)value), XSDDatatype.XSDbyte)); } else if (value instanceof Double) { this.setParameter(parameterIndex, NodeFactoryExtra.doubleToNode((Double)value)); } else if (value instanceof Float) { this.setParameter(parameterIndex, NodeFactoryExtra.floatToNode((Float)value)); } else if (value instanceof Short) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString((Short)value), XSDDatatype.XSDshort)); } else if (value instanceof Integer) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Integer)value)); } else if (value instanceof Long) { this.setParameter(parameterIndex, NodeFactoryExtra.intToNode((Long)value)); } else if (value instanceof Date) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Date)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode(c)); } else if (value instanceof Time) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Time)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Calendar) { this.setParameter(parameterIndex, NodeFactoryExtra.dateTimeToNode((Calendar)value)); } else { this.setParameter(parameterIndex, NodeFactory.createLiteral(value.toString())); } break; case Types.CHAR: case Types.NVARCHAR: case Types.VARCHAR: this.setParameter(parameterIndex, NodeFactory.createLiteral(value.toString())); break; case Types.SMALLINT: if (value instanceof Short) { Short s = (Short)value; this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString(s), XSDDatatype.XSDshort)); } else if (value instanceof Byte) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString((Byte)value), XSDDatatype.XSDshort)); } else if (value instanceof Node) { Short s = JdbcNodeUtils.toShort((Node)value); this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString(s), XSDDatatype.XSDshort)); } else if (value instanceof String) { this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString(Short.parseShort((String)value)), XSDDatatype.XSDshort)); } else { throw new SQLException("The given value is not marshallable to the desired type"); } break; case Types.TIME: if (value instanceof Time) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Time)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Node) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(JdbcNodeUtils.toDate((Node)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Date) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(((Date)value).getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } else if (value instanceof Calendar) { this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode((Calendar)value)); } else { throw new SQLException("The given value is not marshallable to the desired type"); } break; case Types.TINYINT: if (value instanceof Byte) { Byte b = (Byte)value; this.setParameter(parameterIndex, NodeFactory.createLiteral(Byte.toString(b), XSDDatatype.XSDbyte)); } else if (value instanceof Node) { Byte b = JdbcNodeUtils.toByte((Node)value); this.setParameter(parameterIndex, NodeFactory.createLiteral(Byte.toString(b), XSDDatatype.XSDbyte)); } else { throw new SQLException("The given value is not marshallable to the desired type"); } break; default: throw new SQLException("Cannot translate an unknown SQL Target Type into an appropriate RDF term type"); } } catch (SQLException e) { // Throw as-is throw e; } catch (Throwable e) { // Wrap as SQL Exception throw new SQLException("Unexpected error trying to marshal a value to the desired target type", e); } } @Override public void setObject(int parameterIndex, Object value, int arg2, int arg3) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setRef(int parameterIndex, Ref value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setRowId(int parameterIndex, RowId value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setSQLXML(int parameterIndex, SQLXML value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setShort(int parameterIndex, short value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(Short.toString(value), XSDDatatype.XSDshort)); } @Override public void setString(int parameterIndex, String value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createLiteral(value)); } @Override public void setTime(int parameterIndex, Time value) throws SQLException { Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); c.setTimeInMillis(value.getTime()); this.setParameter(parameterIndex, NodeFactoryExtra.timeToNode(c)); } @Override public void setTime(int parameterIndex, Time value, Calendar arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setTimestamp(int parameterIndex, Timestamp value) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setTimestamp(int parameterIndex, Timestamp value, Calendar arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public void setURL(int parameterIndex, URL value) throws SQLException { this.setParameter(parameterIndex, NodeFactory.createURI(value.toString())); } @Deprecated @Override public void setUnicodeStream(int parameterIndex, InputStream value, int arg2) throws SQLException { throw new SQLFeatureNotSupportedException(); } /** * Gets a copy of the underlying {@link ParameterizedSparqlString} used to * implement this prepared statement * * @return Copy of the underlying string */ public ParameterizedSparqlString getParameterizedString() { return this.sparqlStr.copy(); } }