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

org.datanucleus.store.db4o.query.SQLQuery Maven / Gradle / Ivy

/**********************************************************************
Copyright (c) 2008 Andy Jefferson and others. 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.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.store.db4o.query;

import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.sql4o.ObjectSetWrapper;
import org.datanucleus.sql4o.Sql4o;
import org.datanucleus.sql4o.Sql4oException;
import org.datanucleus.sql4o.parser.SqlParseException;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.db4o.DB4OStoreManager;
import org.datanucleus.store.query.AbstractSQLQuery;
import org.datanucleus.store.query.Query;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

import com.db4o.ObjectContainer;

/**
 * SQL query implementation for db4o datastores.
 * Builds a DataNucleus interface on top of the db4o-sql plugin written by Travis Reeder.
 * See http://code.google.com/p/db4o-sql/
 */
public class SQLQuery extends AbstractSQLQuery
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER_DB4O = Localiser.getInstance(
        "org.datanucleus.store.db4o.Localisation", DB4OStoreManager.class.getClassLoader());

    /** State variable for the compilation state */
    protected transient boolean isCompiled = false;

    /**
     * Constructor for a new query using the existing query.
     * @param ec execution context
     * @param query The existing query
     */
    public SQLQuery(ExecutionContext ec, SQLQuery query)
    {
        super(ec, query);
    }

    /**
     * Constructs a new query instance having the same criteria as the given query.
     * @param ec execution context
     */
    public SQLQuery(ExecutionContext ec)
    {
        super(ec, (String)null);
    }

    /**
     * Constructs a new query instance having the same criteria as the given query.
     * @param ec execution context
     * @param sqlText The SQL query string
     */
    public SQLQuery(ExecutionContext ec, String sqlText)
    {
        super(ec, sqlText);

        // Check if sql4o JAR is in CLASSPATH
        ClassUtils.assertClassForJarExistsInClasspath(ec.getClassLoaderResolver(), 
            "org.datanucleus.sql4o.Sql4o", "sql4o.jar");

        // JDO spec [14.7]. SQL queries must start with SELECT/select. Also what db4o-sql supports
        String firstToken = new StringTokenizer(sqlText, " ").nextToken();
        if (!firstToken.equals("SELECT") && !firstToken.startsWith("select"))
        {
            throw new NucleusUserException(LOCALISER.msg("059002", inputSQL));
        }
    }

    /**
     * Equality operator.
     * Returns true if the other object is of this type and if the input SQL string is the same.
     * @param obj The object to compare against
     * @return Whether they are equal
     */
    public boolean equals(Object obj)
    {
        if (obj == this)
        {
            return true;
        }

        if (!(obj instanceof SQLQuery) || !super.equals(obj))
        {
            return false;
        }

        return inputSQL.equals(((SQLQuery)obj).inputSQL);
    }

    /**
     * Utility to remove any previous compilation of this Query.
     **/
    protected void discardCompiled()
    {
        isCompiled = false;
        super.discardCompiled();
    }

    /**
     * Method to return if the query is compiled.
     * @return Whether it is compiled
     */
    protected boolean isCompiled()
    {
        return isCompiled;
    }

    /**
     * Verify the elements of the query and provide a hint to the query to prepare and optimize an execution plan.
     * TODO Drop this method for 2.2 onwards
     */
    public void compileInternal(boolean forExecute, Map parameterValues)
    {
        compileInternal(parameterValues);
    }

    /**
     * Verify the elements of the query and provide a hint to the query to prepare and optimize an execution plan.
     */
    public void compileInternal(Map parameterValues)
    {
        if (isCompiled)
        {
            return;
        }

        compiledSQL = generateQueryStatement();
        if (NucleusLogger.QUERY.isDebugEnabled())
        {
            NucleusLogger.QUERY.debug(LOCALISER.msg("059012", compiledSQL));
        }

        isCompiled = true;
    }

    /**
     * Execute the query and return the result.
     * For a SELECT query this will be the QueryResult. For an UPDATE/DELETE it will be the row count for
     * the update statement.
     * @param parameters the Map containing all of the parameters (positional parameters)
     * @return the result of the query
     */
    protected Object performExecute(Map parameters)
    {
        ManagedConnection mconn = ec.getStoreManager().getConnection(ec);
        ObjectContainer cont = (ObjectContainer) mconn.getConnection();
        try
        {
            List results = Sql4o.execute(cont, compiledSQL);
            return new SQLQueryResult(this, cont, (ObjectSetWrapper)results, resultMetaData);
        }
        catch (SqlParseException sqlpe)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("059025", compiledSQL), sqlpe);
        }
        catch (Sql4oException sql4e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("059025", compiledSQL), sql4e);
        }
        finally
        {
            mconn.release();
        }
    }

    /**
     * Method to perform any necessary pre-processing on the users query statement
     * before we execute it. SQL queries are not modified in any way, as per JDO2 spec [14.7].
     * @return The compiled SQL statement
     */
    protected String generateQueryStatement()
    {
        // We're returning the users SQL direct with no substitution of params etc
        String compiledSQL = getInputSQL();

        if (candidateClass != null && getType() == Query.SELECT)
        {
            // Perform any sanity checking of input for SELECT queries
            ClassLoaderResolver clr = ec.getClassLoaderResolver();
            AbstractClassMetaData cmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);

            if (getResultClass() == null)
            {
                // Check the presence of the required columns (id, version, discriminator) in the candidate class
                String selections = compiledSQL.trim().substring(7); // Skip "SELECT "
                int fromStart = selections.indexOf("FROM");
                if (fromStart == -1)
                {
                    fromStart = selections.indexOf("from");
                }
                selections = selections.substring(0, fromStart).trim();
                String[] selectedFields = StringUtils.split(selections, ",");

                if (selectedFields == null || selectedFields.length == 0)
                {
                    throw new NucleusUserException(LOCALISER.msg("059003", compiledSQL)); // TODO Check message location
                }
                if (selectedFields.length == 1 && selectedFields[0].trim().equals("*"))
                {
                    // SQL Query using * so just return since all possible fields will be selected
                    return compiledSQL;
                }

                // Generate id column field information for later checking the id is present
                if (cmd.getIdentityType() == IdentityType.DATASTORE)
                {
                    // Datastore identity - no field to check
                }
                else if (cmd.getIdentityType() == IdentityType.APPLICATION)
                {
                    // Application identity - need to have PK fields selected as minimum
                    String[] idFieldNames = cmd.getPrimaryKeyMemberNames();
                    boolean[] idFieldMissing = null;
                    idFieldMissing = new boolean[idFieldNames.length];
                    for (int i=0;i 0)
                        {
                            // Allow for user specification of "XX.YY AS ZZ"
                            fieldName = fieldName.substring(fieldName.indexOf(" AS ")+4).trim();
                        }
                        else if (fieldName.indexOf(" as ") > 0)
                        {
                            // Allow for user specification of "XX.YY as ZZ"
                            fieldName = fieldName.substring(fieldName.indexOf(" as ")+4).trim();
                        }

                        for (int j=0; j




© 2015 - 2024 Weber Informatics LLC | Privacy Policy