All Downloads are FREE. Search and download functionalities are using the official Maven repository.

world.data.jdbc.internal.statements.StatementImpl Maven / Gradle / Ivy

/*
 * dw-jdbc
 * Copyright 2017 data.world, Inc.

 * Licensed 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.
 *
 * This product includes software developed at data.world, Inc.(http://www.data.world/).
 */
package world.data.jdbc.internal.statements;

import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import world.data.jdbc.DataWorldConnection;
import world.data.jdbc.DataWorldStatement;
import world.data.jdbc.JdbcCompatibility;
import world.data.jdbc.internal.query.QueryEngine;
import world.data.jdbc.internal.util.ResourceContainer;
import world.data.jdbc.internal.util.ResourceManager;
import world.data.jdbc.model.Node;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import static java.util.Objects.requireNonNull;
import static world.data.jdbc.internal.util.Conditions.check;
import static world.data.jdbc.internal.util.Conditions.checkSupported;
import static world.data.jdbc.internal.util.Optionals.or;

@Log
public class StatementImpl implements DataWorldStatement, ReadOnlyStatement, ResourceContainer {

    private static final int NO_LIMIT = 0;

    private int timeout = NO_LIMIT;
    private JdbcCompatibility compatibilityLevel;
    private SQLWarning warnings = null;

    final QueryEngine queryEngine;
    private final DataWorldConnection connection;
    private final ResourceManager resources = new ResourceManager();

    private final List commands = new ArrayList<>();
    private final Queue batchResults = new LinkedList<>();
    private final List openResults = new ArrayList<>();
    private ResultSet currResults;
    private boolean closed;

    public StatementImpl(QueryEngine queryEngine, DataWorldConnection connection) {
        this.queryEngine = requireNonNull(queryEngine, "queryEngine");
        this.connection = requireNonNull(connection, "connection");
        ((ResourceContainer) connection).getResources().register(this);
    }

    @Override
    public ResourceManager getResources() {
        return resources;
    }

    /**
     * Gets the JDBC compatibility level that is in use, see
     * {@link JdbcCompatibility} for explanations
     * 

* By default this is set at the connection level and inherited, however you * may call {@link #setJdbcCompatibilityLevel(JdbcCompatibility)} to set the compatibility * level for this statement. This allows you to change the compatibility * level on a per-query basis if so desired. *

* * @return Compatibility level */ @Override public final JdbcCompatibility getJdbcCompatibilityLevel() throws SQLException { checkClosed(); return or(compatibilityLevel, connection.getJdbcCompatibilityLevel()); } /** * Sets the JDBC compatibility level that is in use, see * {@link JdbcCompatibility} for explanations. *

* By default this is set at the connection level and inherited, however you * may call the {@code setJdbcCompatibilityLevel} method to set the compatibility * level for this statement. This allows you to change the compatibility * level on a per-query basis if so desired. *

*

* Changing the level may not effect existing open objects, behaviour in * this case will be implementation specific. *

* * @param compatibilityLevel Compatibility level */ @Override public final void setJdbcCompatibilityLevel(JdbcCompatibility compatibilityLevel) throws SQLException { checkClosed(); this.compatibilityLevel = compatibilityLevel; } @Override public final DataWorldConnection getConnection() { return connection; } @Override public final void clearWarnings() { warnings = null; } @Override public final int getFetchDirection() { return ResultSet.FETCH_FORWARD; } @Override public final int getFetchSize() { return 0; } @Override public final int getMaxFieldSize() { return NO_LIMIT; } @Override public final int getMaxRows() { return NO_LIMIT; } /** * Gets that result sets are read-only */ @Override public final int getResultSetConcurrency() { return ResultSet.CONCUR_READ_ONLY; } @Override public final int getResultSetHoldability() { return ResultSet.CLOSE_CURSORS_AT_COMMIT; } @Override public final int getResultSetType() { return ResultSet.TYPE_FORWARD_ONLY; } @Override public final SQLWarning getWarnings() { return warnings; } /** * Helper method that derived classes may use to set warnings * * @param warning Warning */ private void setWarning(SQLWarning warning) { log.warning("SQL Warning was issued: " + warning); if (warnings == null) { warnings = warning; } else { // Chain with existing warnings warning.setNextWarning(warnings); warnings = warning; } } @Override public boolean isWrapperFor(Class iface) throws SQLException { return DataWorldStatement.class.equals(iface); } @Override public final T unwrap(Class iface) throws SQLException { check(isWrapperFor(iface), "Not a wrapper for the desired interface"); return iface.cast(this); } @Override public final void addBatch(String query) throws SQLException { checkClosed(); doAddBatch(query, Collections.emptyMap()); } void doAddBatch(String query, Map params) throws SQLException { commands.add(new BatchItem(query, params)); } @Override public final void cancel() throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public final boolean execute(String query, int autoGeneratedKeys) throws SQLException { checkClosed(); checkSupported(autoGeneratedKeys == NO_GENERATED_KEYS, "Does not support auto-generated keys"); return execute(query); } @Override public final boolean execute(String query, int[] columnIndexes) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public final boolean execute(String query, String[] columnNames) throws SQLException { throw new SQLFeatureNotSupportedException(); } @Override public final int[] executeBatch() throws SQLException { checkClosed(); // Go ahead and process the batch int[] rets = new int[commands.size()]; ResultSet curr = currResults; for (int i = 0; i < commands.size(); i++) { BatchItem batchItem = commands.get(i); if (doExecuteQuery(batchItem.query, batchItem.params)) { // True means it returned a ResultSet batchResults.add(getResultSet()); currResults = null; rets[i] = SUCCESS_NO_INFO; } else { // Need to add a null to getMoreResults() to produce correct // behavior across subsequent calls to getMoreResults() batchResults.add(null); rets[i] = -1; } } currResults = curr; // Make the next available results the current results if there // are no current results if (currResults == null && !batchResults.isEmpty()) { currResults = batchResults.poll(); } return rets; } @Override public final void clearBatch() { commands.clear(); } @Override public final void close() throws SQLException { if (closed) { return; } log.fine("Closing statement"); ((ResourceContainer) connection).getResources().remove(this); try { // Close results that are still open resources.close(); } catch (Exception e) { log.warning("Unexpected trying to close resources: " + e); } finally { closed = true; log.fine("Statement was closed"); } } private void setWarning(String warning) { setWarning(new SQLWarning(warning)); } @Override public final void setEscapeProcessing(boolean enable) { // Ignored, no-op } @Override public final void setFetchDirection(int direction) throws SQLException { checkSupported(direction == ResultSet.FETCH_FORWARD, "Only ResultSet.FETCH_FORWARD is supported as a fetch direction"); } @Override public final void setFetchSize(int rows) { setWarning("setMaxFieldSize() was called but there is no fetch size control for data.world JDBC connections"); } @Override public final void setMaxFieldSize(int max) { // Ignored setWarning("setMaxFieldSize() was called but there is no field size limit for data.world JDBC connections"); } @Override public final void setMaxRows(int max) { setWarning("setMaxRows() was called but there is no row size limit for data.world JDBC connections"); } @Override public final void setPoolable(boolean poolable) { setWarning("setPoolable() was called but data.world JDBC statements are always considered poolable"); } @Override public final void setQueryTimeout(int seconds) { timeout = Math.max(seconds, 0); } // Java 6/7 compatibility @Override @SuppressWarnings("javadoc") public final boolean isCloseOnCompletion() { // Statements do not automatically close return false; } @Override @SuppressWarnings("javadoc") public final void closeOnCompletion() throws SQLException { // We don't support the JDBC 4.1 feature of closing statements // automatically throw new SQLFeatureNotSupportedException(); } @Override public final boolean isPoolable() { return true; } @Override public final boolean execute(String query) throws SQLException { checkClosed(); return doExecuteQuery(query, Collections.emptyMap()); } @Override public final ResultSet executeQuery(String query) throws SQLException { checkClosed(); boolean hasResultSet = doExecuteQuery(query, Collections.emptyMap()); check(hasResultSet, "Query did not produce a result set"); return getResultSet(); } boolean doExecuteQuery(String query, Map parameters) throws SQLException { log.fine(() -> "Received input command text:\n " + query); try { currResults = queryEngine.execute(this, query, parameters, timeout != 0 ? timeout : null); return true; } catch (SQLException e) { throw e; } catch (Throwable e) { throw new SQLException("Error occurred during query evaluation", e); } } @Override public final boolean getMoreResults() throws SQLException { checkClosed(); if (currResults != null) { currResults.close(); currResults = null; } if (!batchResults.isEmpty()) { currResults = batchResults.poll(); return true; } else { return false; } } @Override public final boolean getMoreResults(int current) throws SQLException { checkClosed(); switch (current) { case CLOSE_CURRENT_RESULT: return getMoreResults(); case CLOSE_ALL_RESULTS: for (ResultSet rset : openResults) { rset.close(); } openResults.clear(); return getMoreResults(); case KEEP_CURRENT_RESULT: if (currResults != null) { openResults.add(currResults); currResults = null; } return 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 final int getQueryTimeout() { return timeout; } @Override public final ResultSet getResultSet() throws SQLException { checkClosed(); return currResults; } @Override public final boolean isClosed() { return closed; } void checkClosed() throws SQLException { check(!closed, "Statement is closed"); } @RequiredArgsConstructor private static class BatchItem { private final String query; private final Map params; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy