/*
* 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.
*/
package org.teiid.query.tempdata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import org.teiid.adminapi.impl.SessionMetadata;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.api.exception.query.ExpressionEvaluationException;
import org.teiid.api.exception.query.QueryMetadataException;
import org.teiid.api.exception.query.QueryProcessingException;
import org.teiid.api.exception.query.QueryResolverException;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.client.security.SessionToken;
import org.teiid.common.buffer.BlockedException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.TupleBuffer;
import org.teiid.common.buffer.TupleSource;
import org.teiid.core.CoreConstants;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.types.ArrayImpl;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.types.TransformationException;
import org.teiid.core.util.Assertion;
import org.teiid.core.util.StringUtil;
import org.teiid.dqp.internal.process.CachedResults;
import org.teiid.dqp.internal.process.DQPWorkContext;
import org.teiid.dqp.internal.process.RequestWorkItem;
import org.teiid.dqp.internal.process.SessionAwareCache;
import org.teiid.dqp.internal.process.SessionAwareCache.CacheID;
import org.teiid.dqp.internal.process.TupleSourceCache;
import org.teiid.events.EventDistributor;
import org.teiid.language.SQLConstants;
import org.teiid.language.SQLConstants.Reserved;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.metadata.FunctionMethod.Determinism;
import org.teiid.query.QueryPlugin;
import org.teiid.query.eval.Evaluator;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.TempMetadataAdapter;
import org.teiid.query.metadata.TempMetadataID;
import org.teiid.query.optimizer.relational.RelationalPlanner;
import org.teiid.query.parser.ParseInfo;
import org.teiid.query.processor.BatchCollector;
import org.teiid.query.processor.CollectionTupleSource;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.processor.RegisterRequestParameter;
import org.teiid.query.resolver.util.ResolverUtil;
import org.teiid.query.sql.lang.*;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.symbol.Reference;
import org.teiid.query.tempdata.GlobalTableStoreImpl.MatTableInfo;
import org.teiid.query.util.CommandContext;
import org.teiid.translator.CacheDirective.Scope;
/**
* This proxy ProcessorDataManager is used to handle temporary tables.
*
* This isn't handled as a connector because of the temporary metadata and
* the create/drop handling (which doesn't have push down support)
*/
public class TempTableDataManager implements ProcessorDataManager {
public interface RequestExecutor {
void execute(String command, List<?> parameters);
boolean isShutdown();
}
public abstract class ProxyTupleSource implements TupleSource {
TupleSource actual;
@Override
public List<?> nextTuple() throws TeiidComponentException,
TeiidProcessingException {
if (actual == null) {
actual = createTupleSource();
}
return actual.nextTuple();
}
protected abstract TupleSource createTupleSource() throws TeiidComponentException,
TeiidProcessingException;
@Override
public void closeSource() {
if (actual != null) {
actual.closeSource();
}
}
}
private static final String REFRESHMATVIEWROW = ".refreshmatviewrow"; //$NON-NLS-1$
private static final String REFRESHMATVIEWROWS = ".refreshmatviewrows"; //$NON-NLS-1$
private static final String REFRESHMATVIEW = ".refreshmatview"; //$NON-NLS-1$
public static final String CODE_PREFIX = "#CODE_"; //$NON-NLS-1$
private static String REFRESH_SQL = SQLConstants.Reserved.CALL + ' ' + CoreConstants.SYSTEM_ADMIN_MODEL + REFRESHMATVIEW + "(?, ?)"; //$NON-NLS-1$
private ProcessorDataManager processorDataManager;
private BufferManager bufferManager;
private SessionAwareCache<CachedResults> cache;
private RequestExecutor executor;
private EventDistributor eventDistributor;
public TempTableDataManager(ProcessorDataManager processorDataManager, BufferManager bufferManager,
SessionAwareCache<CachedResults> cache){
this.processorDataManager = processorDataManager;
this.bufferManager = bufferManager;
this.cache = cache;
}
public void setExecutor(RequestExecutor executor) {
this.executor = executor;
}
public void setEventDistributor(EventDistributor eventDistributor) {
this.eventDistributor = eventDistributor;
}
public TupleSource registerRequest(
CommandContext context,
Command command,
String modelName,
RegisterRequestParameter parameterObject)
throws TeiidComponentException, TeiidProcessingException {
if (parameterObject.info != null) {
TupleSourceCache tsc = context.getTupleSourceCache();
if (tsc != null) {
return tsc.getSharedTupleSource(context, command, modelName, parameterObject, bufferManager, this);
}
}
TempTableStore tempTableStore = context.getTempTableStore();
if(tempTableStore != null) {
try {
TupleSource result = registerRequest(context, modelName, command);
if (result != null) {
return result;
}
} catch (BlockedException e) {
throw new AssertionError("blocked is not expected"); //$NON-NLS-1$
}
}
return this.processorDataManager.registerRequest(context, command, modelName, parameterObject);
}
TupleSource registerRequest(final CommandContext context, String modelName, final Command command) throws TeiidComponentException, TeiidProcessingException {
final TempTableStore contextStore = context.getTempTableStore();
if (command instanceof Query) {
Query query = (Query)command;
if (modelName != null && !modelName.equals(TempMetadataAdapter.TEMP_MODEL.getID())) {
return null;
}
return registerQuery(context, contextStore, query);
}
if (command instanceof ProcedureContainer) {
if (command instanceof StoredProcedure) {
StoredProcedure proc = (StoredProcedure)command;
if (CoreConstants.SYSTEM_ADMIN_MODEL.equals(modelName)) {
TupleSource result = handleSystemProcedures(context, proc);
if (result != null) {
return result;
}
} else if (proc.getGroup().isGlobalTable()) {
return handleCachedProcedure(context, proc);
}
return null; //it's not a stored procedure we want to handle
}
final GroupSymbol group = ((ProcedureContainer)command).getGroup();
if (!modelName.equals(TempMetadataAdapter.TEMP_MODEL.getID()) || !group.isTempGroupSymbol()) {
return null;
}
return new ProxyTupleSource() {
@Override
protected TupleSource createTupleSource() throws TeiidComponentException,
TeiidProcessingException {
final String groupKey = group.getNonCorrelationName();
final TempTable table = contextStore.getOrCreateTempTable(groupKey, command, bufferManager, true, true, context, group);
if (command instanceof Insert) {
Insert insert = (Insert)command;
TupleSource ts = insert.getTupleSource();
if (ts == null) {
Evaluator eval = new Evaluator(Collections.emptyMap(), TempTableDataManager.this, context);
List<Object> values = new ArrayList<Object>(insert.getValues().size());
for (Expression expr : (List<Expression>)insert.getValues()) {
values.add(eval.evaluate(expr, null));
}
ts = new CollectionTupleSource(Arrays.asList(values).iterator());
}
return table.insert(ts, insert.getVariables(), true, insert.isUpsert(), context);
}
if (command instanceof Update) {
final Update update = (Update)command;
final Criteria crit = update.getCriteria();
return table.update(crit, update.getChangeList());
}
if (command instanceof Delete) {
final Delete delete = (Delete)command;
final Criteria crit = delete.getCriteria();
if (crit == null) {
//TODO: we'll add a real truncate later
long rows = table.truncate(false);
return CollectionTupleSource.createUpdateCountTupleSource((int)Math.min(Integer.MAX_VALUE, rows));
}
return table.delete(crit);
}
throw new AssertionError("unknown command " + command); //$NON-NLS-1$
}
};
}
if (command instanceof Create) {
Create create = (Create)command;
String tempTableName = create.getTable().getName();
if (contextStore.hasTempTable(tempTableName, true)) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30229, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30229, tempTableName));
}
if (create.getTableMetadata() != null) {
contextStore.addForeignTempTable(tempTableName, create);
} else {
contextStore.addTempTable(tempTableName, create, bufferManager, true, context);
}
return CollectionTupleSource.createUpdateCountTupleSource(0);
}
if (command instanceof Drop) {
String tempTableName = ((Drop)command).getTable().getName();
contextStore.removeTempTableByName(tempTableName, context);
return CollectionTupleSource.createUpdateCountTupleSource(0);
}
if (command instanceof AlterTempTable) {
AlterTempTable att = (AlterTempTable)command;
TempTable tt = contextStore.getTempTable(att.getTempTable());
Assertion.isNotNull(tt, "Table doesn't exist"); //$NON-NLS-1$
tt.setUpdatable(false);
if (att.getIndexColumns() != null && tt.getRowCount() > 2*tt.getTree().getPageSize(true)) {
for (List<ElementSymbol> cols : att.getIndexColumns()) {
tt.addIndex(cols, false);
}
}
return CollectionTupleSource.createUpdateCountTupleSource(0);
}
return null;
}
private TupleSource handleCachedProcedure(final CommandContext context,
StoredProcedure proc) throws TeiidComponentException,
QueryMetadataException, TeiidProcessingException {
String fullName = context.getMetadata().getFullName(proc.getProcedureID());
LogManager.logDetail(LogConstants.CTX_DQP, "processing cached procedure request for", fullName); //$NON-NLS-1$
LinkedList<Object> vals = new LinkedList<Object>();
for (SPParameter param : proc.getInputParameters()) {
vals.add(((Constant)param.getExpression()).getValue());
}
//collapse the hash to single byte for the key to restrict the possible results to 256
int hash = vals.hashCode();
hash |= (hash >>> 16);
hash |= (hash >>> 8);
hash &= 0x000000ff;
final CacheID cid = new CacheID(new ParseInfo(), fullName + hash, context.getVdbName(),
context.getVdbVersion(), context.getConnectionId(), context.getUserName());
cid.setParameters(vals);
CachedResults results = cache.get(cid);
if (results != null) {
TupleBuffer buffer = results.getResults();
return buffer.createIndexedTupleSource();
}
//construct a query with a no cache hint
final CacheHint hint = proc.getCacheHint();
proc.setCacheHint(null);
Option option = new Option();
option.setNoCache(true);
option.addNoCacheGroup(fullName);
proc.setOption(option);
StoredProcedure cloneProc = (StoredProcedure)proc.clone();
int i = 0;
for (SPParameter param : cloneProc.getInputParameters()) {
param.setExpression(new Reference(i++));
}
final QueryProcessor qp = context.getQueryProcessorFactory().createQueryProcessor(cloneProc.toString(), fullName.toUpperCase(), context, vals.toArray());
final BatchCollector bc = qp.createBatchCollector();
return new ProxyTupleSource() {
boolean success = false;
@Override
protected TupleSource createTupleSource() throws TeiidComponentException,
TeiidProcessingException {
TupleBuffer tb = bc.collectTuples();
CachedResults cr = new CachedResults();
cr.setResults(tb, qp.getProcessorPlan());
Determinism determinismLevel = qp.getContext().getDeterminismLevel();
if (hint != null && hint.getDeterminism() != null) {
LogManager.logTrace(LogConstants.CTX_DQP, new Object[] { "Cache hint modified the query determinism from ",determinismLevel, " to ", hint.getDeterminism() }); //$NON-NLS-1$ //$NON-NLS-2$
determinismLevel = hint.getDeterminism();
}
cache.put(cid, determinismLevel, cr, hint != null?hint.getTtl():null);
context.setDeterminismLevel(determinismLevel);
success = true;
return tb.createIndexedTupleSource();
}
@Override
public void closeSource() {
super.closeSource();
qp.closeProcessing();
if (!success && bc.getTupleBuffer() != null) {
bc.getTupleBuffer().remove();
}
}
};
}
private TupleSource handleSystemProcedures(final CommandContext context, StoredProcedure proc)
throws TeiidComponentException, QueryMetadataException,
QueryProcessingException, QueryResolverException,
QueryValidatorException, TeiidProcessingException,
ExpressionEvaluationException {
final QueryMetadataInterface metadata = context.getMetadata();
if (StringUtil.endsWithIgnoreCase(proc.getProcedureCallableName(), REFRESHMATVIEW)) {
Object groupID = validateMatView(metadata, (String)((Constant)proc.getParameter(2).getExpression()).getValue());
TempMetadataID matTableId = context.getGlobalTableStore().getGlobalTempTableMetadataId(groupID);
final GlobalTableStore globalStore = getGlobalStore(context, matTableId);
String matViewName = metadata.getFullName(groupID);
String matTableName = metadata.getFullName(matTableId);
LogManager.logDetail(LogConstants.CTX_MATVIEWS, "processing refreshmatview for", matViewName); //$NON-NLS-1$
boolean invalidate = Boolean.TRUE.equals(((Constant)proc.getParameter(3).getExpression()).getValue());
boolean needsLoading = globalStore.getMatTableInfo(matTableName).getAndClearAsynch();
if (!needsLoading) {
needsLoading = globalStore.needsLoading(matTableName, globalStore.getAddress(), true, true, invalidate);
if (needsLoading) {
needsLoading = globalStore.needsLoading(matTableName, globalStore.getAddress(), false, false, invalidate);
}
}
if (!needsLoading) {
return CollectionTupleSource.createUpdateCountTupleSource(-1);
}
GroupSymbol matTable = new GroupSymbol(matTableName);
matTable.setMetadataID(matTableId);
return loadGlobalTable(context, matTable, matTableName, globalStore);
} else if (StringUtil.endsWithIgnoreCase(proc.getProcedureCallableName(), REFRESHMATVIEWROWS)) {
final Object groupID = validateMatView(metadata, (String)((Constant)proc.getParameter(2).getExpression()).getValue());
TempMetadataID matTableId = context.getGlobalTableStore().getGlobalTempTableMetadataId(groupID);
final GlobalTableStore globalStore = getGlobalStore(context, matTableId);
Object pk = metadata.getPrimaryKey(groupID);
String matViewName = metadata.getFullName(groupID);
if (pk == null) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30230, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30230, matViewName));
}
List<?> ids = metadata.getElementIDsInKey(pk);
Object[][] params = (Object[][]) ((ArrayImpl) ((Constant)proc.getParameter(3).getExpression()).getValue()).getValues();
return updateMatviewRows(context, metadata, groupID, globalStore, matViewName, ids, params);
} else if (StringUtil.endsWithIgnoreCase(proc.getProcedureCallableName(), REFRESHMATVIEWROW)) {
final Object groupID = validateMatView(metadata, (String)((Constant)proc.getParameter(2).getExpression()).getValue());
TempMetadataID matTableId = context.getGlobalTableStore().getGlobalTempTableMetadataId(groupID);
final GlobalTableStore globalStore = getGlobalStore(context, matTableId);
Object pk = metadata.getPrimaryKey(groupID);
final String matViewName = metadata.getFullName(groupID);
if (pk == null) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30230, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30230, matViewName));
}
List<?> ids = metadata.getElementIDsInKey(pk);
Constant key = (Constant)proc.getParameter(3).getExpression();
Object initialValue = key.getValue();
SPParameter keyOther = proc.getParameter(4);
Object[] param = null;
if (keyOther != null) {
Object[] otherCols = ((ArrayImpl) ((Constant)keyOther.getExpression()).getValue()).getValues();
if (otherCols != null) {
param = new Object[1 + otherCols.length];
param[0] = initialValue;
for (int i = 0; i < otherCols.length; i++) {
param[i+1] = otherCols[i];
}
}
}
if (param == null) {
param = new Object[] {initialValue};
}
Object[][] params = new Object[][] {param};
return updateMatviewRows(context, metadata, groupID, globalStore,
matViewName, ids, params);
}
return null;
}
private TupleSource updateMatviewRows(final CommandContext context,
final QueryMetadataInterface metadata, final Object groupID,
final GlobalTableStore globalStore, final String matViewName,
List<?> ids, Object[][] params) throws QueryProcessingException,
TeiidComponentException, QueryMetadataException,
TransformationException {
final String matTableName = RelationalPlanner.MAT_PREFIX+matViewName.toUpperCase();
MatTableInfo info = globalStore.getMatTableInfo(matTableName);
if (!info.isValid()) {
return CollectionTupleSource.createUpdateCountTupleSource(-1);
}
TempTable tempTable = globalStore.getTempTable(matTableName);
if (!tempTable.isUpdatable()) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30232, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30232, matViewName));
}
List<Object[]> converted = new ArrayList<Object[]>();
for (Object[] param : params) {
if (param == null || ids.size() != param.length) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30231, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30231, matViewName, ids.size(), param == null ? 0 : param.length));
}
final Object[] vals = new Object[param.length];
for (int i = 0; i < ids.size(); i++) {
Object value = param[i];
String targetTypeName = metadata.getElementRuntimeTypeName(ids.get(i));
value = DataTypeManager.transformValue(value, DataTypeManager.getDataTypeClass(targetTypeName));
vals[i] = value;
}
converted.add(vals);
}
final Iterator<Object[]> paramIter = converted.iterator();
Iterator<?> iter = ids.iterator();
StringBuilder criteria = new StringBuilder();
for (int i = 0; i < ids.size(); i++) {
Object id = iter.next();
if (i != 0) {
criteria.append(" AND "); //$NON-NLS-1$
}
criteria.append(metadata.getFullName(id)).append(" = ?"); //$NON-NLS-1$
}
final String queryString = Reserved.SELECT + " * " + Reserved.FROM + ' ' + matViewName + ' ' + Reserved.WHERE + ' ' + //$NON-NLS-1$
criteria.toString() + ' ' + Reserved.OPTION + ' ' + Reserved.NOCACHE;
return new ProxyTupleSource() {
private QueryProcessor qp;
private TupleSource ts;
private Object[] params;
private int count;
@Override
protected TupleSource createTupleSource()
throws TeiidComponentException,
TeiidProcessingException {
while (true) {
if (qp == null) {
params = paramIter.next();
LogManager.logInfo(LogConstants.CTX_MATVIEWS, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30012, matViewName, Arrays.toString(params)));
qp = context.getQueryProcessorFactory().createQueryProcessor(queryString, matViewName.toUpperCase(), context, params);
ts = new BatchCollector.BatchProducerTupleSource(qp);
}
List<?> tuple = ts.nextTuple();
boolean delete = false;
if (tuple == null) {
delete = true;
tuple = Arrays.asList(params);
} else {
tuple = new ArrayList<Object>(tuple); //ensure the list is serializable
}
List<?> result = globalStore.updateMatViewRow(matTableName, tuple, delete);
if (result != null) {
count++;
}
if (eventDistributor != null) {
eventDistributor.updateMatViewRow(context.getVdbName(), context.getVdbVersion(), metadata.getName(metadata.getModelID(groupID)), metadata.getName(groupID), tuple, delete);
}
qp.closeProcessing();
qp = null;
ts = null;
if (!paramIter.hasNext()) {
break;
}
}
return CollectionTupleSource.createUpdateCountTupleSource(count);
}
@Override
public void closeSource() {
super.closeSource();
if (qp != null) {
qp.closeProcessing();
}
}
};
}
private Object validateMatView(QueryMetadataInterface metadata, String viewName) throws TeiidComponentException,
TeiidProcessingException {
try {
Object groupID = metadata.getGroupID(viewName);
if (!metadata.hasMaterialization(groupID) || metadata.getMaterialization(groupID) != null) {
throw new QueryProcessingException(QueryPlugin.Event.TEIID30233, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30233, viewName));
}
return groupID;
} catch (QueryMetadataException e) {
throw new TeiidProcessingException(QueryPlugin.Event.TEIID30234, e);
}
}
private TupleSource registerQuery(final CommandContext context,
final TempTableStore contextStore, final Query query) {
final GroupSymbol group = query.getFrom().getGroups().get(0);
if (!group.isTempGroupSymbol()) {
return null;
}
final String tableName = group.getNonCorrelationName();
if (group.isGlobalTable()) {
TempMetadataID matTableId = (TempMetadataID)group.getMetadataID();
final GlobalTableStore globalStore = getGlobalStore(context, matTableId);
final MatTableInfo info = globalStore.getMatTableInfo(tableName);
return new ProxyTupleSource() {
Future<Void> moreWork = null;
TupleSource loadingTupleSource;
DQPWorkContext newWorkContext;
@Override
protected TupleSource createTupleSource()
throws TeiidComponentException,
TeiidProcessingException {
if (loadingTupleSource != null) {
load();
} else {
boolean load = false;
if (!info.isUpToDate()) {
boolean invalidate = shouldInvalidate(context.getVdb());
load = globalStore.needsLoading(tableName, globalStore.getAddress(), true, false, info.isValid() && invalidate);
if (load) {
load = globalStore.needsLoading(tableName, globalStore.getAddress(), false, false, info.isValid() && invalidate);
}
if (!load) {
synchronized (info) {
if (!info.isUpToDate()) {
RequestWorkItem workItem = context.getWorkItem();
info.addWaiter(workItem);
if (moreWork != null) {
moreWork.cancel(false);
}
moreWork = workItem.scheduleWork(10000); //fail-safe - attempt again in 10 seconds
throw BlockedException.block("Blocking on mat view load", tableName); //$NON-NLS-1$
}
}
} else {
if (!info.isValid() || executor == null) {
//blocking load
//TODO: we should probably do all loads using a temp session
if (info.getVdbMetaData() != null && context.getDQPWorkContext() != null && !info.getVdbMetaData().getFullName().equals(context.getDQPWorkContext().getVDB().getFullName())) {
assert executor != null;
//load with by pretending we're in the imported vdb
newWorkContext = createWorkContext(context, info.getVdbMetaData());
CommandContext newContext = context.clone();
newContext.setNewVDBState(newWorkContext);
loadingTupleSource = loadGlobalTable(newContext, group, tableName, newContext.getGlobalTableStore());
} else {
loadingTupleSource = loadGlobalTable(context, group, tableName, globalStore);
}
load();
} else {
loadViaRefresh(context, tableName, context.getDQPWorkContext().getVDB(), info);
}
}
}
}
TempTable table = globalStore.getTempTable(tableName);
context.accessedDataObject(group.getMetadataID());
TupleSource result = table.createTupleSource(query.getProjectedSymbols(), query.getCriteria(), query.getOrderBy());
cancelMoreWork();
return result;
}
private void load() throws TeiidComponentException,
TeiidProcessingException {
try {
if (newWorkContext != null) {
newWorkContext.runInContext(new Callable<Void>() {
@Override
public Void call() throws Exception {
loadingTupleSource.nextTuple();
return null;
}
});
} else {
loadingTupleSource.nextTuple();
}
} catch (Throwable e) {
rethrow(e);
}
}
private void cancelMoreWork() {
if (moreWork != null) {
moreWork.cancel(false);
moreWork = null;
}
}
@Override
public void closeSource() {
if (loadingTupleSource != null) {
loadingTupleSource.closeSource();
}
super.closeSource();
cancelMoreWork();
}
};
}
//it's not expected for a blocked exception to bubble up from here, so return a tuplesource to perform getOrCreateTempTable
return new ProxyTupleSource() {
@Override
protected TupleSource createTupleSource()
throws TeiidComponentException, TeiidProcessingException {
TempTableStore tts = contextStore;
TempTable tt = tts.getOrCreateTempTable(tableName, query, bufferManager, true, false, context, group);
if (context.getDataObjects() != null) {
Object id = RelationalPlanner.getTrackableGroup(group, context.getMetadata());
if (id != null) {
context.accessedDataObject(id);
}
}
return tt.createTupleSource(query.getProjectedSymbols(), query.getCriteria(), query.getOrderBy());
}
};
}
private GlobalTableStore getGlobalStore(final CommandContext context, TempMetadataID matTableId) {
GlobalTableStore globalStore = context.getGlobalTableStore();
if (matTableId.getCacheHint() == null || matTableId.getCacheHint().getScope() == null || Scope.VDB.compareTo(matTableId.getCacheHint().getScope()) <= 0) {
return globalStore;
}
return context.getSessionScopedStore(true);
}
private void loadViaRefresh(final CommandContext context, final String tableName, VDBMetaData vdb, MatTableInfo info) throws TeiidProcessingException, TeiidComponentException {
info.setAsynchLoad();
DQPWorkContext workContext = createWorkContext(context, vdb);
final String viewName = tableName.substring(RelationalPlanner.MAT_PREFIX.length());
workContext.runInContext(new Runnable() {
@Override
public void run() {
executor.execute(REFRESH_SQL, Arrays.asList(viewName, Boolean.FALSE));
}
});
}
private DQPWorkContext createWorkContext(final CommandContext context,
VDBMetaData vdb) {
SessionMetadata session = createTemporarySession(context.getUserName(), "asynch-mat-view-load", vdb); //$NON-NLS-1$
session.setSubject(context.getSubject());
session.setSecurityDomain(context.getSession().getSecurityDomain());
session.setSecurityContext(context.getSession().getSecurityContext());
DQPWorkContext workContext = new DQPWorkContext();
workContext.setAdmin(true);
DQPWorkContext current = context.getDQPWorkContext();
workContext.setSession(session);
workContext.setPolicies(current.getAllowedDataPolicies());
workContext.setSecurityHelper(current.getSecurityHelper());
return workContext;
}
private TupleSource loadGlobalTable(final CommandContext context,
final GroupSymbol group, final String tableName, final GlobalTableStore globalStore)
throws TeiidComponentException, TeiidProcessingException {
LogManager.logInfo(LogConstants.CTX_MATVIEWS, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30013, tableName));
final QueryMetadataInterface metadata = context.getMetadata();
final List<ElementSymbol> allColumns = ResolverUtil.resolveElementsInGroup(group, metadata);
final TempTable table = globalStore.createMatTable(tableName, group);
table.setUpdatable(false);
return new ProxyTupleSource() {
TupleSource insertTupleSource;
boolean success;
QueryProcessor qp;
boolean closed;
boolean errored;
@Override
protected TupleSource createTupleSource() throws TeiidComponentException,
TeiidProcessingException {
long rowCount = -1;
try {
if (insertTupleSource == null) {
String fullName = metadata.getFullName(group.getMetadataID());
String transformation = metadata.getVirtualPlan(group.getMetadataID()).getQuery();
qp = context.getQueryProcessorFactory().createQueryProcessor(transformation, fullName, context);
insertTupleSource = new BatchCollector.BatchProducerTupleSource(qp);
}
table.insert(insertTupleSource, allColumns, false, false, null);
table.getTree().compact();
rowCount = table.getRowCount();
Determinism determinism = qp.getContext().getDeterminismLevel();
context.setDeterminismLevel(determinism);
//TODO: could pre-process indexes to remove overlap
for (Object index : metadata.getIndexesInGroup(group.getMetadataID())) {
List<ElementSymbol> columns = GlobalTableStoreImpl.resolveIndex(metadata, allColumns, index);
table.addIndex(columns, false);
}
for (Object key : metadata.getUniqueKeysInGroup(group.getMetadataID())) {
List<ElementSymbol> columns = GlobalTableStoreImpl.resolveIndex(metadata, allColumns, key);
table.addIndex(columns, true);
}
CacheHint hint = table.getCacheHint();
if (hint != null && table.getPkLength() > 0) {
table.setUpdatable(hint.isUpdatable(false));
}
if (determinism.compareTo(Determinism.VDB_DETERMINISTIC) < 0 && (hint == null || hint.getScope() == null || Scope.VDB.compareTo(hint.getScope()) <= 0)) {
LogManager.logInfo(LogConstants.CTX_DQP, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31143, determinism, tableName)); //$NON-NLS-1$
}
globalStore.loaded(tableName, table);
success = true;
LogManager.logInfo(LogConstants.CTX_MATVIEWS, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30014, tableName, rowCount));
return CollectionTupleSource.createUpdateCountTupleSource((int)Math.min(Integer.MAX_VALUE, rowCount));
} catch (BlockedException e) {
throw e;
} catch (Exception e) {
errored = true;
if (executor == null || !executor.isShutdown()) {
//if we're shutting down, no need to log
LogManager.logError(LogConstants.CTX_MATVIEWS, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30015, tableName));
}
closeSource();
rethrow(e);
throw new AssertionError();
}
}
@Override
public void closeSource() {
if (closed) {
return;
}
if (!errored && !success) {
LogManager.logInfo(LogConstants.CTX_MATVIEWS, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31153, tableName));
}
closed = true;
if (!success) {
globalStore.failedLoad(tableName);
table.remove();
}
if (qp != null) {
qp.closeProcessing();
}
super.closeSource();
}
};
}
public Object lookupCodeValue(CommandContext context, String codeTableName,
String returnElementName, String keyElementName, Object keyValue)
throws BlockedException, TeiidComponentException,
TeiidProcessingException {
//we are not using a resolved form of a lookup, so we canonicallize with upper case
codeTableName = codeTableName.toUpperCase();
keyElementName = keyElementName.toUpperCase();
returnElementName = returnElementName.toUpperCase();
String matTableName = CODE_PREFIX + codeTableName + ElementSymbol.SEPARATOR + keyElementName + ElementSymbol.SEPARATOR + returnElementName;
TupleSource ts = context.getCodeLookup(matTableName, keyValue);
if (ts == null) {
QueryMetadataInterface metadata = context.getMetadata();
TempMetadataID id = context.getGlobalTableStore().getCodeTableMetadataId(codeTableName,
returnElementName, keyElementName, matTableName);
ElementSymbol keyElement = new ElementSymbol(keyElementName, new GroupSymbol(matTableName));
ElementSymbol returnElement = new ElementSymbol(returnElementName, new GroupSymbol(matTableName));
keyElement.setType(DataTypeManager.getDataTypeClass(metadata.getElementRuntimeTypeName(metadata.getElementID(codeTableName + ElementSymbol.SEPARATOR + keyElementName))));
returnElement.setType(DataTypeManager.getDataTypeClass(metadata.getElementRuntimeTypeName(metadata.getElementID(codeTableName + ElementSymbol.SEPARATOR + returnElementName))));
Query query = RelationalPlanner.createMatViewQuery(id, matTableName, Arrays.asList(returnElement), true);
query.setCriteria(new CompareCriteria(keyElement, CompareCriteria.EQ, new Constant(keyValue)));
ts = registerQuery(context, context.getTempTableStore(), query);
}
try {
List<?> row = ts.nextTuple();
Object result = null;
if (row != null) {
result = row.get(0);
}
ts.closeSource();
return result;
} catch (BlockedException e) {
context.putCodeLookup(matTableName, keyValue, ts);
throw e;
}
}
@Override
public EventDistributor getEventDistributor() {
return this.eventDistributor;
}
/**
* Create an unauthenticated session
* @param userName
* @param app
* @param vdb
* @return
*/
public static SessionMetadata createTemporarySession(String userName, String app, VDBMetaData vdb) {
long creationTime = System.currentTimeMillis();
SessionMetadata newSession = new SessionMetadata();
newSession.setSessionToken(new SessionToken(userName));
newSession.setSessionId(newSession.getSessionToken().getSessionID());
newSession.setUserName(userName);
newSession.setCreatedTime(creationTime);
newSession.setApplicationName(app);
newSession.setVDBName(vdb.getName());
newSession.setVDBVersion(vdb.getVersion());
newSession.setVdb(vdb);
newSession.setEmbedded(true);
return newSession;
}
private static void rethrow(Throwable e)
throws TeiidComponentException,
TeiidProcessingException {
if (e instanceof TeiidComponentException) {
throw (TeiidComponentException)e;
}
if (e instanceof TeiidProcessingException) {
throw (TeiidProcessingException)e;
}
if (e instanceof RejectedExecutionException) {
throw new TeiidComponentException(e);
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
throw new TeiidRuntimeException(e);
}
public static boolean shouldInvalidate(VDBMetaData vdb) {
boolean invalidate = true;
if (vdb != null) {
String val = vdb.getPropertyValue("lazy-invalidate"); //$NON-NLS-1$
if (val != null) {
invalidate = !Boolean.valueOf(val);
}
}
return invalidate;
}
}