/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library 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.
 * 
 * This library 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.
 * Modified by
 * @author  Data Virtuality GmbH
 * @date    $Date$
 *  
 */
/*
 * EXTENSION
 * ISSUE: DVCORE-2521
 * FIXED BY: Roman Pedchenko
 * DATE: 2014-12-05
 * ADOPTED FROM: TEIID-2153, 2171, 2359, 2149, 2358, 2410, 2271, 2467, 2429, 2363, 2160, 2461, 2797, 2326, 1557
 * END EXTENSION
 */
/*
 * EXTENSION
 * ISSUE: DVCORE-2441
 * FIXED BY: Roman Pedchenko
 * DATE: 2014.10.23
 * ADOPTED FROM: https://issues.jboss.org/browse/TEIID-247, 2166, 2151, 2234, 2581, 2571, 2369, 1386, 2226, 2233, 2224, 2225, 2257
 * END EXTENSION
 */
// https://issues.jboss.org/browse/TEIID-3756
// https://issues.jboss.org/browse/TEIID-4017
// https://issues.jboss.org/browse/TEIID-2369
// https://issues.jboss.org/browse/TEIID-2661
// https://issues.jboss.org/browse/TEIID-2205
// https://issues.jboss.org/browse/TEIID-2086
// https://issues.jboss.org/browse/TEIID-4076
// https://issues.jboss.org/browse/TEIID-3390
// https://issues.jboss.org/browse/TEIID-2111
// https://issues.jboss.org/browse/TEIID-2507
package org.teiid.dqp.internal.process;

import com.datavirtuality.dv.core.interfaces.DVCaller;
import com.datavirtuality.dv.core.interfaces.MyOptimizerRule;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.api.exception.query.QueryParserException;
import org.teiid.api.exception.query.QueryResolverException;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.client.RequestMessage;
import org.teiid.client.RequestMessage.ResultsMode;
import org.teiid.client.RequestMessage.ShowPlan;
import org.teiid.client.xa.XATransactionException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.id.IDGenerator;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.types.TransformationException;
import org.teiid.core.util.Assertion;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository;
import org.teiid.dqp.internal.process.AuthorizationValidator.CommandType;
import org.teiid.dqp.internal.process.multisource.MultiSourceCapabilitiesFinder;
import org.teiid.dqp.internal.process.multisource.MultiSourceMetadataWrapper;
import org.teiid.dqp.internal.process.multisource.MultiSourcePlanToProcessConverter;
import org.teiid.dqp.message.RequestID;
import org.teiid.dqp.service.TransactionContext;
import org.teiid.dqp.service.TransactionContext.Scope;
import org.teiid.dqp.service.TransactionService;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.metadata.FunctionMethod.Determinism;
import org.teiid.metadata.MetadataFactory;
import org.teiid.metadata.Schema;
import org.teiid.metadata.Table;
import org.teiid.query.QueryPlugin;
import org.teiid.query.analysis.AnalysisRecord;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.TempCapabilitiesFinder;
import org.teiid.query.metadata.TempMetadataAdapter;
import org.teiid.query.optimizer.QueryOptimizer;
import org.teiid.query.optimizer.capabilities.CapabilitiesFinder;
import org.teiid.query.optimizer.relational.rules.RuleConstants;
import org.teiid.query.parser.ParseInfo;
import org.teiid.query.parser.QueryParser;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.ProcessorPlan;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.processor.proc.ProcedurePlan;
import org.teiid.query.processor.xml.XMLPlan;
import org.teiid.query.resolver.QueryResolver;
import org.teiid.query.rewriter.QueryRewriter;
import org.teiid.query.sql.lang.*;
import org.teiid.query.sql.proc.CreateProcedureCommand;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.symbol.Reference;
import org.teiid.query.sql.visitor.GroupCollectorVisitor;
import org.teiid.query.sql.visitor.ReferenceCollectorVisitor;
import org.teiid.query.tempdata.GlobalTableStore;
import org.teiid.query.tempdata.TempTableStore;
import org.teiid.query.util.CommandContext;
import org.teiid.query.util.Options;
import org.teiid.query.validator.*;

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


/**
 * Server side representation of the RequestMessage.  Knows how to process itself.
 */
public class Request {

	private static final String CLEAN_LOBS_ONCLOSE = "clean_lobs_onclose"; //$NON-NLS-1$
	// init state
    protected RequestMessage requestMsg;
    private String vdbName;
    private int vdbVersion;
    private BufferManager bufferManager;
    private ProcessorDataManager processorDataManager;
    private TransactionService transactionService;
    private TempTableStore tempTableStore;
    protected IDGenerator idGenerator = new IDGenerator();
    DQPWorkContext workContext;
    RequestID requestId;

    // acquired state
    protected CapabilitiesFinder capabilitiesFinder;
    protected QueryMetadataInterface metadata;
    private Set<String> multiSourceModels;

    // internal results
    protected boolean addedLimit;
    protected ProcessorPlan processPlan;
    // external results
    protected AnalysisRecord analysisRecord;
    protected CommandContext context;
    protected QueryProcessor processor;
    
    protected TransactionContext transactionContext;
    protected ConnectorManagerRepository connectorManagerRepo; 
    
    protected Command userCommand;
    protected boolean returnsUpdateCount;
	private GlobalTableStore globalTables;
	private SessionAwareCache<PreparedPlan> planCache;
	private boolean resultSetCacheEnabled = true;
	private int userRequestConcurrency;
	private AuthorizationValidator authorizationValidator;
	private Executor executor;
	private Options options;

    void initialize(RequestMessage requestMsg,
                              BufferManager bufferManager,
                              ProcessorDataManager processorDataManager,
                              TransactionService transactionService,
                              TempTableStore tempTableStore,
                              DQPWorkContext workContext,
                              SessionAwareCache<PreparedPlan> planCache) {

        this.requestMsg = requestMsg;
        this.vdbName = workContext.getVdbName();        
        this.vdbVersion = workContext.getVdbVersion();
        this.bufferManager = bufferManager;
        this.processorDataManager = processorDataManager;
        this.transactionService = transactionService;
        this.tempTableStore = tempTableStore;
        this.workContext = workContext;
        this.requestId = workContext.getRequestID(this.requestMsg.getExecutionId());
        this.connectorManagerRepo = workContext.getVDB().getAttachment(ConnectorManagerRepository.class);
        this.planCache = planCache;
    }

	public void setOptions(Options options) {
		this.options = options;
	}
    
	void setMetadata(CapabilitiesFinder capabilitiesFinder, QueryMetadataInterface metadata, Set multiSourceModels) {
		this.capabilitiesFinder = capabilitiesFinder;
		this.metadata = metadata;
		this.multiSourceModels = multiSourceModels;
	}
	
	public void setResultSetCacheEnabled(boolean resultSetCacheEnabled) {
		this.resultSetCacheEnabled = resultSetCacheEnabled;
	}
	
	public void setAuthorizationValidator(
			AuthorizationValidator authorizationValidator) {
		this.authorizationValidator = authorizationValidator;
	}

	public CommandContext getContext() {
		return context;
	}

	public Command getUserCommand() {
		return userCommand;
	}

	/**
	 * if the metadata has not been supplied via setMetadata, this method will create the appropriate state
	 * 
	 * @throws TeiidComponentException
	 */
    protected void initMetadata() throws TeiidComponentException {
        if (this.metadata != null) {
        	return;
        }
    	// Prepare dependencies for running the optimizer        
        this.capabilitiesFinder = new CachedFinder(this.connectorManagerRepo, workContext.getVDB());        
        this.capabilitiesFinder = new TempCapabilitiesFinder(this.capabilitiesFinder);

        VDBMetaData vdbMetadata = workContext.getVDB();
        metadata = vdbMetadata.getAttachment(QueryMetadataInterface.class);
        globalTables = vdbMetadata.getAttachment(GlobalTableStore.class);

        if (metadata == null) {
             throw new TeiidComponentException(QueryPlugin.Event.TEIID30489, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30489, this.vdbName, this.vdbVersion));
        }
        
        // Check for multi-source models and further wrap the metadata interface
        Set<String> multiSourceModelList = workContext.getVDB().getMultiSourceModelNames();
        if(multiSourceModelList != null && multiSourceModelList.size() > 0) {
        	this.multiSourceModels = multiSourceModelList;
            this.metadata = new MultiSourceMetadataWrapper(this.metadata, this.multiSourceModels);
        }
        
        TempMetadataAdapter tma = new TempMetadataAdapter(metadata, this.tempTableStore.getMetadataStore());
        tma.setSession(true);
        this.metadata = tma;
    }
    
    protected void createCommandContext(Command command) throws QueryValidatorException {
	    if (this.context != null) {
		    return;
	    }
    	// Create command context, used in rewriting, planning, and processing
        // Identifies a "group" of requests on a per-connection basis to allow later
        // cleanup of all resources in the group on connection shutdown
        String groupName = workContext.getSessionId();

        this.context =
            new CommandContext(
                groupName,
                workContext.getUserName(),
                requestMsg.getExecutionPayload(),
                workContext.getVdbName(), 
                workContext.getVdbVersion(), 
                this.requestMsg.getShowPlan() != ShowPlan.OFF);
        this.context.setProcessorBatchSize(bufferManager.getProcessorBatchSize());
        this.context.setGlobalTableStore(this.globalTables);
	    boolean autoCleanLobs = true;
	    if (this.workContext.getSession().isEmbedded()) {
		    Object value = this.workContext.getSession().getSessionVariables().get(CLEAN_LOBS_ONCLOSE); // = null
		    if (value != null) {
			    value = DataTypeManager.convertToRuntimeType(value, false);
			    try {
				    value = DataTypeManager.transformValue(value, value.getClass(), DataTypeManager.DefaultDataClasses.BOOLEAN);
				    if (!(Boolean)value) {
					    autoCleanLobs = false;
				    }
			    } catch (TransformationException e) {
				    LogManager.logDetail(LogConstants.CTX_DQP, e, "Improper value for", CLEAN_LOBS_ONCLOSE); //$NON-NLS-1$
			    }
		    }
	    }
	    if (!autoCleanLobs) {
		    context.disableAutoCleanLobs();
	    }
        if (multiSourceModels != null) {
            MultiSourcePlanToProcessConverter modifier = new MultiSourcePlanToProcessConverter(
					metadata, idGenerator, analysisRecord, capabilitiesFinder,
					multiSourceModels, workContext, context);
            context.setPlanToProcessConverter(modifier);
        }
        context.setExecutor(this.executor);
        context.setAuthoriziationValidator(authorizationValidator);
        context.setTempTableStore(tempTableStore);
        context.setQueryProcessorFactory(new QueryProcessorFactoryImpl(this.bufferManager, this.processorDataManager, this.capabilitiesFinder, idGenerator, metadata));
        context.setMetadata(this.metadata);
        context.setBufferManager(this.bufferManager);
        context.setPreparedPlanCache(planCache);
        context.setResultSetCacheEnabled(this.resultSetCacheEnabled);
        context.setUserRequestSourceConcurrency(this.userRequestConcurrency);
        if (command instanceof Insert) {
        	context.setReturnAutoGeneratedKeys(this.requestMsg.isReturnAutoGeneratedKeys());
        }
        this.context.setOptions(options);
        this.context.setSession(workContext.getSession());
        this.context.setRequestId(this.requestId);
        this.context.setDQPWorkContext(this.workContext);
        this.context.setTransactionService(this.transactionService);
        this.context.setVDBClassLoader(workContext.getVDB().getAttachment(ClassLoader.class));
	    // EXTENSION
	    // ISSUE: DVCORE-316, DVCORE-913
	    // FIXED BY: Roman Pedchenko, Dmitry Pogorelov
	    // DATE: 2013.05.12, 2014.09.29
	    this.context.setDvOptions(getDvOptions());
        // END EXTENSION
    }
    
    public void setUserRequestConcurrency(int userRequestConcurrency) {
		this.userRequestConcurrency = userRequestConcurrency;
	}

    protected void checkReferences(List<Reference> references) throws QueryValidatorException {
    	referenceCheck(references);
    }
    
    static void referenceCheck(List<Reference> references) throws QueryValidatorException {
    	if (references != null && !references.isEmpty()) {
    		 throw new QueryValidatorException(QueryPlugin.Event.TEIID30491, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30491));
    	}
    }

    protected void resolveCommand(Command command) throws QueryResolverException, TeiidComponentException {
    	//ensure that the user command is distinct from the processing command
        //rewrite and planning may alter options, symbols, etc.
    	QueryResolver.resolveCommand(command, metadata, this);
    }
        
    private void validateQuery(Command command)
        throws QueryValidatorException, TeiidComponentException {
                
        // Create generic sql validation visitor
        AbstractValidationVisitor visitor = new ValidationVisitor();
        validateWithVisitor(visitor, metadata, command);
    }
    
    public Command parseCommand() throws QueryParserException {
    	if (requestMsg.getCommand() != null) {
    		return (Command)requestMsg.getCommand();
    	}
        String[] commands = requestMsg.getCommands();
        ParseInfo parseInfo = createParseInfo(this.requestMsg);
        if (requestMsg.isPreparedStatement() || requestMsg.isCallableStatement() || !requestMsg.isBatchedUpdate()) {
        	String commandStr = commands[0];
            return QueryParser.getQueryParser().parseCommand(commandStr, parseInfo);
        } 
        List<Command> parsedCommands = new ArrayList<Command>(commands.length);
        for (int i = 0; i < commands.length; i++) {
        	String updateCommand = commands[i];
            parsedCommands.add(QueryParser.getQueryParser().parseCommand(updateCommand, parseInfo));
        }
        return new BatchedUpdateCommand(parsedCommands);
    }

	public static ParseInfo createParseInfo(RequestMessage requestMsg) {
		ParseInfo parseInfo = new ParseInfo();
    	parseInfo.ansiQuotedIdentifiers = requestMsg.isAnsiQuotedIdentifiers();
		return parseInfo;
	}

    public static void validateWithVisitor(
        AbstractValidationVisitor visitor,
        QueryMetadataInterface metadata,
        Command command)
        throws QueryValidatorException, TeiidComponentException {

        // Validate with visitor
        ValidatorReport report = Validator.validate(command, metadata, visitor);
        if (report.hasItems()) {
            ValidatorFailure firstFailure = report.getItems().iterator().next();
             throw new QueryValidatorException(QueryPlugin.Event.TEIID30492, firstFailure.getMessage());
        }
    }

    private void createProcessor() throws TeiidComponentException {
	    if (this.userCommand instanceof CreateProcedureCommand && this.processPlan instanceof ProcedurePlan) {
		    ((ProcedurePlan)this.processPlan).setValidateAccess(true);
		}
    	this.context.setTransactionContext(getTransactionContext(true));
        this.processor = new QueryProcessor(processPlan, context, bufferManager, processorDataManager);
    }

    TransactionContext getTransactionContext(boolean startAutoWrap) throws TeiidComponentException {
	    if (this.transactionContext != null) {
		    return this.transactionContext;
		}
        TransactionContext tc = transactionService.getOrCreateTransactionContext(workContext.getSessionId());
        
        Assertion.assertTrue(tc.getTransactionType() != TransactionContext.Scope.REQUEST, "Transaction already associated with request."); //$NON-NLS-1$

        // If local or global transaction is not started.
	    if (tc.getTransactionType() == Scope.NONE && !requestMsg.isNoExec()) {
		    if (!startAutoWrap) {
				return null;
			}
            Boolean startAutoWrapTxn = false;
            
            if(RequestMessage.TXN_WRAP_ON.equals(requestMsg.getTxnAutoWrapMode())){ 
                startAutoWrapTxn = true;
            } else if (RequestMessage.TXN_WRAP_DETECT.equals(requestMsg.getTxnAutoWrapMode())){
            	boolean transactionalRead = requestMsg.getTransactionIsolation() == Connection.TRANSACTION_REPEATABLE_READ
						|| requestMsg.getTransactionIsolation() == Connection.TRANSACTION_SERIALIZABLE;
        		startAutoWrapTxn = processPlan.requiresTransaction(transactionalRead);
        		if (startAutoWrapTxn == null) {
        		 	startAutoWrapTxn = false;
        		}
            } 
            
            if (startAutoWrapTxn) {
                try {
                    transactionService.begin(tc);
                } catch (XATransactionException err) {
                     throw new TeiidComponentException(QueryPlugin.Event.TEIID30493, err);
                }
            }
        } 
        
        tc.setIsolationLevel(requestMsg.getTransactionIsolation());
        this.transactionContext = tc;
        return this.transactionContext;
    }

    /**
     * state side effects:
     *      creates the analysis record
     * 		creates the command context
     * 		sets the pre-rewrite command on the request
     * 		adds a limit clause if the row limit is specified
     * 		sets the processor plan
     * 
     * @throws TeiidComponentException
     * @throws TeiidProcessingException 
     */
    protected void generatePlan(boolean addLimit) throws TeiidComponentException, TeiidProcessingException {
        Command command = parseCommand();

        List<Reference> references = ReferenceCollectorVisitor.getReferences(command);
        
        this.analysisRecord = new AnalysisRecord(requestMsg.getShowPlan() != ShowPlan.OFF, requestMsg.getShowPlan() == ShowPlan.DEBUG);
                
        resolveCommand(command);

        checkReferences(references);
        
        validateAccess(requestMsg.getCommands(), command, CommandType.USER);
        
        // EXTENSION
		// ISSUE: DVCORE-1177, DVCORE-2678
		// FIXED BY: Salvatore Raunich
		// DATE: 2014.06.05, 2015.02.21
    	DVCaller.call(57, command, requestMsg);		
		// END EXTENSION

    	this.userCommand = (Command) command.clone();
    	
    	// EXTENSION
		// ISSUE: DVCORE-331, DVCORE-522, DVCORE-840
		// FIXED BY: Salvatore Raunich
		// DATE: 2013.04.23, 2013.07.16, 2013.12.10
    	if (RuleConstants.TEST3 != null) {
    		Command cloneCommand = (Command) command.clone();
    		// validating the query will also rewrite indices in group by clause
    		validateQuery(cloneCommand);
        	((MyOptimizerRule)RuleConstants.TEST3).setCommand(cloneCommand, context);
            ((MyOptimizerRule)RuleConstants.TEST3).setCurrentCommand(command, context);
		}		
		// END EXTENSION
        
        Collection<GroupSymbol> groups = GroupCollectorVisitor.getGroups(command, true);
        for (GroupSymbol groupSymbol : groups) {
			if (groupSymbol.isTempTable()) {
				this.context.setDeterminismLevel(Determinism.SESSION_DETERMINISTIC);
				break;
			}
		}

        validateQuery(command);
        
        command = QueryRewriter.rewrite(command, metadata, context);
        
        /*
         * Adds a row limit to a query if Statement.setMaxRows has been called and the command
         * doesn't already have a limit clause.
         */
        if (addLimit && requestMsg.getRowLimit() > 0 && command instanceof QueryCommand) {
            QueryCommand query = (QueryCommand)command;
            if (query.getLimit() == null) {
                query.setLimit(new Limit(null, new Constant(new Integer(requestMsg.getRowLimit()), DataTypeManager.DefaultDataClasses.INTEGER)));
                this.addedLimit = true;
            }
        }
        
    	// If using multi-source models, insert a proxy to simplify the supported capabilities.  This is 
        // done OUTSIDE the cache (wrapped around the cache) intentionally to avoid caching the simplified
        // capabilities which may be different for the same model in a different VDB used by this same DQP.
    	CapabilitiesFinder finder = this.capabilitiesFinder;
        if(this.multiSourceModels != null) {
            finder = new MultiSourceCapabilitiesFinder(finder, this.multiSourceModels);
        }
        
        boolean debug = analysisRecord.recordDebug();
		if(debug) {
			analysisRecord.println("\n============================================================================"); //$NON-NLS-1$
            analysisRecord.println("USER COMMAND:\n" + command);		 //$NON-NLS-1$
        }
        // Run the optimizer
        try {
        	CommandContext.pushThreadLocalContext(context);
            processPlan = QueryOptimizer.optimizePlan(command, metadata, idGenerator, finder, analysisRecord, context);
        } finally {
        	CommandContext.popThreadLocalContext();
            String debugLog = analysisRecord.getDebugLog();
            if(debugLog != null && debugLog.length() > 0) {
                LogManager.log(requestMsg.getShowPlan()==ShowPlan.DEBUG?MessageLevel.INFO:MessageLevel.TRACE, LogConstants.CTX_QUERY_PLANNER, debugLog);               
            }
            if (analysisRecord.recordAnnotations() && analysisRecord.getAnnotations() != null && !analysisRecord.getAnnotations().isEmpty()) {
            	LogManager.logDetail(LogConstants.CTX_QUERY_PLANNER, analysisRecord.getAnnotations());
            }
        }
        LogManager.logDetail(LogConstants.CTX_DQP, new Object[] { QueryPlugin.Util.getString("BasicInterceptor.ProcessTree_for__4"), requestId, processPlan }); //$NON-NLS-1$
    }

    public void processRequest() 
        throws TeiidComponentException, TeiidProcessingException {
    	Object isExt2Applicable = null;
    	try{
    	LogManager.logDetail(LogConstants.CTX_DQP, this.requestId, "executing", this.requestMsg.isPreparedStatement()?"prepared":"", this.requestMsg.getCommandString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    	
        initMetadata();

        isExt2Applicable = DVCaller.callWithThrow(TeiidProcessingException.class, 2, requestMsg, this.context, requestId.toString());

        generatePlan(true);
        
        DVCaller.callWithThrow(TeiidProcessingException.class, 74, processPlan, isExt2Applicable);
        
        postProcessXML();

        DVCaller.callWithThrow(TeiidProcessingException.class, 1002, this);

        createProcessor();
    	} catch (Throwable t) {
    		DVCaller.callWithThrow(TeiidProcessingException.class, 127, isExt2Applicable);
    		throw t;
    	} finally {
    		DVCaller.call(34, context);
            DVCaller.call(277, context, this);
            if (userCommand != null) {
                final Collection<GroupSymbol> groups = GroupCollectorVisitor.getGroups(userCommand, true);
                groups.stream()
                        .map(GroupSymbol::getMetadataID)
                        .filter(Objects::nonNull)
                        .distinct()
                        .filter(o -> o instanceof Table t && t.getParent() != null && t.getParent().isLazy())
                        .map(o -> (Table) o)
                        .forEach(t -> t.getParent().removeTable(t.getName()));
    	}
    }
    }

	private void postProcessXML() {
        if (requestMsg.getXMLFormat() != null && processPlan instanceof XMLPlan) {
        	((XMLPlan)processPlan).setXMLFormat(requestMsg.getXMLFormat());
        }
        this.context.setValidateXML(requestMsg.getValidationMode());
	}

	public boolean validateAccess(String[] commandStr, Command command, CommandType type) throws QueryValidatorException, TeiidComponentException {
		return validateAccess(commandStr, command, type, false);
	}

	public boolean validateAccess(String[] commandStr, Command command, CommandType type, boolean parent) throws QueryValidatorException, TeiidComponentException {
		boolean returnsResultSet = command.returnsResultSet();
		this.returnsUpdateCount = !(command instanceof StoredExecutable) && !returnsResultSet;
		if (!parent) {
			if ((this.requestMsg.getResultsMode() == ResultsMode.UPDATECOUNT && !returnsUpdateCount)
					|| (this.requestMsg.getResultsMode() == ResultsMode.RESULTSET && !returnsResultSet)) {
				throw new QueryValidatorException(QueryPlugin.Event.TEIID30490, QueryPlugin.Util.getString(this.requestMsg.getResultsMode() == ResultsMode.RESULTSET ? "Request.no_result_set" : "Request.result_set")); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		createCommandContext(command);
		// this part will be introduced in the scope of TEIID-3369, TEIID-3383, TEIID-5790 issues
		/*if (this.requestMsg.isReturnAutoGeneratedKeys() && command instanceof Insert) {
			Insert insert = (Insert)command;
			List<ElementSymbol> variables = ResolverUtil.resolveElementsInGroup(insert.getGroup(), metadata);
			variables.removeAll(insert.getVariables());
			Object pk = metadata.getPrimaryKey(insert.getGroup().getMetadataID());
			if (pk != null) {
				List<?> cols = metadata.getElementIDsInKey(pk);
				for (Iterator<ElementSymbol> iter = variables.iterator(); iter.hasNext();) {
					ElementSymbol variable = iter.next();
					if (!(metadata.elementSupports(variable.getMetadataID(), SupportConstants.Element.NULL) ||
							metadata.elementSupports(variable.getMetadataID(), SupportConstants.Element.AUTO_INCREMENT))
							|| !cols.contains(variable.getMetadataID())) {
						iter.remove();
					}
				}
				context.setReturnAutoGeneratedKeys(variables);
			}
		}*/
		// EXTENSION
		// ISSUE: DVCORE-375, DVCORE-950
		// FIXED BY: Salvatore Raunich, Salvatore Raunich
		// DATE: 2013.04.19, 2014.02.03
		Boolean skipAuthorization = (Boolean) DVCaller.call(235, this.workContext.getSession(), command);
		if (skipAuthorization != null && skipAuthorization)
			return true;
		// END EXTENSION
		if (!this.workContext.isAdmin() && this.authorizationValidator != null) {
			context.setAllowResourcesByParent(parent);
			try {
				return this.authorizationValidator.validate(commandStr, command, metadata, context, type);
			} finally {
				context.setAllowResourcesByParent(false);
			}
		}
		return false;
	}
	
	public void setExecutor(Executor executor) {
		this.executor = executor;
	}

	public boolean isReturingParams() {
		return false;
	}

	// EXTENSION
	// ISSUE: DVCORE-316, DVCORE-913
	// FIXED BY: Roman Pedchenko, Dmitry Pogorelov
	// DATE: 2013.05.12, 2014.09.29
	private Map<String, String> dvoptions;

	public Map<String, String> getDvOptions() {
		return dvoptions;
	}

	public void setDvOptions(Map<String, String> options) {
		this.dvoptions = options;
	}
	// END EXTENSION
}
