/* * 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.processor; import static org.junit.Assert.*; import static org.teiid.query.processor.TestProcessor.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.teiid.cache.DefaultCacheFactory; import org.teiid.dqp.internal.process.PreparedPlan; import org.teiid.dqp.internal.process.SessionAwareCache; import org.teiid.query.metadata.TransformationMetadata; import org.teiid.query.optimizer.TestOptimizer; import org.teiid.query.optimizer.capabilities.BasicSourceCapabilities; import org.teiid.query.optimizer.capabilities.DefaultCapabilitiesFinder; import org.teiid.query.rewriter.TestQueryRewriter; 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.query.validator.TestUpdateValidator; import org.teiid.translator.SourceSystemFunctions; @SuppressWarnings("nls") public class TestInherintlyUpdatableViews { @Test public void testUpdatePassThrough() throws Exception { String userSql = "update vm1.gx set e1 = e2"; //$NON-NLS-1$ String viewSql = "select * from pm1.g1 where e3 < 5"; String expectedSql = "UPDATE pm1.g1 SET e1 = convert(pm1.g1.e2, string) WHERE convert(e3, integer) < 5"; helpTest(userSql, viewSql, expectedSql, null); } private Command helpTest(String userSql, String viewSql, String expectedSql, ProcessorDataManager dm) throws Exception { TransformationMetadata metadata = TestUpdateValidator.example1(); TestUpdateValidator.createView(viewSql, metadata, "gx"); Command command = TestQueryRewriter.helpTestRewriteCommand(userSql, expectedSql, metadata); if (dm != null) { CommandContext context = createCommandContext(); SessionAwareCache<PreparedPlan> planCache = new SessionAwareCache<PreparedPlan>("preparedplan", DefaultCacheFactory.INSTANCE, SessionAwareCache.Type.PREPAREDPLAN, 0); context.setPreparedPlanCache(planCache); //$NON-NLS-1$ BasicSourceCapabilities caps = TestOptimizer.getTypicalCapabilities(); caps.setFunctionSupport(SourceSystemFunctions.CONVERT, true); ProcessorPlan plan = helpGetPlan(helpParse(userSql), metadata, new DefaultCapabilitiesFinder(caps), context); List<?>[] expected = new List[] {Arrays.asList(1)}; helpProcess(plan, context, dm, expected); assertEquals(0, planCache.getTotalCacheEntries()); } return command; } @Test public void testUpdatePassThroughWithAlias() throws Exception { String userSql = "update vm1.gx set e1 = e2"; //$NON-NLS-1$ String viewSql = "select * from pm1.g1 as x where e3 < 5"; String expectedSql = "UPDATE pm1.g1 SET e1 = convert(pm1.g1.e2, string) WHERE convert(e3, integer) < 5"; helpTest(userSql, viewSql, expectedSql, null); } @Test public void testDeletePassThrough() throws Exception { String userSql = "delete from vm1.gx where e1 = e2"; //$NON-NLS-1$ String viewSql = "select * from pm1.g1 where e3 < 5"; String expectedSql = "DELETE FROM pm1.g1 WHERE (pm1.g1.e1 = convert(pm1.g1.e2, string)) AND (convert(e3, integer) < 5)"; helpTest(userSql, viewSql, expectedSql, null); } @Test public void testInsertPassThrough() throws Exception { String userSql = "insert into vm1.gx (e1) values (1)"; //$NON-NLS-1$ String viewSql = "select * from pm1.g1 where e3 < 5"; String expectedSql = "INSERT INTO pm1.g1 (e1) VALUES ('1')"; helpTest(userSql, viewSql, expectedSql, null); } @Test public void testDeleteUnion() throws Exception { String userSql = "delete from vm1.gx where e4 is null"; //$NON-NLS-1$ String viewSql = "select * from pm1.g1 where e3 < 5 union all select * from pm1.g2 where e1 > 1"; String expectedSql = "BatchedUpdate{D,D}"; BatchedUpdateCommand buc = (BatchedUpdateCommand)helpTest(userSql, viewSql, expectedSql, null); assertEquals("DELETE FROM pm1.g2 WHERE (pm1.g2.e4 IS NULL) AND (e1 > '1')", buc.getUpdateCommands().get(1).toString()); } /** * Here we should be able to figure out that we can pass through the join * @throws Exception */ @Test public void testInsertPassThrough1() throws Exception { String userSql = "insert into vm1.gx (e1) values (1)"; //$NON-NLS-1$ String viewSql = "select g2.* from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1"; String expectedSql = "INSERT INTO pm1.g2 (e1) VALUES ('1')"; helpTest(userSql, viewSql, expectedSql, null); } @Test public void testUpdateComplex() throws Exception { String userSql = "update vm1.gx set e1 = e2 where e3 is null"; //$NON-NLS-1$ String viewSql = "select g2.e1, g1.e2, g2.e3, g2.e4 from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1"; HardcodedDataManager dm = new HardcodedDataManager(); dm.addData("SELECT convert(g_0.e2, string), g_1.e2 FROM pm1.g1 AS g_0, pm1.g2 AS g_1 WHERE (g_0.e1 = g_1.e1) AND (g_1.e3 IS NULL)", new List[] {Arrays.asList("1", 1)}); dm.addData("UPDATE pm1.g2 SET e1 = '1' WHERE pm1.g2.e2 = 1", new List[] {Arrays.asList(1)}); helpTest(userSql, viewSql, "BEGIN ATOMIC\nDECLARE integer VARIABLES.ROWS_UPDATED = 0;\nLOOP ON (SELECT convert(g1.e2, string) AS s_0, pm1.g2.e2 AS s_1 FROM pm1.g1 INNER JOIN pm1.g2 ON g1.e1 = g2.e1 WHERE g2.e3 IS NULL) AS X\nBEGIN\nUPDATE pm1.g2 SET e1 = X.s_0 WHERE pm1.g2.e2 = X.s_1;\nVARIABLES.ROWS_UPDATED = (VARIABLES.ROWS_UPDATED + 1);\nEND\nSELECT VARIABLES.ROWS_UPDATED;\nEND", dm); } @Test public void testDeleteComplex() throws Exception { String userSql = "delete from vm1.gx where e2 < 10"; //$NON-NLS-1$ String viewSql = "select g2.e1, g1.e2, g2.e3, g2.e4 from pm1.g1 inner join pm1.g2 on g1.e1 = g2.e1"; HardcodedDataManager dm = new HardcodedDataManager(); dm.addData("SELECT g_1.e2 FROM pm1.g1 AS g_0, pm1.g2 AS g_1 WHERE (g_0.e1 = g_1.e1) AND (g_0.e2 < 10)", new List[] {Arrays.asList(2)}); dm.addData("DELETE FROM pm1.g2 WHERE pm1.g2.e2 = 2", new List[] {Arrays.asList(1)}); helpTest(userSql, viewSql, "BEGIN ATOMIC\nDECLARE integer VARIABLES.ROWS_UPDATED = 0;\nLOOP ON (SELECT pm1.g2.e2 AS s_0 FROM pm1.g1 INNER JOIN pm1.g2 ON g1.e1 = g2.e1 WHERE g1.e2 < 10) AS X\nBEGIN\nDELETE FROM pm1.g2 WHERE pm1.g2.e2 = X.s_0;\nVARIABLES.ROWS_UPDATED = (VARIABLES.ROWS_UPDATED + 1);\nEND\nSELECT VARIABLES.ROWS_UPDATED;\nEND", dm); } /** * Here we should use the partitioning * @throws Exception */ @Test public void testInsertPartitionedUnion() throws Exception { String userSql = "insert into vm1.gx (e1, e2) values (1, 2)"; //$NON-NLS-1$ String viewSql = "select 1 as e1, e2 from pm1.g1 union all select 2 as e1, e2 from pm1.g2"; String expectedSql = "INSERT INTO pm1.g1 (e2) VALUES (2)"; helpTest(userSql, viewSql, expectedSql, null); } @Test public void testWherePartitioningUpdates() throws Exception { TransformationMetadata metadata = RealMetadataFactory.fromDDL("create foreign table b (custid integer, field1 varchar) options (updatable true); " + "create view finnish_customers options (updatable true) as select custid, field1 as name from b where custid = 1; " + "create view other_customers options (updatable true) as select custid, field1 as name from b where custid = 2; " + "create view customers options (updatable true) as select * from finnish_customers where custid = 1 union all select * from other_customers where custid = 2;", "x", "y"); ProcessorPlan plan = TestProcessor.helpGetPlan("insert into customers (custid, name) values (1, 'a')", metadata); HardcodedDataManager dataManager = new HardcodedDataManager(); dataManager.addData("INSERT INTO b (custid, field1) VALUES (1, 'a')", new List<?>[] {Arrays.asList(1)}); TestProcessor.helpProcess(plan, dataManager, new List<?>[] {Arrays.asList(1)}); //ensure that update works as expected - TODO: eventually we should support a check option to not allow updates such as this plan = TestProcessor.helpGetPlan("update customers set custid = 3, name = 'a'", metadata, TestOptimizer.getGenericFinder()); dataManager = new HardcodedDataManager(); dataManager.addData("UPDATE b SET custid = 3, field1 = 'a' WHERE custid = 1", new List<?>[] {Arrays.asList(1)}); dataManager.addData("UPDATE b SET custid = 3, field1 = 'a' WHERE custid = 2", new List<?>[] {Arrays.asList(1)}); TestProcessor.helpProcess(plan, dataManager, new List<?>[] {Arrays.asList(2)}); } }