| /** |
| * 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.sql.Connection; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.SQLFeatureNotSupportedException; |
| import java.sql.SQLWarning; |
| import java.sql.Statement; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Queue; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.jena.atlas.iterator.Iter; |
| import org.apache.jena.jdbc.JdbcCompatibility; |
| import org.apache.jena.jdbc.connections.JenaConnection; |
| import org.apache.jena.jdbc.results.AskResults; |
| import org.apache.jena.jdbc.results.MaterializedSelectResults; |
| import org.apache.jena.jdbc.results.SelectResults; |
| import org.apache.jena.jdbc.results.TripleIteratorResults; |
| import org.apache.jena.jdbc.results.TripleListResults; |
| import org.apache.jena.query.* ; |
| import org.apache.jena.update.UpdateFactory ; |
| import org.apache.jena.update.UpdateProcessor ; |
| import org.apache.jena.update.UpdateRequest ; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Abstract Jena JDBC implementation of a statement that only permits read |
| * operations |
| * |
| */ |
| public abstract class JenaStatement implements Statement { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(JenaStatement.class); |
| |
| protected static final int DEFAULT_HOLDABILITY = ResultSet.CLOSE_CURSORS_AT_COMMIT; |
| protected static final int DEFAULT_FETCH_DIRECTION = ResultSet.FETCH_FORWARD; |
| protected static final int DEFAULT_FETCH_SIZE = 0; |
| protected static final boolean DEFAULT_AUTO_COMMIT = JenaConnection.DEFAULT_AUTO_COMMIT; |
| protected static final int DEFAULT_TRANSACTION_LEVEL = JenaConnection.DEFAULT_ISOLATION_LEVEL; |
| protected static final int NO_LIMIT = 0; |
| protected static final int DEFAULT_TYPE = ResultSet.TYPE_FORWARD_ONLY; |
| protected static final int USE_CONNECTION_COMPATIBILITY = Integer.MIN_VALUE; |
| |
| private List<String> commands = new ArrayList<>(); |
| private SQLWarning warnings = null; |
| private JenaConnection connection; |
| private ResultSet currResults = null; |
| private Queue<ResultSet> results = new LinkedList<>(); |
| private List<ResultSet> openResults = new ArrayList<>(); |
| private boolean closed = false; |
| private int type = DEFAULT_TYPE; |
| private int fetchDirection = DEFAULT_FETCH_DIRECTION; |
| private int fetchSize = DEFAULT_FETCH_SIZE; |
| private int holdability = DEFAULT_HOLDABILITY; |
| private int updateCount = 0; |
| private boolean autoCommit = DEFAULT_AUTO_COMMIT; |
| private int transactionLevel = DEFAULT_TRANSACTION_LEVEL; |
| private int maxRows = NO_LIMIT; |
| @SuppressWarnings("unused") |
| private boolean escapeProcessing = false; |
| private int timeout = NO_LIMIT; |
| private int compatibilityLevel = USE_CONNECTION_COMPATIBILITY; |
| |
| /** |
| * Creates a new statement |
| * |
| * @param connection |
| * Connection |
| * @throws SQLException |
| * Thrown if the arguments are invalid |
| */ |
| public JenaStatement(JenaConnection connection) throws SQLException { |
| this(connection, DEFAULT_TYPE, DEFAULT_FETCH_DIRECTION, DEFAULT_FETCH_SIZE, DEFAULT_HOLDABILITY, DEFAULT_AUTO_COMMIT, |
| DEFAULT_TRANSACTION_LEVEL); |
| } |
| |
| /** |
| * Creates a new statement |
| * |
| * @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 behaviour |
| * @param transactionLevel |
| * Transaction level |
| * @throws SQLException |
| * Thrown if there is an error with the statement parameters |
| * |
| */ |
| public JenaStatement(JenaConnection connection, int type, int fetchDir, int fetchSize, int holdability, boolean autoCommit, |
| int transactionLevel) throws SQLException { |
| if (connection == null) |
| throw new SQLException("Cannot create a Statement with a null connection"); |
| this.connection = connection; |
| this.checkFetchDirection(fetchDir); |
| this.type = type; |
| this.fetchDirection = fetchDir; |
| this.fetchSize = fetchSize; |
| this.checkHoldability(holdability); |
| this.holdability = holdability; |
| this.autoCommit = autoCommit; |
| this.transactionLevel = transactionLevel; |
| } |
| |
| /** |
| * Gets the underlying {@link JenaConnection} implementation, useful for |
| * accessing Jena JDBC specific information such as desired JDBC |
| * compatibility level |
| * |
| * @return Underlying Jena Connection |
| */ |
| public JenaConnection getJenaConnection() { |
| return this.connection; |
| } |
| |
| /** |
| * Gets the JDBC compatibility level that is in use, see |
| * {@link JdbcCompatibility} for explanations |
| * <p> |
| * By default this is set at the connection level and inherited, however you |
| * may call {@link #setJdbcCompatibilityLevel(int)} to set the compatibility |
| * level for this statement. This allows you to change the compatibility |
| * level on a per-query basis if so desired. |
| * </p> |
| * |
| * @return Compatibility level |
| */ |
| public int getJdbcCompatibilityLevel() { |
| if (this.compatibilityLevel == USE_CONNECTION_COMPATIBILITY) |
| return this.connection.getJdbcCompatibilityLevel(); |
| return this.compatibilityLevel; |
| } |
| |
| /** |
| * Sets the JDBC compatibility level that is in use, see |
| * {@link JdbcCompatibility} for explanations. |
| * <p> |
| * By default this is set at the connection level and inherited, however you |
| * may call {@link #setJdbcCompatibilityLevel(int)} to set the compatibility |
| * level for this statement. This allows you to change the compatibility |
| * level on a per-query basis if so desired. |
| * </p> |
| * <p> |
| * Changing the level may not effect existing open objects, behaviour in |
| * this case will be implementation specific. |
| * </p> |
| * |
| * @param level |
| * Compatibility level |
| */ |
| public void setJdbcCompatibilityLevel(int level) { |
| if (level == USE_CONNECTION_COMPATIBILITY) { |
| this.compatibilityLevel = USE_CONNECTION_COMPATIBILITY; |
| } else { |
| this.compatibilityLevel = JdbcCompatibility.normalizeLevel(level); |
| } |
| } |
| |
| @Override |
| public boolean isWrapperFor(Class<?> iface) throws SQLException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| @Override |
| public <T> T unwrap(Class<T> iface) throws SQLException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| @Override |
| public void addBatch(String sql) { |
| this.commands.add(sql); |
| } |
| |
| @Override |
| public void cancel() throws SQLException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| @Override |
| public void clearBatch() { |
| this.commands.clear(); |
| } |
| |
| @Override |
| public void clearWarnings() { |
| this.warnings = null; |
| } |
| |
| @Override |
| public void close() throws SQLException { |
| if (this.closed) |
| return; |
| LOGGER.info("Closing statement"); |
| this.closed = true; |
| // Close current result set (if any) |
| if (this.currResults != null) { |
| this.currResults.close(); |
| this.currResults = null; |
| } |
| // Close any remaining open results |
| if (this.results.size() > 0 || this.openResults.size() > 0) { |
| LOGGER.info("Closing " + (this.results.size() + this.openResults.size()) + " open result sets"); |
| |
| // Queue results i.e. stuff resulting from a query that produced |
| // multiple result sets or executeBatch() calls |
| while (!this.results.isEmpty()) { |
| ResultSet rset = this.results.poll(); |
| if (rset != null) |
| rset.close(); |
| } |
| // Close open result sets i.e. stuff left around depending on |
| // statement correction |
| for (ResultSet rset : this.openResults) { |
| rset.close(); |
| } |
| this.openResults.clear(); |
| LOGGER.info("All open result sets were closed"); |
| } |
| LOGGER.info("Statement was closed"); |
| } |
| |
| @Override |
| public final boolean execute(String sql) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| // Pre-process the command text |
| LOGGER.info("Received input command text:\n {}", sql); |
| sql = this.connection.applyPreProcessors(sql); |
| LOGGER.info("Command text after pre-processing:\n {}", sql); |
| |
| Query q = null; |
| UpdateRequest u = null; |
| try { |
| // Start by assuming a query |
| q = QueryFactory.create(sql); |
| } catch (Exception e) { |
| try { |
| // If that fails try as an update instead |
| u = UpdateFactory.create(sql); |
| } catch (Exception e2) { |
| LOGGER.error("Command text was not a valid SPARQL query/update", e2); |
| throw new SQLException("Not a valid SPARQL query/update", e); |
| } |
| } |
| |
| if (q != null) { |
| // Execute as a query |
| LOGGER.info("Treating command text as a query"); |
| return this.executeQuery(q); |
| } else if (u != null) { |
| // Execute as an update |
| LOGGER.info("Treating command text as an update"); |
| this.executeUpdate(u); |
| return false; |
| } else { |
| throw new SQLException("Unable to create a SPARQL query/update"); |
| } |
| } |
| |
| private boolean executeQuery(Query q) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| // Do we need transactions? |
| boolean needsBegin = (!this.autoCommit && this.transactionLevel != Connection.TRANSACTION_NONE && !this |
| .hasActiveTransaction()); |
| boolean needsCommit = (this.autoCommit && this.transactionLevel != Connection.TRANSACTION_NONE); |
| |
| // Do this first in a separate try catch so if we fail to start a |
| // transaction we don't then try to roll it back which can mask the |
| // actual cause of the error |
| try { |
| // Start a transaction if necessary |
| if (needsCommit) { |
| LOGGER.info("Running query in auto-commit mode"); |
| this.beginTransaction(ReadWrite.READ); |
| } else if (needsBegin) { |
| LOGGER.info("Starting a new transaction to run query, transaction will not be auto-committed"); |
| this.beginTransaction(ReadWrite.WRITE); |
| } |
| } catch (Exception e) { |
| LOGGER.error("Starting the new transaction failed", e); |
| throw new SQLException("Failed to start a new query transaction", e); |
| } |
| |
| try { |
| // Pre-process the query |
| q = this.connection.applyPreProcessors(q); |
| |
| // Manipulate the query if appropriate |
| if (this.maxRows > NO_LIMIT) { |
| // If we have no LIMIT or the LIMIT is greater than the |
| // permitted max rows |
| // then we will set the LIMIT to the max rows |
| if (!q.hasLimit() || q.getLimit() > this.maxRows) { |
| LOGGER.info("Enforced max rows on results by applying LIMIT {} to the query", this.maxRows); |
| q.setLimit(this.maxRows); |
| } |
| } |
| |
| // Create the query execution |
| QueryExecution qe = this.createQueryExecution(q); |
| |
| // Manipulate the query execution if appropriate |
| if (this.timeout > NO_LIMIT) { |
| qe.setTimeout(this.timeout, TimeUnit.SECONDS, this.timeout, TimeUnit.SECONDS); |
| } |
| |
| // Return the appropriate result set type |
| if (q.isSelectType()) { |
| switch (this.type) { |
| case ResultSet.TYPE_SCROLL_INSENSITIVE: |
| this.currResults = new MaterializedSelectResults(this, qe, ResultSetFactory.makeRewindable(this.connection |
| .applyPostProcessors(qe.execSelect())), false); |
| break; |
| case ResultSet.TYPE_FORWARD_ONLY: |
| default: |
| this.currResults = new SelectResults(this, qe, this.connection.applyPostProcessors(qe.execSelect()), |
| needsCommit); |
| break; |
| } |
| } else if (q.isAskType()) { |
| boolean askRes = qe.execAsk(); |
| qe.close(); |
| this.currResults = new AskResults(this, this.connection.applyPostProcessors(askRes), needsCommit); |
| } else if (q.isDescribeType()) { |
| switch (this.type) { |
| case ResultSet.TYPE_SCROLL_INSENSITIVE: |
| this.currResults = new TripleListResults(this, qe, Iter.toList(this.connection.applyPostProcessors(qe |
| .execDescribeTriples())), false); |
| break; |
| case ResultSet.TYPE_FORWARD_ONLY: |
| default: |
| this.currResults = new TripleIteratorResults(this, qe, this.connection.applyPostProcessors(qe |
| .execDescribeTriples()), needsCommit); |
| break; |
| } |
| } else if (q.isConstructType()) { |
| switch (this.type) { |
| case ResultSet.TYPE_SCROLL_INSENSITIVE: |
| this.currResults = new TripleListResults(this, qe, Iter.toList(this.connection.applyPostProcessors(qe |
| .execConstructTriples())), false); |
| break; |
| case ResultSet.TYPE_FORWARD_ONLY: |
| default: |
| this.currResults = new TripleIteratorResults(this, qe, this.connection.applyPostProcessors(qe |
| .execConstructTriples()), needsCommit); |
| break; |
| } |
| } else { |
| throw new SQLException("Unknown SPARQL Query type"); |
| } |
| |
| // Can immediately commit when type is |
| // TYPE_SCROLL_INSENSITIVE and auto-committing since we have |
| // already materialized results so don't need the read |
| // transaction |
| if (this.type == ResultSet.TYPE_SCROLL_INSENSITIVE && needsCommit) { |
| LOGGER.info("Auto-committing query transaction since results have been materialized"); |
| this.commitTransaction(); |
| } |
| |
| return true; |
| } catch (SQLException e) { |
| if (needsCommit) { |
| // When auto-committing and query fails roll back immediately |
| LOGGER.warn("Rolling back failed query transaction", e); |
| this.rollbackTransaction(); |
| } |
| throw e; |
| } catch (Throwable e) { |
| LOGGER.error("SPARQL Query evaluation failed", e); |
| if (needsCommit) { |
| // When auto-committing and query fails roll back immediately |
| LOGGER.warn("Rolling back failed query transaction"); |
| this.rollbackTransaction(); |
| } |
| throw new SQLException("Error occurred during SPARQL query evaluation", e); |
| } |
| } |
| |
| /** |
| * Helper method which derived classes must implement to provide a query |
| * execution |
| * |
| * @param q |
| * Query |
| * @return Query Execution |
| * @throws SQLException |
| * Thrown if there is a problem creating a query execution |
| */ |
| protected abstract QueryExecution createQueryExecution(Query q) throws SQLException; |
| |
| private int executeUpdate(UpdateRequest u) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| if (this.connection.isReadOnly()) |
| throw new SQLException("The JDBC connection is currently in read-only mode, updates are not permitted"); |
| |
| // Do we need transactions? |
| boolean needsBegin = (!this.autoCommit && this.transactionLevel != Connection.TRANSACTION_NONE && !this |
| .hasActiveTransaction()); |
| boolean needsCommit = (this.autoCommit && this.transactionLevel != Connection.TRANSACTION_NONE); |
| |
| try { |
| // Start a Transaction if necessary |
| if (needsCommit || needsBegin) { |
| if (this.autoCommit) { |
| LOGGER.info("Running update in auto-commit mode"); |
| } else { |
| LOGGER.info("Starting a new transaction to run update, transaction will not be auto-committed"); |
| } |
| this.beginTransaction(ReadWrite.WRITE); |
| } |
| } catch (Exception e) { |
| LOGGER.error("Starting the new transaction failed", e); |
| throw new SQLException("Failed to start a new query transaction", e); |
| } |
| |
| try { |
| // Pre-process the update |
| u = this.connection.applyPreProcessors(u); |
| |
| // Execute updates |
| UpdateProcessor processor = this.createUpdateProcessor(u); |
| processor.execute(); |
| |
| // If auto-committing can commit immediately |
| if (needsCommit) { |
| LOGGER.info("Auto-committing update transaction"); |
| this.commitTransaction(); |
| } |
| |
| return 0; |
| } catch (SQLException e) { |
| if (needsCommit) { |
| LOGGER.warn("Rolling back failed update transaction", e); |
| this.rollbackTransaction(); |
| } |
| throw e; |
| } catch (Exception e) { |
| LOGGER.error("SPARQL Update evaluation failed", e); |
| if (needsCommit) { |
| LOGGER.warn("Rolling back failed update transaction"); |
| this.rollbackTransaction(); |
| } |
| throw new SQLException("Error occurred during SPARQL update evaluation", e); |
| } |
| } |
| |
| /** |
| * Helper method which derived classes must implement to provide an update |
| * processor |
| * |
| * @param u |
| * Update |
| * @return Update Processor |
| */ |
| protected abstract UpdateProcessor createUpdateProcessor(UpdateRequest u) throws SQLException; |
| |
| protected abstract boolean hasActiveTransaction() throws SQLException; |
| |
| protected abstract void beginTransaction(ReadWrite type) throws SQLException; |
| |
| protected abstract void commitTransaction() throws SQLException; |
| |
| protected abstract void rollbackTransaction() throws SQLException; |
| |
| @Override |
| public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { |
| return this.execute(sql); |
| } |
| |
| @Override |
| public boolean execute(String sql, int[] columnIndexes) throws SQLException { |
| return this.execute(sql); |
| } |
| |
| @Override |
| public boolean execute(String sql, String[] columnNames) throws SQLException { |
| return this.execute(sql); |
| } |
| |
| @Override |
| public final int[] executeBatch() throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| // Issue warning where appropriate |
| if (this.commands.size() > 1 && this.autoCommit && this.holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT) { |
| this.setWarning("Executing this batch of commands may lead to unexpectedly closed result sets because auto-commit is enabled and commit behaviour is set to close cursors at commit"); |
| } |
| |
| // Go ahead and process the batch |
| int[] rets = new int[this.commands.size()]; |
| ResultSet curr = this.currResults; |
| for (int i = 0; i < this.commands.size(); i++) { |
| if (this.execute(this.commands.get(i))) { |
| // True means it returned a ResultSet |
| this.results.add(this.getResultSet()); |
| this.currResults = null; |
| rets[i] = SUCCESS_NO_INFO; |
| } else { |
| // Need to add a null to getMoreResults() to produce correct |
| // behavior across subsequent calls to getMoreResults() |
| this.results.add(null); |
| rets[i] = this.getUpdateCount(); |
| } |
| } |
| this.currResults = curr; |
| // Make the next available results the current results if there |
| // are no current results |
| if (this.currResults == null && !this.results.isEmpty()) { |
| this.currResults = this.results.poll(); |
| } |
| return rets; |
| } |
| |
| @Override |
| public final ResultSet executeQuery(String sql) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| // Pre-process the command text |
| LOGGER.info("Received input command text:\n {}", sql); |
| sql = this.connection.applyPreProcessors(sql); |
| LOGGER.info("Command text after pre-processing:\n {}", sql); |
| |
| Query q = null; |
| try { |
| q = QueryFactory.create(sql); |
| } catch (Exception e) { |
| LOGGER.error("Invalid SPARQL query", e); |
| throw new SQLException("Not a valid SPARQL query", e); |
| } |
| |
| if (q == null) |
| throw new SQLException("Unable to create a SPARQL Query"); |
| if (this.executeQuery(q)) { |
| return this.currResults; |
| } else { |
| throw new SQLException("Query did not produce a result set"); |
| } |
| } |
| |
| @Override |
| public final int executeUpdate(String sql) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| if (this.connection.isReadOnly()) |
| throw new SQLException("The JDBC connection is currently in read-only mode, updates are not permitted"); |
| |
| // Pre-process the command text |
| LOGGER.info("Received input command text:\n {}", sql); |
| sql = this.connection.applyPreProcessors(sql); |
| LOGGER.info("Command text after pre-processing:\n {}", sql); |
| |
| UpdateRequest u = null; |
| try { |
| u = UpdateFactory.create(sql); |
| } catch (Exception e) { |
| LOGGER.error("Invalid SPARQL update", e); |
| throw new SQLException("Not a valid SPARQL Update", e); |
| } |
| |
| if (u == null) |
| throw new SQLException("Unable to create a SPARQL Update Request"); |
| return this.executeUpdate(u); |
| } |
| |
| @Override |
| public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { |
| return this.executeUpdate(sql); |
| } |
| |
| @Override |
| public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { |
| return this.executeUpdate(sql); |
| } |
| |
| @Override |
| public int executeUpdate(String sql, String[] columnNames) throws SQLException { |
| return this.executeUpdate(sql); |
| } |
| |
| @Override |
| public final Connection getConnection() { |
| return this.connection; |
| } |
| |
| @Override |
| public int getFetchDirection() { |
| return this.fetchDirection; |
| } |
| |
| @Override |
| public int getFetchSize() { |
| return this.fetchSize; |
| } |
| |
| @Override |
| public ResultSet getGeneratedKeys() throws SQLException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| @Override |
| public int getMaxFieldSize() { |
| return NO_LIMIT; |
| } |
| |
| @Override |
| public int getMaxRows() { |
| return maxRows; |
| } |
| |
| @Override |
| public boolean getMoreResults() throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| if (this.currResults != null) { |
| this.currResults.close(); |
| this.currResults = null; |
| } |
| if (!this.results.isEmpty()) { |
| this.currResults = this.results.poll(); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @Override |
| public boolean getMoreResults(int current) throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| |
| switch (current) { |
| case Statement.CLOSE_CURRENT_RESULT: |
| return this.getMoreResults(); |
| case Statement.CLOSE_ALL_RESULTS: |
| for (ResultSet rset : this.openResults) { |
| rset.close(); |
| } |
| this.openResults.clear(); |
| return this.getMoreResults(); |
| case Statement.KEEP_CURRENT_RESULT: |
| if (this.currResults != null) { |
| this.openResults.add(this.currResults); |
| this.currResults = null; |
| } |
| return this.getMoreResults(); |
| default: |
| throw new SQLFeatureNotSupportedException( |
| "Unsupported mode for dealing with current results, only Statement.CLOSE_CURRENT_RESULT, Statement.CLOSE_ALL_RESULTS and Statement.KEEP_CURRENT_RESULT are supported"); |
| } |
| } |
| |
| @Override |
| public int getQueryTimeout() { |
| return this.timeout; |
| } |
| |
| @Override |
| public final ResultSet getResultSet() throws SQLException { |
| if (this.isClosed()) |
| throw new SQLException("The Statement is closed"); |
| return this.currResults; |
| } |
| |
| /** |
| * Helper method for use in execute() method implementations to set the |
| * current results |
| * |
| * @param results |
| * Results |
| * @throws SQLException |
| * Thrown if there is an error closing the previous results |
| */ |
| protected void setCurrentResults(ResultSet results) throws SQLException { |
| if (this.currResults != null) { |
| this.currResults.close(); |
| } |
| this.currResults = results; |
| } |
| |
| /** |
| * Gets that result sets are read-only |
| */ |
| @Override |
| public final int getResultSetConcurrency() { |
| return ResultSet.CONCUR_READ_ONLY; |
| } |
| |
| @Override |
| public int getResultSetHoldability() { |
| return this.holdability; |
| } |
| |
| protected void checkHoldability(int h) throws SQLException { |
| switch (h) { |
| case ResultSet.CLOSE_CURSORS_AT_COMMIT: |
| case ResultSet.HOLD_CURSORS_OVER_COMMIT: |
| return; |
| default: |
| throw new SQLException(String.format("Holdability %d is supported for Jena JDBC statements", h)); |
| } |
| } |
| |
| @Override |
| public final int getResultSetType() { |
| return this.type; |
| } |
| |
| @Override |
| public int getUpdateCount() { |
| return this.updateCount; |
| } |
| |
| /** |
| * Helper method which derived classes may use to set the update count |
| * |
| * @param count |
| * Update Count |
| */ |
| protected void setUpdateCount(int count) { |
| this.updateCount = count; |
| } |
| |
| @Override |
| public SQLWarning getWarnings() { |
| return this.warnings; |
| } |
| |
| /** |
| * Helper method that derived classes may use to set warnings |
| * |
| * @param warning |
| * Warning |
| */ |
| protected void setWarning(SQLWarning warning) { |
| LOGGER.warn("SQL Warning was issued", warning); |
| if (this.warnings == null) { |
| this.warnings = warning; |
| } else { |
| // Chain with existing warnings |
| warning.setNextWarning(this.warnings); |
| this.warnings = warning; |
| } |
| } |
| |
| /** |
| * Helper method that derived classes may use to set warnings |
| * |
| * @param warning |
| * Warning |
| */ |
| protected void setWarning(String warning) { |
| this.setWarning(new SQLWarning(warning)); |
| } |
| |
| /** |
| * Helper method that derived classes may use to set warnings |
| * |
| * @param warning |
| * Warning |
| * @param cause |
| * Cause |
| */ |
| protected void setWarning(String warning, Throwable cause) { |
| this.setWarning(new SQLWarning(warning, cause)); |
| } |
| |
| @Override |
| public final boolean isClosed() { |
| return this.closed; |
| } |
| |
| @Override |
| public final boolean isPoolable() { |
| return true; |
| } |
| |
| @Override |
| public void setCursorName(String name) throws SQLException { |
| throw new SQLFeatureNotSupportedException(); |
| } |
| |
| @Override |
| public void setEscapeProcessing(boolean enable) { |
| this.escapeProcessing = enable; |
| } |
| |
| @Override |
| public void setFetchDirection(int direction) throws SQLException { |
| this.checkFetchDirection(direction); |
| this.fetchDirection = direction; |
| } |
| |
| /** |
| * Helper method which checks whether a given fetch direction is valid |
| * throwing an error if it is not |
| * |
| * @param dir |
| * Fetch Direction |
| * @throws SQLException |
| * Thrown if the direction is not valid |
| */ |
| protected void checkFetchDirection(int dir) throws SQLException { |
| switch (dir) { |
| case ResultSet.FETCH_FORWARD: |
| return; |
| default: |
| throw new SQLFeatureNotSupportedException("Only ResultSet.FETCH_FORWARD is supported as a fetch direction"); |
| } |
| } |
| |
| @Override |
| public void setFetchSize(int rows) { |
| this.fetchSize = rows; |
| } |
| |
| @Override |
| public void setMaxFieldSize(int max) { |
| // Ignored |
| this.setWarning("setMaxFieldSize() was called but there is no field size limit for Jena JDBC connections"); |
| } |
| |
| @Override |
| public void setMaxRows(int max) { |
| if (max <= NO_LIMIT) { |
| this.maxRows = NO_LIMIT; |
| } else { |
| this.maxRows = max; |
| } |
| } |
| |
| @Override |
| public void setPoolable(boolean poolable) { |
| // Ignored |
| this.setWarning("setPoolable() was called but Jena JDBC statements are always considered poolable"); |
| } |
| |
| @Override |
| public void setQueryTimeout(int seconds) { |
| if (seconds <= NO_LIMIT) { |
| this.timeout = NO_LIMIT; |
| } else { |
| this.timeout = seconds; |
| } |
| } |
| |
| @Override |
| public boolean isCloseOnCompletion() { |
| // Statements do not automatically close |
| return false; |
| } |
| |
| @Override |
| public void closeOnCompletion() throws SQLException { |
| // We don't support the JDBC 4.1 feature of closing statements automatically |
| throw new SQLFeatureNotSupportedException(); |
| } |
| } |