| /* |
| * 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.calcite.avatica.jdbc; |
| |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.SQLFeatureNotSupportedException; |
| import java.sql.Statement; |
| |
| /** |
| * All we know about a statement. Encapsulates a {@link ResultSet}. |
| */ |
| public class StatementInfo { |
| private volatile Boolean relativeSupported = null; |
| |
| final Statement statement; // sometimes a PreparedStatement |
| private ResultSet resultSet; |
| private long position = 0; |
| |
| // True when setResultSet(ResultSet) is called to let us determine the difference between |
| // a null ResultSet (from an update) from the lack of a ResultSet. |
| private boolean resultsInitialized = false; |
| |
| public StatementInfo(Statement statement) { |
| // May be null when coming from a DatabaseMetaData call |
| this.statement = statement; |
| } |
| |
| // Visible for testing |
| void setPosition(long position) { |
| this.position = position; |
| } |
| |
| // Visible for testing |
| long getPosition() { |
| return this.position; |
| } |
| |
| /** |
| * Set a ResultSet on this object. |
| * |
| * @param resultSet The current ResultSet |
| */ |
| public void setResultSet(ResultSet resultSet) { |
| resultsInitialized = true; |
| this.resultSet = resultSet; |
| } |
| |
| /** |
| * @return The {@link ResultSet} for this Statement, may be null. |
| */ |
| public ResultSet getResultSet() { |
| return this.resultSet; |
| } |
| |
| /** |
| * @return True if {@link #setResultSet(ResultSet)} was ever invoked. |
| */ |
| public boolean isResultSetInitialized() { |
| return resultsInitialized; |
| } |
| |
| /** |
| * @see ResultSet#next() |
| */ |
| public boolean next() throws SQLException { |
| return _next(resultSet); |
| } |
| |
| boolean _next(ResultSet results) throws SQLException { |
| boolean ret = results.next(); |
| position++; |
| return ret; |
| } |
| |
| /** |
| * Consumes <code>offset - position</code> elements from the {@link ResultSet}. |
| * |
| * @param offset The offset to advance to |
| * @return True if the resultSet was advanced to the current point, false if insufficient rows |
| * were present to advance to the requested offset. |
| */ |
| public boolean advanceResultSetToOffset(ResultSet results, long offset) throws SQLException { |
| if (offset < 0 || offset < position) { |
| throw new IllegalArgumentException("Offset should be " |
| + " non-negative and not less than the current position. " + offset + ", " + position); |
| } |
| if (position >= offset) { |
| return true; |
| } |
| |
| if (null == relativeSupported) { |
| Boolean moreResults = null; |
| synchronized (this) { |
| if (null == relativeSupported) { |
| try { |
| moreResults = advanceByRelative(results, offset); |
| relativeSupported = true; |
| } catch (SQLFeatureNotSupportedException e) { |
| relativeSupported = false; |
| } |
| } |
| } |
| |
| if (null != moreResults) { |
| // We figured out whether or not relative is supported. |
| // Make sure we actually do the necessary work. |
| if (!relativeSupported) { |
| // We avoided calling advanceByNext in the synchronized block earlier. |
| moreResults = advanceByNext(results, offset); |
| } |
| |
| return moreResults; |
| } |
| |
| // Another thread updated the RELATIVE_SUPPORTED before we did, fall through. |
| } |
| |
| if (relativeSupported) { |
| return advanceByRelative(results, offset); |
| } else { |
| return advanceByNext(results, offset); |
| } |
| } |
| |
| private boolean advanceByRelative(ResultSet results, long offset) throws SQLException { |
| long diff = offset - position; |
| while (diff > Integer.MAX_VALUE) { |
| if (!results.relative(Integer.MAX_VALUE)) { |
| // Avoid updating position until relative succeeds. |
| position += Integer.MAX_VALUE; |
| return false; |
| } |
| // Avoid updating position until relative succeeds. |
| position += Integer.MAX_VALUE; |
| diff -= Integer.MAX_VALUE; |
| } |
| boolean ret = results.relative((int) diff); |
| // Make sure we only update the position after successfully calling relative(int). |
| position += diff; |
| return ret; |
| } |
| |
| private boolean advanceByNext(ResultSet results, long offset) throws SQLException { |
| while (position < offset) { |
| // Advance while maintaining `position` |
| if (!_next(results)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| // End StatementInfo.java |