/*
 NeoDatis ODB : Native Object Database (odb.info@neodatis.org)
 Copyright (C) 2007 NeoDatis Inc. http://www.neodatis.org

 "This file is part of the NeoDatis ODB open source object database".

 NeoDatis ODB is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 NeoDatis ODB 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.neodatis.odb.core.query.execution;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.neodatis.btree.IBTree;
import org.neodatis.btree.IBTreeMultipleValuesPerKey;
import org.neodatis.btree.IBTreeSingleValuePerKey;
import org.neodatis.odb.ODBRuntimeException;
import org.neodatis.odb.OID;
import org.neodatis.odb.Objects;
import org.neodatis.odb.OdbConfiguration;
import org.neodatis.odb.core.NeoDatisError;
import org.neodatis.odb.core.layers.layer2.meta.AttributeValuesMap;
import org.neodatis.odb.core.layers.layer2.meta.ClassInfo;
import org.neodatis.odb.core.layers.layer2.meta.ClassInfoIndex;
import org.neodatis.odb.core.layers.layer2.meta.NonNativeObjectInfo;
import org.neodatis.odb.core.layers.layer3.IObjectReader;
import org.neodatis.odb.core.layers.layer3.IStorageEngine;
import org.neodatis.odb.core.query.IQuery;
import org.neodatis.odb.core.query.QueryManager;
import org.neodatis.odb.core.transaction.ISession;
import org.neodatis.odb.impl.core.btree.LazyODBBTreePersister;
import org.neodatis.odb.impl.tool.MemoryMonitor;
import org.neodatis.tool.DLogger;
import org.neodatis.tool.wrappers.OdbComparable;

/**
 * 
 * 

* Generic query executor. This class does all the job of iterating in the * object list and call particular query matching to check if the object must be * included in the query result. *

* *

* If the query has index, An execution plan is calculated to optimize the * execution. The query execution plan is calculated by subclasses (using * abstract method getExecutionPlan). * *

* */ public abstract class GenericQueryExecutor implements IMultiClassQueryExecutor { public static final String LOG_ID = "GenericQueryExecutor"; /** The storage engine */ protected IStorageEngine storageEngine; /** The query being executed */ protected IQuery query; /** The class of the object being fetched */ protected ClassInfo classInfo; /** The object used to read object data from database */ protected IObjectReader objectReader; /** The current database session */ protected ISession session; /** The next object position */ protected OID nextOID; /** A boolean to indicate if query must be ordered */ private boolean queryHasOrderBy; /** The key for ordering */ private OdbComparable orderByKey; protected OID currentOid; protected NonNativeObjectInfo currentNnoi; protected IQueryExecutorCallback callback; /** * Used for multi class executor to indicate not to execute start and end * method of query result action */ protected boolean executeStartAndEndOfQueryAction; public GenericQueryExecutor(IQuery query, IStorageEngine engine) { this.query = query; this.storageEngine = engine; this.objectReader = storageEngine.getObjectReader(); this.session = storageEngine.getSession(true); this.callback = OdbConfiguration.getQueryExecutorCallback(); this.executeStartAndEndOfQueryAction = true; } public abstract IQueryExecutionPlan getExecutionPlan(); public abstract void prepareQuery(); public abstract Comparable computeIndexKey(ClassInfo ci, ClassInfoIndex index); /** * This can be a NonNAtiveObjectInf or AttributeValuesMap * * @return */ public abstract Object getCurrentObjectMetaRepresentation(); /** * Check if the object with oid matches the query, returns true * * This method must compute the next object oid and the orderBy key if it * exists! * * @param oid * The object position * @param loadObjectInfo * To indicate if object must loaded (when the query indicator * 'in memory' is false, we do not need to load object, only ids) * @param inMemory To indicate if object must be actually loaded to memory */ public abstract boolean matchObjectWithOid(OID oid, boolean loadObjectInfo, boolean inMemory); /* * (non-Javadoc) * * @see * org.neodatis.odb.core.query.execution.IQueryExecutor#execute(boolean, * int, int, boolean, * org.neodatis.odb.core.query.execution.IMatchingObjectAction) */ public Objects execute(boolean inMemory, int startIndex, int endIndex, boolean returnObjects, IMatchingObjectAction queryResultAction) { if (storageEngine.isClosed()) { throw new ODBRuntimeException(NeoDatisError.ODB_IS_CLOSED.addParameter(storageEngine.getBaseIdentification().getIdentification())); } if (session.isRollbacked()) { throw new ODBRuntimeException(NeoDatisError.ODB_HAS_BEEN_ROLLBACKED); } // When used as MultiClass Executor, classInfo is already set if (classInfo == null) { // Class to execute query on String fullClassName = getFullClassName(query); // If the query class does not exist in meta model, return an empty // collection if (!session.getMetaModel().existClass(fullClassName)) { queryResultAction.start(); queryResultAction.end(); query.setExecutionPlan(new EmptyExecutionPlan()); return queryResultAction.getObjects(); } classInfo = session.getMetaModel().getClassInfo(fullClassName, true); } // Get the query execution plan IQueryExecutionPlan plan = getExecutionPlan(); plan.start(); try { if (plan.useIndex() && OdbConfiguration.useIndex()) { return executeUsingIndex(plan.getIndex(), inMemory, startIndex, endIndex, returnObjects, queryResultAction); } // When query must be applied to a single object if (query.isForSingleOid()) { return executeForOneOid(inMemory, returnObjects, queryResultAction); } return executeFullScan(inMemory, startIndex, endIndex, returnObjects, queryResultAction); } finally { plan.end(); } } public abstract String getFullClassName(IQuery query); /** * Query execution full scan * *
	 * 
	 * startIndex & endIndex
	 * A B C D E F G H I J K L
	 * 
	 * 
	 * [1,3] : nb >=1 && nb<3
	 * 
	 * 1) 
	 * analyze A
	 * nb = 0
	 * nb E [1,3] ? no
	 * r=[]
	 * 2) 
	 * analyze B
	 * nb = 1
	 * nb E [1,3] ? yes
	 * r=[B]
	 * 3) analyze C
	 * nb = 2
	 * nb E [1,3] ? yes
	 * r=[B,C]
	 * 4) analyze C
	 * nb = 3
	 * nb E [1,3] ? no and 3> upperBound([1,3]) => exit
	 * 
	 * 
* * @param inMemory * @param startIndex * @param endIndex * @param returnObjects * @return * @throws Exception */ private Objects executeFullScan(boolean inMemory, int startIndex, int endIndex, boolean returnObjects, IMatchingObjectAction queryResultAction) { boolean objectInRange = false; boolean objectMatches = false; if (storageEngine.isClosed()) { throw new ODBRuntimeException(NeoDatisError.ODB_IS_CLOSED.addParameter(storageEngine.getBaseIdentification().getIdentification())); } long nbObjects = classInfo.getNumberOfObjects(); if (OdbConfiguration.isDebugEnabled(LOG_ID)) { DLogger.debug("loading " + nbObjects + " instance(s) of " + classInfo.getFullClassName()); } if (executeStartAndEndOfQueryAction()) { queryResultAction.start(); } OID currentOID = null; OID prevOID = null; // TODO check if all instances are in the cache! and then load from the // cache nextOID = classInfo.getCommitedZoneInfo().first; if (nbObjects > 0 && nextOID == null) { // This means that some changes have not been commited! // Take next position from uncommited zone nextOID = classInfo.getUncommittedZoneInfo().first; } prepareQuery(); if (query != null) { queryHasOrderBy = query.hasOrderBy(); } boolean monitorMemory = OdbConfiguration.isMonitoringMemory(); // used when startIndex and endIndex are not negative int nbObjectsInResult = 0; for (int i = 0; i < nbObjects; i++) { //int i=0; //while(nextOID!=null){ if (monitorMemory && i % 10000 == 0) { MemoryMonitor.displayCurrentMemory("" + (i + 1), true); } // Reset the order by key orderByKey = null; objectMatches = false; prevOID = currentOID; currentOID = nextOID; // This is an error if (currentOID == null) { if (OdbConfiguration.throwExceptionWhenInconsistencyFound()) { throw new ODBRuntimeException(NeoDatisError.NULL_NEXT_OBJECT_OID.addParameter(classInfo.getFullClassName()).addParameter(i) .addParameter(nbObjects).addParameter(prevOID)); } break; } // If there is an endIndex condition if (endIndex != -1 && nbObjectsInResult >= endIndex) { break; } // If there is a startIndex condition if (startIndex != -1 && nbObjectsInResult < startIndex) { objectInRange = false; } else { objectInRange = true; } /* * // Object is not is the range, just peek next position if * (!objectInRange) { nextOID = * objectReader.getNextObjectOID(currentOID); continue; } */ // There is no query if (!inMemory && query == null ) { nbObjectsInResult++; // keep object position if we must if (objectInRange) { orderByKey = buildOrderByKey(currentNnoi); // TODO Where is the key for order by queryResultAction.objectMatch(nextOID, orderByKey); } nextOID = objectReader.getNextObjectOID(currentOID); } else { objectMatches = matchObjectWithOid(currentOID, returnObjects,inMemory); if (objectMatches) { nbObjectsInResult++; if (objectInRange) { if (queryHasOrderBy) { orderByKey = buildOrderByKey(getCurrentObjectMetaRepresentation()); } queryResultAction.objectMatch(currentOID, getCurrentObjectMetaRepresentation(), orderByKey); if (callback != null) { callback.readingObject(i, -1); } } } } //i++; } if (executeStartAndEndOfQueryAction()) { queryResultAction.end(); } return queryResultAction.getObjects(); } /** * Execute query using index * * @param index * @param inMemory * @param startIndex * @param endIndex * @param returnObjects * @return * @throws Exception */ private Objects executeUsingIndex(ClassInfoIndex index, boolean inMemory, int startIndex, int endIndex, boolean returnObjects, IMatchingObjectAction queryResultAction) { // Index that have not been used yet do not have persister! if (index.getBTree().getPersister() == null) { index.getBTree().setPersister(new LazyODBBTreePersister(storageEngine)); } boolean objectMatches = false; long nbObjects = classInfo.getNumberOfObjects(); long btreeSize = index.getBTree().getSize(); // the two values should be equal if(nbObjects!=btreeSize){ ClassInfo ci = storageEngine.getSession(true).getMetaModel().getClassInfoFromId(index.getClassInfoId()); throw new ODBRuntimeException(NeoDatisError.INDEX_IS_CORRUPTED.addParameter(index.getName()).addParameter(ci.getFullClassName()).addParameter(nbObjects).addParameter(btreeSize)); } if (OdbConfiguration.isDebugEnabled(LOG_ID)) { DLogger.debug("loading " + nbObjects + " instance(s) of " + classInfo.getFullClassName()); } if (executeStartAndEndOfQueryAction()) { queryResultAction.start(); } prepareQuery(); if (query != null) { queryHasOrderBy = query.hasOrderBy(); } IBTree tree = index.getBTree(); boolean isUnique = index.isUnique(); // Iterator iterator = new BTreeIterator(tree, // OrderByConstants.ORDER_BY_ASC); Comparable key = computeIndexKey(classInfo, index); List list = null; // If index is unique, get the object if (isUnique) { IBTreeSingleValuePerKey treeSingle = (IBTreeSingleValuePerKey) tree; Object o = treeSingle.search(key); if (o != null) { list = new ArrayList(); list.add(o); } } else { IBTreeMultipleValuesPerKey treeMultiple = (IBTreeMultipleValuesPerKey) tree; list = treeMultiple.search(key); } if (list != null) { Iterator iterator = list.iterator(); while (iterator.hasNext()) { OID oid = (OID) iterator.next(); // FIXME Why calling this method long position = objectReader.getObjectPositionFromItsOid(oid, true, true); orderByKey = null; objectMatches = matchObjectWithOid(oid, returnObjects,inMemory); if (objectMatches) { queryResultAction.objectMatch(oid, getCurrentObjectMetaRepresentation(), orderByKey); } } queryResultAction.end(); return queryResultAction.getObjects(); } if (executeStartAndEndOfQueryAction()) { queryResultAction.end(); } return queryResultAction.getObjects(); } /** * Execute query using index * * @param inMemory * @param returnObjects * @return * @throws Exception */ private Objects executeForOneOid(boolean inMemory, boolean returnObjects, IMatchingObjectAction queryResultAction) { if (OdbConfiguration.isDebugEnabled(LOG_ID)) { DLogger.debug("loading Object with oid " + query.getOidOfObjectToQuery() + " - class " + classInfo.getFullClassName()); } if (executeStartAndEndOfQueryAction()) { queryResultAction.start(); } prepareQuery(); OID oid = query.getOidOfObjectToQuery(); // FIXME Why calling this method long position = objectReader.getObjectPositionFromItsOid(oid, true, true); boolean objectMatches = matchObjectWithOid(oid, returnObjects,inMemory); queryResultAction.objectMatch(oid, getCurrentObjectMetaRepresentation(), orderByKey); queryResultAction.end(); return queryResultAction.getObjects(); } /** * TODO very bad. Should remove the instanceof * * @param object * @return */ public OdbComparable buildOrderByKey(Object object) { if (object instanceof AttributeValuesMap) { return buildOrderByKey((AttributeValuesMap) object); } return buildOrderByKey((NonNativeObjectInfo) object); } public OdbComparable buildOrderByKey(NonNativeObjectInfo nnoi) { // TODO cache the attributes ids to compute them only once return IndexTool.buildIndexKey("OrderBy", nnoi, QueryManager.getOrderByAttributeIds(classInfo, query)); } public OdbComparable buildOrderByKey(AttributeValuesMap values) { return IndexTool.buildIndexKey("OrderBy", values, query.getOrderByFieldNames()); } /* * (non-Javadoc) * * @seeorg.neodatis.odb.core.query.execution.IQueryExecutor# * executeStartAndEndOfQueryAction() */ public boolean executeStartAndEndOfQueryAction() { return executeStartAndEndOfQueryAction; } public void setExecuteStartAndEndOfQueryAction(boolean yes) { this.executeStartAndEndOfQueryAction = yes; } /* * (non-Javadoc) * * @see * org.neodatis.odb.core.query.execution.IQueryExecutor#getStorageEngine() */ public IStorageEngine getStorageEngine() { return storageEngine; } /* * (non-Javadoc) * * @see org.neodatis.odb.core.query.execution.IQueryExecutor#getQuery() */ public IQuery getQuery() { return query; } public void setClassInfo(ClassInfo classInfo) { this.classInfo = classInfo; } }

Ads help maintain this website.