/* * 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.dqp.internal.process; import java.util.Collection; import java.util.List; import org.teiid.common.buffer.BlockedException; import org.teiid.common.buffer.TupleBuffer; import org.teiid.common.buffer.TupleSource; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidException; import org.teiid.core.TeiidProcessingException; import org.teiid.dqp.internal.process.SessionAwareCache.CacheID; import org.teiid.logging.LogConstants; import org.teiid.logging.LogManager; import org.teiid.metadata.FunctionMethod.Determinism; import org.teiid.query.processor.RegisterRequestParameter; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.translator.CacheDirective; import org.teiid.translator.CacheDirective.Scope; /** * A proxy {@link TupleSource} that caches a {@link DataTierTupleSource} */ final class CachingTupleSource extends TupleSourceCache.CopyOnReadTupleSource { private final DataTierManagerImpl dataTierManagerImpl; private final CacheID cid; private final RegisterRequestParameter parameterObject; private final CacheDirective cd; private final Collection<GroupSymbol> accessedGroups; final DataTierTupleSource dtts; final RequestWorkItem item; CachingTupleSource(DataTierManagerImpl dataTierManagerImpl, TupleBuffer tb, DataTierTupleSource ts, CacheID cid, RegisterRequestParameter parameterObject, CacheDirective cd, Collection<GroupSymbol> accessedGroups, RequestWorkItem item) { super(tb, ts); this.dataTierManagerImpl = dataTierManagerImpl; this.dtts = ts; this.cid = cid; this.parameterObject = parameterObject; this.cd = cd; this.accessedGroups = accessedGroups; this.item = item; } @Override public List<?> nextTuple() throws TeiidComponentException, TeiidProcessingException { if (dtts.scope == Scope.NONE || tb == null) { removeTupleBuffer(); return ts.nextTuple(); } //TODO: the cache directive object needs synchronized for consistency List<?> tuple = super.nextTuple(); if (tuple == null && !dtts.errored) { synchronized (cd) { if (dtts.scope == Scope.NONE) { removeTupleBuffer(); return tuple; } CachedResults cr = new CachedResults(); cr.setResults(tb, null); if (!Boolean.FALSE.equals(cd.getUpdatable())) { if (accessedGroups != null) { for (GroupSymbol gs : accessedGroups) { cr.getAccessInfo().addAccessedObject(gs.getMetadataID()); } } } else { cr.getAccessInfo().setSensitiveToMetadataChanges(false); } if (parameterObject.limit > 0 && parameterObject.limit == rowNumber) { cr.setRowLimit(rowNumber); } tb.setPrefersMemory(Boolean.TRUE.equals(cd.getPrefersMemory())); Determinism determinismLevel = getDeterminismLevel(this.dtts.scope); this.dataTierManagerImpl.requestMgr.getRsCache().put(cid, determinismLevel, cr, cd.getTtl()); tb = null; } } return tuple; } public static Determinism getDeterminismLevel(CacheDirective.Scope scope) { Determinism determinismLevel = Determinism.SESSION_DETERMINISTIC; if (scope != null) { switch (scope) { case VDB: determinismLevel = Determinism.VDB_DETERMINISTIC; break; case SESSION: determinismLevel = Determinism.SESSION_DETERMINISTIC; break; case USER: determinismLevel = Determinism.USER_DETERMINISTIC; break; } } return determinismLevel; } private void removeTupleBuffer() { if (tb != null) { tb.remove(); tb = null; } } @Override public void closeSource() { try { if (tb != null && !dtts.errored) { boolean readAll = true; synchronized (cd) { readAll = !Boolean.FALSE.equals(cd.getReadAll()); } if (readAll) { //TODO that this is blocking, so it could be made faster in non-transactional scenarios //we should also shut off any warnings, since the plan isn't consuming these tuples //the approach would probably be to do more read-ahead dtts.getAtomicRequestMessage().setSerial(true); while (dtts.scope != Scope.NONE) { if (item.isCanceled()) { LogManager.logDetail(LogConstants.CTX_DQP, dtts.getAtomicRequestMessage().getAtomicRequestID(), "Not using full results due to cancellation."); //$NON-NLS-1$ break; } try { List<?> tuple = nextTuple(); if (tuple == null) { break; } } catch (BlockedException e) { //this is possible if were were already waiting for an asynch result try { Thread.sleep(50); //TODO: we could synch/notify in the DataTierTupleSource } catch (InterruptedException e1) { break; } } catch (TeiidException e) { LogManager.logDetail(LogConstants.CTX_DQP, e, dtts.getAtomicRequestMessage().getAtomicRequestID(), "Not using full results due to error."); //$NON-NLS-1$ break; } } } } } finally { removeTupleBuffer(); ts.closeSource(); } } }