/* * 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 static org.junit.Assert.*; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.teiid.adminapi.impl.VDBMetaData; import org.teiid.cache.CacheConfiguration; import org.teiid.cache.DefaultCacheFactory; import org.teiid.client.RequestMessage; import org.teiid.client.SourceWarning; import org.teiid.common.buffer.BlockedException; import org.teiid.common.buffer.TupleSource; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository; import org.teiid.dqp.internal.datamgr.FakeTransactionService; import org.teiid.dqp.message.AtomicRequestMessage; import org.teiid.dqp.message.RequestID; import org.teiid.dqp.service.AutoGenDataService; import org.teiid.dqp.service.FakeBufferService; import org.teiid.metadata.FunctionMethod.Determinism; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.optimizer.capabilities.DefaultCapabilitiesFinder; import org.teiid.query.parser.QueryParser; import org.teiid.query.processor.RegisterRequestParameter; import org.teiid.query.resolver.QueryResolver; import org.teiid.query.sql.lang.BatchedUpdateCommand; import org.teiid.query.sql.lang.Command; import org.teiid.query.unittest.RealMetadataFactory; import org.teiid.query.util.CommandContext; import org.teiid.translator.CacheDirective; import org.teiid.translator.CacheDirective.Invalidation; import org.teiid.translator.CacheDirective.Scope; @SuppressWarnings("nls") public class TestDataTierManager { private VDBMetaData vdb = RealMetadataFactory.exampleBQTVDB(); private DQPCore rm; private DataTierManagerImpl dtm; private CommandContext context; private AutoGenDataService connectorManager = new AutoGenDataService(); private RequestWorkItem workItem; private int limit = -1; private boolean serial = false; @Before public void setUp() { limit = -1; connectorManager = new AutoGenDataService(); serial = false; } private static Command helpGetCommand(String sql, QueryMetadataInterface metadata) throws Exception { Command command = QueryParser.getQueryParser().parseCommand(sql); QueryResolver.resolveCommand(command, metadata); return command; } private DataTierTupleSource helpSetup(int nodeId) throws Exception { return helpSetup("SELECT * FROM BQT1.SmallA", nodeId); //$NON-NLS-1$ } private DataTierTupleSource helpSetup(String sql, int nodeId) throws Exception { helpSetupDataTierManager(); AtomicRequestMessage request = helpSetupRequest(sql, nodeId, RealMetadataFactory.exampleBQTCached()); request.setSerial(serial); return new DataTierTupleSource(request, workItem, connectorManager.registerRequest(request), dtm, limit); } private int id; private AtomicRequestMessage helpSetupRequest(String sql, int nodeId, QueryMetadataInterface metadata) throws Exception { DQPWorkContext workContext = RealMetadataFactory.buildWorkContext(metadata, vdb); Command command = helpGetCommand(sql, metadata); RequestMessage original = new RequestMessage(); original.setExecutionId(id++); original.setPartialResults(true); RequestID requestID = workContext.getRequestID(original.getExecutionId()); context = new CommandContext(); context.setSession(workContext.getSession()); context.setVdbName("test"); //$NON-NLS-1$ context.setVdbVersion(1); context.setQueryProcessorFactory(new QueryProcessorFactoryImpl(dtm.getBufferManager(), dtm, new DefaultCapabilitiesFinder(), null, metadata)); workItem = TestDQPCoreRequestHandling.addRequest(rm, original, requestID, null, workContext); context.setWorkItem(workItem); AtomicRequestMessage request = new AtomicRequestMessage(original, workContext, nodeId); request.setCommand(command); request.setConnectorName("FakeConnectorID"); //$NON-NLS-1$ request.setCommandContext(context); return request; } private void helpSetupDataTierManager() { FakeBufferService bs = new FakeBufferService(); rm = new DQPCore(); rm.setTransactionService(new FakeTransactionService()); rm.setBufferManager(bs.getBufferManager()); CacheConfiguration config = new CacheConfiguration(); config.setMaxAgeInSeconds(-1); rm.setResultsetCache(new SessionAwareCache<CachedResults>("resultset", new DefaultCacheFactory(config), SessionAwareCache.Type.RESULTSET, 0)); rm.setPreparedPlanCache(new SessionAwareCache<PreparedPlan>("preparedplan", new DefaultCacheFactory(config), SessionAwareCache.Type.PREPAREDPLAN, 0)); rm.start(new DQPConfiguration()); ConnectorManagerRepository repo = Mockito.mock(ConnectorManagerRepository.class); Mockito.stub(repo.getConnectorManager(Mockito.anyString())).toReturn(connectorManager); vdb.addAttchment(ConnectorManagerRepository.class, repo); dtm = new DataTierManagerImpl(rm,bs.getBufferManager(), true); } @Test public void testDataTierTupleSource() throws Exception { DataTierTupleSource info = helpSetup(1); assertEquals(10, pullTuples(info, 10)); assertNotNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); assertNull(info.nextTuple()); info.closeSource(); assertNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); } @Test public void testDataTierTupleSourceWarnings() throws Exception { DataTierTupleSource info = helpSetup(1); connectorManager.addWarning = true; assertEquals(10, pullTuples(info, 10)); assertNotNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); assertNull(info.nextTuple()); List<Exception> warnings = context.getAndClearWarnings(); assertEquals(1, warnings.size()); SourceWarning warning = (SourceWarning) warnings.get(0); assertFalse(warning.isPartialResultsError()); info.closeSource(); assertNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); } @Test public void testDataTierTupleSourceLimit() throws Exception { limit = 1; DataTierTupleSource info = helpSetup(1); assertEquals(1, pullTuples(info, 1)); assertNotNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); assertNull(info.nextTuple()); info.closeSource(); assertNull(workItem.getConnectorRequest(info.getAtomicRequestMessage().getAtomicRequestID())); } private int pullTuples(TupleSource info, int l) throws TeiidComponentException, TeiidProcessingException, InterruptedException { int i = 0; while (true) { try { if (info.nextTuple() == null) { break; } if (++i == l) { break; } } catch (BlockedException e) { Thread.sleep(50); } } return i; } @Test public void testPartialResults() throws Exception { DataTierTupleSource info = helpSetup(1); connectorManager.throwExceptionOnExecute = true; for (int i = 0; i < 10; i++) { try { assertNull(info.nextTuple()); SourceWarning warning = (SourceWarning) context.getAndClearWarnings().get(0); assertTrue(warning.isPartialResultsError()); return; } catch (BlockedException e) { Thread.sleep(50); } } fail(); } @Test public void testNoRowsException() throws Exception { this.connectorManager.setRows(0); DataTierTupleSource info = helpSetup(3); while (true) { try { assertNull(info.nextTuple()); break; } catch (BlockedException e) { Thread.sleep(50); } } } @Test public void testAsynch() throws Exception { this.connectorManager.dataNotAvailable = 10; this.serial = true; this.connectorManager.setRows(0); DataTierTupleSource info = helpSetup(3); boolean blocked = false; while (true) { try { assertNull(info.nextTuple()); break; } catch (BlockedException e) { blocked = true; try { info.nextTuple(); } catch (BlockedException be) { fail(); } Thread.sleep(50); } } assertTrue(blocked); } @Test public void testAsynchStrict() throws Exception { this.connectorManager.dataNotAvailable = 1000; this.serial = true; this.connectorManager.strict = true; this.connectorManager.setRows(0); DataTierTupleSource info = helpSetup(3); boolean blocked = false; while (true) { try { assertNull(info.nextTuple()); break; } catch (BlockedException e) { blocked = true; try { info.nextTuple(); fail(); } catch (BlockedException be) { //we won't bother to wait the full second } break; } } assertTrue(blocked); } @Test public void testCachingScope() throws Exception { assertEquals(Determinism.SESSION_DETERMINISTIC, CachingTupleSource.getDeterminismLevel(Scope.SESSION)); assertEquals(Determinism.SESSION_DETERMINISTIC, CachingTupleSource.getDeterminismLevel(Scope.NONE)); assertEquals(Determinism.USER_DETERMINISTIC, CachingTupleSource.getDeterminismLevel(Scope.USER)); assertEquals(Determinism.VDB_DETERMINISTIC, CachingTupleSource.getDeterminismLevel(Scope.VDB)); } @Test public void testCaching() throws Exception { assertEquals(0, connectorManager.getExecuteCount().get()); QueryMetadataInterface metadata = RealMetadataFactory.exampleBQTCached(); CacheDirective cd = new CacheDirective(); this.connectorManager.cacheDirective = cd; helpSetupDataTierManager(); Command command = helpSetupRequest("SELECT stringkey from bqt1.smalla", 1, metadata).getCommand(); RegisterRequestParameter rrp = new RegisterRequestParameter(); rrp.connectorBindingId = "x"; TupleSource ts = dtm.registerRequest(context, command, "foo", rrp); assertTrue(ts instanceof CachingTupleSource); assertEquals(10, pullTuples(ts, -1)); assertEquals(1, connectorManager.getExecuteCount().get()); assertFalse(rrp.doNotCache); assertFalse(((CachingTupleSource)ts).dtts.errored); assertNull(((CachingTupleSource)ts).dtts.scope); ts.closeSource(); assertEquals(1, this.rm.getRsCache().getCachePutCount()); assertEquals(1, this.rm.getRsCache().getTotalCacheEntries()); //same session, should be cached command = helpSetupRequest("SELECT stringkey from bqt1.smalla", 1, metadata).getCommand(); rrp = new RegisterRequestParameter(); rrp.connectorBindingId = "x"; ts = dtm.registerRequest(context, command, "foo", rrp); assertFalse(ts instanceof CachingTupleSource); assertEquals(10, pullTuples(ts, -1)); assertEquals(1, connectorManager.getExecuteCount().get()); assertTrue(rrp.doNotCache); //switch sessions command = helpSetupRequest("SELECT stringkey from bqt1.smalla", 1, metadata).getCommand(); this.context.getSession().setSessionId("different"); rrp = new RegisterRequestParameter(); rrp.connectorBindingId = "x"; ts = dtm.registerRequest(context, command, "foo", rrp); assertTrue(ts instanceof CachingTupleSource); assertEquals(9, pullTuples(ts, 9)); assertEquals(2, connectorManager.getExecuteCount().get()); assertFalse(rrp.doNotCache); ts.closeSource(); //should force read all assertFalse(((CachingTupleSource)ts).dtts.errored); assertNull(((CachingTupleSource)ts).dtts.scope); assertEquals(2, this.rm.getRsCache().getCachePutCount()); assertEquals(2, this.rm.getRsCache().getTotalCacheEntries()); //proactive invalidation, removes immediately command = helpSetupRequest("SELECT stringkey from bqt1.smalla", 1, metadata).getCommand(); cd.setInvalidation(Invalidation.IMMEDIATE); rrp = new RegisterRequestParameter(); rrp.connectorBindingId = "x"; ts = dtm.registerRequest(context, command, "foo", rrp); assertTrue(ts instanceof CachingTupleSource); assertEquals(10, pullTuples(ts, -1)); assertEquals(3, connectorManager.getExecuteCount().get()); assertFalse(rrp.doNotCache); } @Test public void testCancelWithCaching() throws Exception { QueryMetadataInterface metadata = RealMetadataFactory.exampleBQTCached(); CacheDirective cd = new CacheDirective(); this.connectorManager.cacheDirective = cd; helpSetupDataTierManager(); Command command = helpSetupRequest("SELECT stringkey from bqt1.smalla", 1, metadata).getCommand(); this.context.getSession().setSessionId("different"); RegisterRequestParameter rrp = new RegisterRequestParameter(); rrp.connectorBindingId = "x"; TupleSource ts = dtm.registerRequest(context, command, "foo", rrp); assertTrue(ts instanceof CachingTupleSource); assertEquals(4, pullTuples(ts, 4)); ((CachingTupleSource)ts).item.requestCancel(); assertEquals(1, connectorManager.getExecuteCount().get()); assertFalse(rrp.doNotCache); ts.closeSource(); //should force read all assertFalse(((CachingTupleSource)ts).dtts.errored); assertNull(((CachingTupleSource)ts).dtts.scope); assertEquals(0, this.rm.getRsCache().getCachePutCount()); } @Test public void testCheckForUpdatesWithBatched() throws Exception { helpSetupDataTierManager(); QueryMetadataInterface metadata = RealMetadataFactory.exampleBQTCached(); AtomicRequestMessage request = helpSetupRequest("delete from bqt1.smalla", 1, metadata); Command command = helpGetCommand("insert into bqt1.smalla (stringkey) values ('1')", metadata); BatchedUpdateCommand bac = new BatchedUpdateCommand(Arrays.asList(request.getCommand(), command)); request.setCommand(bac); DataTierTupleSource dtts = new DataTierTupleSource(request, workItem, connectorManager.registerRequest(request), dtm, limit); pullTuples(dtts, 2); } }