com.amazon.carbonado.repo.jdbc.OracleSupportStrategy.java Source code

Java tutorial

Introduction

Here is the source code for com.amazon.carbonado.repo.jdbc.OracleSupportStrategy.java

Source

/*
 * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
 * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 *
 * 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.
 */

package com.amazon.carbonado.repo.jdbc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.logging.LogFactory;

import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;

/**
 *
 *
 * @author Brian S O'Neill
 * @author bcastill
 */
class OracleSupportStrategy extends JDBCSupportStrategy {

    private static final String DEFAULT_SEQUENCE_SELECT_STATEMENT = "SELECT %s.NEXTVAL FROM DUAL";

    private static final String TRUNCATE_STATEMENT = "TRUNCATE TABLE %s";

    private static final int BLOB_CHUNK_LIMIT = 2000;
    private static final int CLOB_CHUNK_LIMIT = 1000;

    //private static final String PLAN_TABLE_NAME = "TEMP_CARBONADO_PLAN_TABLE";

    final Method mBLOB_empty_lob;
    final Method mBLOB_getBinaryStream;
    final Method mBLOB_length;
    final Method mBLOB_getBinaryOutputStream;
    final Method mBLOB_trim;

    final Method mCLOB_empty_lob;
    final Method mCLOB_getCharacterStream;
    final Method mCLOB_length;
    final Method mCLOB_getCharacterOutputStream;
    final Method mCLOB_trim;

    protected OracleSupportStrategy(JDBCRepository repo) {
        super(repo);

        // Set printf style format to create sequence query
        setSequenceSelectStatement(DEFAULT_SEQUENCE_SELECT_STATEMENT);

        setTruncateTableStatement(TRUNCATE_STATEMENT);

        // Access all the custom oracle.sql.BLOB methods via reflection.
        {
            Method blob_empty_lob = null;
            Method blob_getBinaryStream = null;
            Method blob_length = null;
            Method blob_getBinaryOutputStream = null;
            Method blob_trim = null;

            try {
                Class blobClass = Class.forName("oracle.sql.BLOB");

                blob_empty_lob = blobClass.getMethod("empty_lob");
                blob_getBinaryStream = blobClass.getMethod("getBinaryStream", long.class);
                blob_length = blobClass.getMethod("length");
                blob_getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", long.class);
                blob_trim = blobClass.getMethod("trim", long.class);
            } catch (ClassNotFoundException e) {
                LogFactory.getLog(getClass()).warn("Unable to find Oracle BLOB class", e);
            } catch (NoSuchMethodException e) {
                LogFactory.getLog(getClass()).warn("Unable to find Oracle BLOB method", e);
            }

            mBLOB_empty_lob = blob_empty_lob;
            mBLOB_getBinaryStream = blob_getBinaryStream;
            mBLOB_length = blob_length;
            mBLOB_getBinaryOutputStream = blob_getBinaryOutputStream;
            mBLOB_trim = blob_trim;
        }

        // Access all the custom oracle.sql.CLOB methods via reflection.
        {
            Method clob_empty_lob = null;
            Method clob_getCharacterStream = null;
            Method clob_length = null;
            Method clob_getCharacterOutputStream = null;
            Method clob_trim = null;

            try {
                Class clobClass = Class.forName("oracle.sql.CLOB");

                clob_empty_lob = clobClass.getMethod("empty_lob");
                clob_getCharacterStream = clobClass.getMethod("getCharacterStream", long.class);
                clob_length = clobClass.getMethod("length");
                clob_getCharacterOutputStream = clobClass.getMethod("getCharacterOutputStream", long.class);
                clob_trim = clobClass.getMethod("trim", long.class);
            } catch (ClassNotFoundException e) {
                LogFactory.getLog(getClass()).warn("Unable to find Oracle CLOB class", e);
            } catch (NoSuchMethodException e) {
                LogFactory.getLog(getClass()).warn("Unable to find Oracle CLOB method", e);
            }

            mCLOB_empty_lob = clob_empty_lob;
            mCLOB_getCharacterStream = clob_getCharacterStream;
            mCLOB_length = clob_length;
            mCLOB_getCharacterOutputStream = clob_getCharacterOutputStream;
            mCLOB_trim = clob_trim;
        }
    }

    @Override
    JDBCExceptionTransformer createExceptionTransformer() {
        return new OracleExceptionTransformer();
    }

    @Override
    JDBCBlob convertBlob(java.sql.Blob blob, JDBCBlobLoader loader) {
        return blob == null ? null : new OracleBlob(mRepo, blob, loader);
    }

    @Override
    JDBCClob convertClob(java.sql.Clob clob, JDBCClobLoader loader) {
        return clob == null ? null : new OracleClob(mRepo, clob, loader);
    }

    /**
     * @return original blob if too large and post-insert update is required, null otherwise
     * @throws PersistException instead of FetchException since this code is
     * called during an insert operation
     */
    @Override
    com.amazon.carbonado.lob.Blob setBlobValue(PreparedStatement ps, int column, com.amazon.carbonado.lob.Blob blob)
            throws PersistException {
        try {
            long length = blob.getLength();
            if (length > BLOB_CHUNK_LIMIT || ((long) ((int) length)) != length) {
                if (mBLOB_empty_lob == null) {
                    return super.setBlobValue(ps, column, blob);
                }

                try {
                    ps.setBlob(column, (java.sql.Blob) mBLOB_empty_lob.invoke(null));
                    return blob;
                } catch (InvocationTargetException e) {
                    throw mRepo.toPersistException(e.getCause());
                } catch (Exception e) {
                    throw mRepo.toPersistException(e);
                }
            }

            if (blob instanceof OracleBlob) {
                ps.setBlob(column, ((OracleBlob) blob).getInternalBlobForPersist());
                return null;
            }

            ps.setBinaryStream(column, blob.openInputStream(), (int) length);
            return null;
        } catch (SQLException e) {
            throw mRepo.toPersistException(e);
        } catch (FetchException e) {
            throw e.toPersistException();
        }
    }

    /**
     * @return original clob if too large and post-insert update is required, null otherwise
     */
    @Override
    com.amazon.carbonado.lob.Clob setClobValue(PreparedStatement ps, int column, com.amazon.carbonado.lob.Clob clob)
            throws PersistException {
        try {
            long length = clob.getLength();
            if (length > CLOB_CHUNK_LIMIT || ((long) ((int) length)) != length) {
                if (mCLOB_empty_lob == null) {
                    return super.setClobValue(ps, column, clob);
                }

                try {
                    ps.setClob(column, (java.sql.Clob) mCLOB_empty_lob.invoke(null));
                    return clob;
                } catch (InvocationTargetException e) {
                    throw mRepo.toPersistException(e.getCause());
                } catch (Exception e) {
                    throw mRepo.toPersistException(e);
                }
            }

            if (clob instanceof OracleClob) {
                ps.setClob(column, ((OracleClob) clob).getInternalClobForPersist());
                return null;
            }

            ps.setCharacterStream(column, clob.openReader(), (int) length);
            return null;
        } catch (SQLException e) {
            throw mRepo.toPersistException(e);
        } catch (FetchException e) {
            throw e.toPersistException();
        }
    }

    @Override
    SliceOption getSliceOption() {
        return SliceOption.FROM_AND_TO;
    }

    @Override
    String buildSelectWithSlice(String select, boolean from, boolean to) {
        if (to) {
            if (from) {
                // Use quoted identifier with space to prevent clash with
                // Storable property name.
                return "SELECT * FROM (SELECT \"A ROW\".*, ROWNUM \"A ROWNUM\" FROM (" + select
                        + ") \"A ROW\") WHERE \"A ROWNUM\" > ? AND \"A ROWNUM\" <= ?";
            } else {
                return "SELECT * FROM (" + select + ") WHERE ROWNUM <= ?";
            }
        } else if (from) {
            return "SELECT * FROM (SELECT \"A ROW\".*, ROWNUM \"A ROWNUM\" FROM (" + select
                    + ") \"A ROW\") WHERE \"A ROWNUM\" > ?";
        } else {
            return select;
        }
    }

    /* FIXME
    @Override
    boolean printPlan(Appendable app, int indentLevel, String statement)
    throws FetchException, IOException
    {
    Transaction txn = mRepo.enterTransaction();
    try {
        Connection con = mRepo.getConnection();
        try {
            try {
                return printPlan(app, indentLevel, statement, con);
            } catch (SQLException e) {
                throw mRepo.toFetchException(e);
            }
        } finally {
            mRepo.yieldConnection(con);
        }
    } finally {
        try {
            txn.exit();
        } catch (PersistException e) {
            // I don't care.
        }
    }
    }
        
    private boolean printPlan(Appendable app, int indentLevel, String statement, Connection con)
    throws SQLException, IOException
    {
    preparePlanTable(con);
        
    String explainPlanStatement =
        "EXPLAIN PLAN INTO " + PLAN_TABLE_NAME + " FOR " +
        statement;
        
    Statement st = con.createStatement();
    try {
        st.execute(explainPlanStatement);
    } finally {
        st.close();
    }
        
    st = con.createStatement();
    try {
        String planStatement =
            "SELECT LEVEL, OPERATION, OPTIONS, OBJECT_NAME, CARDINALITY, BYTES, COST " +
            "FROM " + PLAN_TABLE_NAME + " " +
            "START WITH ID=0 " +
            "CONNECT BY PRIOR ID = PARENT_ID " +
            "AND PRIOR NVL(STATEMENT_ID, ' ') = NVL(STATEMENT_ID, ' ') " +
            "AND PRIOR TIMESTAMP <= TIMESTAMP " +
            "ORDER BY ID, POSITION";
        
        ResultSet rs = st.executeQuery(planStatement);
        try {
            while (rs.next()) {
                BaseQuery.indent(app, indentLevel + (rs.getInt(1) - 1) * 2);
        
                app.append(rs.getString(2));
                String options = rs.getString(3);
                if (options != null && options.length() > 0) {
                    app.append(" (");
                    app.append(options);
                    app.append(')');
                }
        
                String name = rs.getString(4);
                if (name != null && name.length() > 0) {
                    app.append(' ');
                    app.append(name);
                }
        
                app.append(" {");
        
                String[] extraNames = {
                    "rows", "CARDINALITY",
                    "bytes", "BYTES",
                    "cost", "COST",
                };
        
                boolean comma = false;
                for (int i=0; i<extraNames.length; i+=2) {
                    String str = rs.getString(extraNames[i + 1]);
                    if (str != null && str.length() > 0) {
                        if (comma) {
                            app.append(", ");
                        }
                        app.append(extraNames[i]);
                        app.append('=');
                        app.append(str);
                        comma = true;
                    }
                }
        
                app.append('}');
                app.append('\n');
            }
        } finally {
            rs.close();
        }
    } finally {
        st.close();
    }
        
    return true;
    }
        
    private void preparePlanTable(Connection con) throws SQLException {
    Statement st = con.createStatement();
    try {
        // TODO: Is there a better way to check if a table exists?
        st.execute("SELECT COUNT(*) FROM " + PLAN_TABLE_NAME);
        return;
    } catch (SQLException e) {
        // Assume table doesn't exist, so create it.
    } finally {
        st.close();
    }
        
    String statement =
        "CREATE GLOBAL TEMPORARY TABLE " + PLAN_TABLE_NAME + " (" +
        "STATEMENT_ID VARCHAR2(30)," +
        "TIMESTAMP DATE," +
        "REMARKS VARCHAR2(80)," +
        "OPERATION VARCHAR2(30)," +
        "OPTIONS VARCHAR2(30)," +
        "OBJECT_NODE VARCHAR2(128)," +
        "OBJECT_OWNER VARCHAR2(30)," +
        "OBJECT_NAME VARCHAR2(30)," +
        "OBJECT_INSTANCE NUMBER(38)," +
        "OBJECT_TYPE VARCHAR2(30)," +
        "OPTIMIZER VARCHAR2(255)," +
        "SEARCH_COLUMNS NUMBER," +
        "ID NUMBER(38)," +
        "PARENT_ID NUMBER(38)," +
        "POSITION NUMBER(38)," +
        "COST NUMBER(38)," +
        "CARDINALITY NUMBER(38)," +
        "BYTES NUMBER(38)," +
        "OTHER_TAG VARCHAR2(255)," +
        "PARTITION_START VARCHAR2(255)," +
        "PARTITION_STOP VARCHAR2(255)," +
        "PARTITION_ID NUMBER(38),"+
        "OTHER LONG," +
        "DISTRIBUTION VARCHAR2(30)" +
        ")";
        
    st = con.createStatement();
    try {
        st.execute(statement);
    } finally {
        st.close();
    }
    }
    */
}