Package com.foundationdb.sql.embedded

Source Code of com.foundationdb.sql.embedded.JDBCConnection

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.sql.embedded;

import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.sql.LayerInfoInterface;
import com.foundationdb.sql.optimizer.rule.ExplainPlanContext;
import com.foundationdb.sql.server.ServerQueryContext;
import com.foundationdb.sql.server.ServerRoutineInvocation;
import com.foundationdb.sql.server.ServerServiceRequirements;
import com.foundationdb.sql.server.ServerSessionBase;
import com.foundationdb.sql.server.ServerSessionMonitor;
import com.foundationdb.sql.server.ServerTransaction;

import com.foundationdb.sql.StandardException;
import com.foundationdb.sql.parser.CallStatementNode;
import com.foundationdb.sql.parser.DDLStatementNode;
import com.foundationdb.sql.parser.DMLStatementNode;
import com.foundationdb.sql.parser.SQLParser;
import com.foundationdb.sql.parser.SQLParserException;
import com.foundationdb.sql.parser.StatementNode;

import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.server.api.DDLFunctions;
import com.foundationdb.server.error.EmbeddedResourceLeakException;
import com.foundationdb.server.error.ErrorCode;
import com.foundationdb.server.error.SQLParseException;
import com.foundationdb.server.error.SQLParserInternalException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.server.explain.Explainable;
import com.foundationdb.server.explain.Explainer;
import com.foundationdb.server.service.monitor.MonitorStage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.*;
import java.util.concurrent.Executor;

public class JDBCConnection extends ServerSessionBase implements Connection {
    static enum CommitMode { AUTO, MANUAL, INHERITED };
    private CommitMode commitMode;
    private boolean closed;
    private JDBCWarning warnings;
    private Properties clientInfo = new Properties();
    private String schema;
    private EmbeddedOperatorCompiler compiler;
    private List<JDBCResultSet> openResultSets = new ArrayList<>();

    private static final Logger logger = LoggerFactory.getLogger(JDBCConnection.class);
    protected static final String SERVER_TYPE = "JDBC";

    protected JDBCConnection(ServerServiceRequirements reqs, Properties info) {
        super(reqs);
        sessionMonitor = new ServerSessionMonitor(SERVER_TYPE, reqs.monitor().allocateSessionId());
        inheritFromCall();
        if ((defaultSchemaName != null) &&
            (info.getProperty("database") == null))
            info.put("database", defaultSchemaName);
        setProperties(info);
        if (session == null)
            session = reqs.sessionService().createSession();
        commitMode = (transaction != null) ? CommitMode.INHERITED : CommitMode.AUTO;
    }

    @Override
    public boolean endCall(ServerQueryContext context,
                           ServerRoutineInvocation invocation,
                           boolean topLevel, boolean success) {
        boolean close = topLevel;
        if ((transaction != null) && (commitMode == CommitMode.MANUAL)) {
            if (success) {
                context.warnClient(new EmbeddedResourceLeakException("Connection with setAutoCommit(false) was not closed"));
                success = false; // One warning is enough.
            }
            close = true;
        }
        if (close) {
            if (!openResultSets.isEmpty()) {
                if (success) {
                    List<String> stmts = new ArrayList<>(openResultSets.size());
                    for (JDBCResultSet resultSet : openResultSets) {
                        stmts.add(resultSet.statement.sql);
                    }
                    context.warnClient(new EmbeddedResourceLeakException("ResultSet was not closed: " + stmts));
                }
                try {
                    while (!openResultSets.isEmpty()) {
                        openResultSets.get(0).close();
                    }
                }
                catch (SQLException ex) {
                    openResultSets.clear();
                }
            }
            if ((transaction != null) && (commitMode != CommitMode.INHERITED)) {
                if (success)
                    commitTransaction();
                else
                    rollbackTransaction();
            }
            deregisterSessionMonitor();
            return false;
        }
        else if (transaction != null) {
            commitMode = CommitMode.INHERITED;
            return true;
        }
        else {
            deregisterSessionMonitor(); // Just in case.
            return false;
        }
    }

    @Override
    protected void sessionChanged() {
    }

    @Override
    public void notifyClient(QueryContext.NotificationLevel level, ErrorCode errorCode, String message) {
        if (shouldNotify(level)) {
            addWarning(new JDBCWarning(level, errorCode, message));
        }
    }

    protected void addWarning(JDBCWarning warning) {
        if (warnings == null)
            warnings = warning;
        else
            warnings.setNextWarning(warning);
    }

    protected ExecutableStatement compileExecutableStatement(String sql) {
        return compileExecutableStatement(sql, false, null);
    }

    protected ExecutableStatement compileExecutableStatement(String sql,
                                                             boolean getParameterNames) {
        return compileExecutableStatement(sql, getParameterNames, null);
    }

    protected ExecutableStatement compileExecutableStatement(String sql,
                                                             ExecuteAutoGeneratedKeys autoGeneratedKeys) {
        return compileExecutableStatement(sql, false, autoGeneratedKeys);
    }

    protected ExecutableStatement compileExecutableStatement(String sql,
                                                             boolean getParameterNames,
                                                             ExecuteAutoGeneratedKeys autoGeneratedKeys) {
        logger.debug("Compile: {}", sql);
        sessionMonitor.startStatement(sql);
        EmbeddedQueryContext context = new EmbeddedQueryContext(this);
        updateAIS(context);
        boolean localTransaction = false;
        sessionMonitor.enterStage(MonitorStage.PARSE);
        try {
            StatementNode sqlStmt;
            SQLParser parser = getParser();
            try {
                sqlStmt = parser.parseStatement(sql);
            }
            catch (SQLParserException ex) {
                throw new SQLParseException(ex);
            }
            catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
            sessionMonitor.enterStage(MonitorStage.OPTIMIZE);
            if (transaction == null) {
                transaction = new ServerTransaction(this, true, ServerTransaction.PeriodicallyCommit.OFF);
                localTransaction = true;
            }
            if ((sqlStmt instanceof DMLStatementNode) &&
                !(sqlStmt instanceof CallStatementNode))
                return compiler.compileExecutableStatement((DMLStatementNode)sqlStmt, parser.getParameterList(), getParameterNames, autoGeneratedKeys, context);
            if (autoGeneratedKeys != null)
                throw new UnsupportedOperationException();
            if (sqlStmt instanceof DDLStatementNode)
                return new ExecutableDDLStatement((DDLStatementNode)sqlStmt, sql);
            if (sqlStmt instanceof CallStatementNode)
                return ExecutableCallStatement.executableStatement((CallStatementNode)sqlStmt, parser.getParameterList(), context);
            throw new UnsupportedSQLException("Statement not recognized", sqlStmt);
        }
        finally {
            sessionMonitor.leaveStage();
            if (localTransaction)
                rollbackTransaction();
        }
    }

    public Explainer explain(String sql) {
        logger.debug("Explain: {}", sql);
        sessionMonitor.startStatement(sql);
        updateAIS(new EmbeddedQueryContext(this));
        boolean localTransaction = false;
        sessionMonitor.enterStage(MonitorStage.PARSE);
        try {
            StatementNode sqlStmt;
            SQLParser parser = getParser();
            try {
                sqlStmt = parser.parseStatement(sql);
            }
            catch (SQLParserException ex) {
                throw new SQLParseException(ex);
            }
            catch (StandardException ex) {
                throw new SQLParserInternalException(ex);
            }
            sessionMonitor.enterStage(MonitorStage.OPTIMIZE);
            if (transaction == null) {
                transaction = new ServerTransaction(this, true, ServerTransaction.PeriodicallyCommit.OFF);
                localTransaction = true;
            }
            ExplainPlanContext context = new ExplainPlanContext(compiler, new EmbeddedQueryContext(this));
            Explainable explainable;
            if ((sqlStmt instanceof DMLStatementNode) &&
                !(sqlStmt instanceof CallStatementNode))
                explainable = compiler.compile((DMLStatementNode)sqlStmt, parser.getParameterList(), context).getPlannable();
            else
                throw new UnsupportedSQLException("Statement not supported for EXPLAIN", sqlStmt);
            return explainable.getExplainer(context.getExplainContext());
        }
        finally {
            sessionMonitor.leaveStage();
            if (localTransaction)
                rollbackTransaction();
        }
    }

    protected void updateAIS(EmbeddedQueryContext context) {
        DDLFunctions ddl = reqs.dxl().ddlFunctions();
        AkibanInformationSchema newAIS = ddl.getAIS(session);
        if ((ais != null) && (ais.getGeneration() == newAIS.getGeneration()))
            return;             // Unchanged.
        ais = newAIS;
        rebuildCompiler();
    }

    protected void rebuildCompiler() {
        initParser();
        compiler = EmbeddedOperatorCompiler.create(this, reqs.store());
        initAdapters(compiler);
    }

    // Slightly different contract than ServerSessionBase, since a transaction
    // remains open when a until its read result set is closed.
    protected void beforeExecuteStatement(ExecutableStatement stmt) {
        sessionMonitor.enterStage(MonitorStage.EXECUTE);
        // If there is already a transaction and not auto-commit, transaction mode needs to allow writes if appropriate
        if (transaction != null && commitMode == CommitMode.MANUAL)
            transaction.setReadOnly(transactionDefaultReadOnly);
        boolean localTransaction = super.beforeExecute(stmt);
        if (localTransaction) {
            logger.debug("Auto BEGIN TRANSACTION");
            registerSessionMonitor();
        }
    }

    protected void afterExecuteStatement(ExecutableStatement stmt, boolean success) {
        sessionMonitor.leaveStage();
        boolean localTransaction = false;
        if (checkAutoCommit()) {
            // An update statement without any open cursors, or a
            // query statement that fails before the cursor gets fully
            // set up. Treat as local transaction and commit / abort
            // now.
            localTransaction = true;
            deregisterSessionMonitor();
            logger.debug(success ? "Auto COMMIT TRANSACTION" : "Auto ROLLBACK TRANSACTION");
        }
        super.afterExecute(stmt, localTransaction, success, true);
    }

    protected void openingResultSet(JDBCResultSet resultSet) {
        assert isTransactionActive();
        openResultSets.add(resultSet);
    }

    protected void closingResultSet(JDBCResultSet resultSet) {
        openResultSets.remove(resultSet);
        if (checkAutoCommit()) {
            commitTransaction();
            deregisterSessionMonitor();
            logger.debug("Auto COMMIT TRANSACTION");
        }
    }

    protected boolean checkAutoCommit() {
        return ((commitMode == CommitMode.AUTO) &&
                (transaction != null) &&
                openResultSets.isEmpty());
    }

    // Register as a result of beginning a transaction (which is implicit).
    protected void registerSessionMonitor() {
        reqs.monitor().registerSessionMonitor(sessionMonitor, session);
    }

    // Deregister when transaction is committed, rolled back, or connection closed.
    protected void deregisterSessionMonitor() {
        reqs.monitor().deregisterSessionMonitor(sessionMonitor, session);
    }

    protected LayerInfoInterface getLayerInfo() {
        return reqs.layerInfo();
    }

    public JDBCCallableStatement prepareCall(TableName routineName) throws SQLException {
        EmbeddedQueryContext context = new EmbeddedQueryContext(this);
        updateAIS(context);
        return new JDBCCallableStatement(this, routineName.toString(), ExecutableCallStatement.executableStatement(routineName, context));
    }

    /* Wrapper */

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        throw new SQLException("Not supported");
    }
   
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    /* Connection */

    @Override
    public Statement createStatement() throws SQLException {
        return new JDBCStatement(this);
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return new JDBCPreparedStatement(this, sql, compileExecutableStatement(sql));
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return new JDBCCallableStatement(this, sql, compileExecutableStatement(sql, true));
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return sql;
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        switch (commitMode) {
        case INHERITED:
            throw new JDBCException("Cannot set auto commit with outer transaction");
        case AUTO:
            if (!autoCommit)
                commitMode = CommitMode.MANUAL;
            break;
        case MANUAL:
            if (autoCommit)
                commitMode = CommitMode.AUTO;
            checkAutoCommit()// Commit now if no open cursors.
            break;
        }
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return (commitMode == CommitMode.AUTO);
    }

    @Override
    public void commit() throws SQLException {
        switch (commitMode) {
        case AUTO:
            throw new JDBCException("Commit not allowed in auto-commit mode");
        case INHERITED:
            throw new JDBCException("Commit not allowed with outer transaction");
        }
        try {
            commitTransaction();
        }
        catch (RuntimeException ex) {
            throw JDBCException.throwUnwrapped(ex);
        }
        if (openResultSets.isEmpty())
            deregisterSessionMonitor();
    }

    @Override
    public void rollback() throws SQLException {
        switch (commitMode) {
        case AUTO:
            throw new JDBCException("Rollback not allowed in auto-commit mode");
        case INHERITED:
            throw new JDBCException("Rollback not allowed with outer transaction");
        }
        try {
            rollbackTransaction();
        }
        catch (RuntimeException ex) {
            throw JDBCException.throwUnwrapped(ex);
        }
        if (openResultSets.isEmpty())
            deregisterSessionMonitor();
    }

    @Override
    public void close() throws SQLException {
        if (isTransactionActive() && (commitMode != CommitMode.INHERITED))
            rollbackTransaction();
        while (!openResultSets.isEmpty()) {
            openResultSets.get(0).close();
        }
        deregisterSessionMonitor();
        this.closed = true;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return closed;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return new JDBCDatabaseMetaData(this);
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        this.transactionDefaultReadOnly = readOnly;
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return transactionDefaultReadOnly;
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
    }

    @Override
    public String getCatalog() throws SQLException {
        return null;
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        if (level != TRANSACTION_SERIALIZABLE)
            throw new SQLException("Only TRANSACTION_SERIALIZABLE supported");
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return TRANSACTION_SERIALIZABLE;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return warnings;
    }

    @Override
    public void clearWarnings() throws SQLException {
        warnings = null;
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency)
            throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY))
            throw new SQLFeatureNotSupportedException();
        return createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType,
                                              int resultSetConcurrency)
            throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY))
            throw new SQLFeatureNotSupportedException();
        return prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType,
                                         int resultSetConcurrency) throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY))
            throw new SQLFeatureNotSupportedException();
        return prepareCall(sql);
    }

    @Override
    public Map<String,Class<?>> getTypeMap() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setTypeMap(java.util.Map<String,Class<?>> map) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        if (holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT)
            throw new SQLFeatureNotSupportedException();
    }

    @Override
    public int getHoldability() throws SQLException {
        return ResultSet.CLOSE_CURSORS_AT_COMMIT;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency,
                                     int resultSetHoldability) throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) ||
            (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT))
            throw new SQLFeatureNotSupportedException();
        return createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType,
                                              int resultSetConcurrency, int resultSetHoldability)
            throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) ||
            (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT))
            throw new SQLFeatureNotSupportedException();
        return prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType,
                                         int resultSetConcurrency,
                                         int resultSetHoldability) throws SQLException {
        if ((resultSetType != ResultSet.TYPE_FORWARD_ONLY) ||
            (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) ||
            (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT))
            throw new SQLFeatureNotSupportedException();
        return prepareCall(sql);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
            throws SQLException {
        return new JDBCPreparedStatement(this, sql,
                                         compileExecutableStatement(sql,
                                                                    ExecuteAutoGeneratedKeys.of(autoGeneratedKeys)));
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
            throws SQLException {
        return new JDBCPreparedStatement(this, sql,
                                         compileExecutableStatement(sql,
                                                                    ExecuteAutoGeneratedKeys.of(columnIndexes)));
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames)
            throws SQLException {
        return new JDBCPreparedStatement(this, sql,
                                         compileExecutableStatement(sql,
                                                                    ExecuteAutoGeneratedKeys.of(columnNames)));
    }

    @Override
    public Clob createClob() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public Blob createBlob() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public NClob createNClob() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return !closed;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        clientInfo.put(name, value);
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        clientInfo.putAll(properties);
    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return clientInfo.getProperty(name);
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return clientInfo;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        this.schema = schema;
    }

    @Override
    public String getSchema() throws SQLException {
        return schema;
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }
}
TOP

Related Classes of com.foundationdb.sql.embedded.JDBCConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.