FetchSpec.java :  » Testing » PolePosition-0.20 » com » versant » core » jdbc » fetch » Java Open Source

Java Open Source » Testing » PolePosition 0.20 
PolePosition 0.20 » com » versant » core » jdbc » fetch » FetchSpec.java

/*
 * Copyright (c) 1998 - 2005 Versant Corporation
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Versant Corporation - initial API and implementation
 */
package com.versant.core.jdbc.fetch;

import com.versant.core.jdbc.sql.exp.SelectExp;
import com.versant.core.jdbc.sql.exp.SqlExp;
import com.versant.core.jdbc.sql.SqlDriver;
import com.versant.core.jdbc.JdbcStorageManager;
import com.versant.core.common.BindingSupportImpl;

import java.io.PrintStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * This specifies how each row from a SQL query is processed and helps to
 * generate the query. A FetchSpec contains a list of FetchOp's, each of
 * which fetch something from the ResultSet. FetchOp's may provide data
 * to other subsequent FetchOp's so complex operations can be broken down
 * into simple operations. A FetchOp may have a nested FetchSpec of its own
 * to be executed once for each row or in parallel with the 'parent'
 * FetchSpec.
 */
public class FetchSpec {

    private FetchOptions options = new FetchOptions();
    private FetchOp[] ops = new FetchOp[2];
    private int opCount;
    private FetchOp[] resultOps = new FetchOp[2];
    private int resultOpCount;
    private boolean singleObjectRow;

    private SqlDriver sqlDriver;
    private SelectExp root;
    private SqlExp pos;
    private int totColumnCount;
    private boolean inAddFetchOp;

    private SqlBuffer sqlBuffer;

    public FetchSpec(SelectExp root, SqlDriver sqlDriver) {
        this.root = root;
        this.sqlDriver = sqlDriver;
    }

    /**
     * Get the topmost SELECT for this spec.
     */
    public SelectExp getRoot() {
        return root;
    }

    /**
     * Add a new FetchOp to this plan. If includeInResult is true the the
     * result of the op is included in the projection returned by the
     * FetchSpec.
     */
    public void addFetchOp(FetchOp op, boolean includeInResult) {
        if (opCount == ops.length) {
            FetchOp[] a = new FetchOp[opCount * 2];
            System.arraycopy(ops, 0, a, 0, opCount);
            ops = a;
        }
        op.setIndex(opCount);
        ops[opCount++] = op;
        if (includeInResult) {
            if (resultOpCount == resultOps.length) {
                FetchOp[] a = new FetchOp[resultOpCount * 2];
                System.arraycopy(resultOps, 0, a, 0, resultOpCount);
                resultOps = a;
            }
            resultOps[resultOpCount++] = op;
        }
        // Process newly added ops in a loop protected by a flag so that
        // recursively added ops are processed in add order. This ensures
        // that the ResultSet columns will be read in ascending order.
        if (!inAddFetchOp) {
            try {
                inAddFetchOp = true;
                for (int i = opCount - 1; i < opCount; i++) {
                    SqlExp list = ops[i].init(root, totColumnCount + 1);
                    if (list != null) {
                        ++totColumnCount;
                        if (pos == null) {
                            pos = root.selectList = list;
                        } else {
                            pos.next = list;
                        }
                        for (; pos.next != null; pos = pos.next) {
                            ++totColumnCount;
                        }
                    }
                }
            } finally {
                inAddFetchOp = false;
            }
        }
    }

    /**
     * Get the number of FetchOp's in this spec.
     */
    public int getFetchOpCount() {
        return opCount;
    }

    /**
     * Get the types of the objects in our projection.
     */
    public int[] getProjectionTypes() {
        int[] a = new int[resultOpCount];
        for (int i = 0; i < resultOpCount; i++) {
            a[i] = resultOps[i].getResultType();
        }
        return a;
    }

    /**
     * Get the default FetchOptions for this spec.
     */
    public FetchOptions getOptions() {
        return options;
    }

    /**
     * Set the compiled parameter info.
     */
    public void setParamList(SqlBuffer.Param paramList) {
        if (sqlBuffer == null) {
            generateSQL();
        }
        sqlBuffer.setParamList(paramList);
    }

    /**
     * Print a user understandable description of this operation.
     */
    public void printPlan(PrintStream p, String indent) {
        for (int i = 0; i < opCount; i++) {
            ops[i].printPlan(p, indent);
        }
    }

    /**
     * Finish creating this spec and generate the SQL buffer for our query.
     * This is a NOP if already done.
     */
    public synchronized void generateSQL() {
        if (sqlBuffer != null) {
            return;
        }
        sqlBuffer = new SqlBuffer();
        int aliasCount = root.createAlias(0);
        if (aliasCount == 1) {
            root.alias = null;
            sqlBuffer.setFirstTableOrAlias(root.table.name);
        } else {
            sqlBuffer.setFirstTableOrAlias(root.alias);
        }
        root.appendSQL(sqlDriver, sqlBuffer.getSqlbuf(), null);
        sqlBuffer.setSelectListRange(root.distinct, root.selectListStartIndex,
                root.selectListFirstColEndIndex, root.selectListEndIndex);
        sqlBuffer.setOrderByRange(root.orderByStartIndex, root.orderByEndIndex);
        // work around bug with replace in CharBuffer class
        sqlBuffer.getSqlbuf().append(' ');

        for (int i = 0; i < opCount; i++) {
            ops[i].generateSQL();
        }

        // clear fields we dont need any more now that we have the SQL
        root = null;
        pos = null;
    }

    /**
     * Create a FetchResult to execute our query. This will execute the
     * query as soon as the data is needed.
     *
     * @param forUpdate Generate SELECT FOR UPDATE type query
     * @param forCount Generate a COUNT(*) query to just count the rows
     * @param fromIncl Index of first row to return
     * @param toExcl Index of row after last row to return (-1 for all)
     * @param scrollable Use a scrollable ResultSet
     *
     * @see FetchResultImp#execute()
     */
    public FetchResult createFetchResult(JdbcStorageManager sm, Connection con,
            Object[] params, boolean forUpdate, boolean forCount,
            long fromIncl, long toExcl, int fetchSize, boolean scrollable) {

        if (scrollable && !sqlDriver.isScrollableResultSetSupported()) {
            throw BindingSupportImpl.getInstance().datastore(
                    "Scrollable ResultSet's not supported for " +
                    sqlDriver.getName() + " using JDBC driver " +
                    sm.getJdbcConnectionSource().getDriverName());
        }

        if (sqlBuffer == null) {
            generateSQL();
        }
        String sql = sqlBuffer.getSql(sqlDriver, params, forUpdate, forCount,
                fromIncl, toExcl);
        boolean error = true;
        PreparedStatement ps = null;
        try {
            try {
                if (scrollable) {
                    ps = con.prepareStatement(sql,
                            ResultSet.TYPE_SCROLL_INSENSITIVE,
                            ResultSet.CONCUR_READ_ONLY);
                } else {
                    ps = con.prepareStatement(sql);
                }
            } catch (Exception e) {
                throw BindingSupportImpl.getInstance().datastore(
                        "Error creating PreparedStatement: " + e + "\nSQL:\n" +
                        sql);
            }
            sqlBuffer.setParamsOnPS(sm.getJmd(), sqlDriver, ps, params, sql);
            if (fetchSize > 0) {
                try {
                    ps.setFetchSize(fetchSize);
                } catch (Exception e) {
                    throw sqlDriver.mapException(e, e.toString(), true);
                }
            }
            FetchResultImp ans = new FetchResultImp(this, ps, sql, scrollable);
            error = false;
            return ans;
        } finally {
            if (error && ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    // ignore
                }
            }
        }
    }

    /**
     * This is invoked by one of our results when it is closed. We call
     * fetchResultClosed on all of our ops so they have a chance to close
     * any nested results.
     */
    public void fetchResultClosed(FetchResult fetchResult) {
        for (int i = 0; i < opCount; i++) {
            ops[i].fetchResultClosed(fetchResult);
        }
    }

    public SqlDriver getSqlDriver() {
        return sqlDriver;
    }

    public boolean isSingleObjectRow() {
        return singleObjectRow;
    }

    /**
     * If singleObjectRow is true and the projection only has one Object
     * then this is returned as is and not in an Object[1].
     */
    public void setSingleObjectRow(boolean singleObjectRow) {
        this.singleObjectRow = singleObjectRow;
    }

    /**
     * Process the current row in fetchResult's ResultSet and return our
     * projection.
     */
    public Object createRow(FetchResultImp fetchResult) {
        for (int i = 0; i < opCount; i++) {
            try {
                ops[i].fetch(fetchResult);
            } catch (Exception e) {
                throw sqlDriver.mapException(e, e.toString() + "\nProcessing " +
                        ops[i].getIndex() + ": " + ops[i].getDescription(),
                        true);
            }
        }
        if (singleObjectRow && resultOpCount == 1) {
            return resultOps[0].getResult(fetchResult);
        } else {
            Object[] a = new Object[resultOpCount];
            for (int i = 0; i < resultOpCount; i++) {
                a[i] = resultOps[i].getResult(fetchResult);
            }
            return a;
        }
    }

}

java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.