package com.tesora.dve.sql.schema; /* * #%L * Tesora Inc. * Database Virtualization Engine * %% * Copyright (C) 2011 - 2014 Tesora Inc. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.Iterator; import java.util.List; import java.util.Map; import com.tesora.dve.db.DBNative; import org.junit.BeforeClass; import org.junit.Test; import com.tesora.dve.common.PEConstants; import com.tesora.dve.common.catalog.CatalogDAO; import com.tesora.dve.common.catalog.CatalogDAO.CatalogDAOFactory; import com.tesora.dve.common.catalog.CatalogEntity; import com.tesora.dve.common.catalog.TemplateMode; import com.tesora.dve.common.catalog.TestCatalogHelper; import com.tesora.dve.db.NativeType; import com.tesora.dve.db.NativeTypeCatalog; import com.tesora.dve.db.mysql.MysqlNativeType; import com.tesora.dve.server.bootstrap.BootstrapHost; import com.tesora.dve.server.global.HostService; import com.tesora.dve.singleton.Singletons; import com.tesora.dve.sql.SchemaTest; import com.tesora.dve.sql.parser.InvokeParser; import com.tesora.dve.sql.schema.cache.SchemaSourceFactory; import com.tesora.dve.sql.statement.ddl.PECreateStatement; import com.tesora.dve.sql.template.TemplateBuilder; import com.tesora.dve.sql.util.Pair; import com.tesora.dve.standalone.PETest; import com.tesora.dve.variables.KnownVariables; // parse some decls, persist them to the db, and make sure we can get them back out again public class TestRoundTrip extends PETest { private static final boolean noisy = Boolean.valueOf(System.getProperty("parser.debug")).booleanValue(); public static void echo(String what) { if (noisy) System.out.println(what); } @BeforeClass public static void setup() throws Throwable { TestCatalogHelper.createTestCatalog(PETest.class,4); bootHost = BootstrapHost.startServices(PETest.class); SchemaTest.setTemplateModeOptional(); } private static SchemaContext buildContext(CatalogDAO c) throws Exception { SchemaContext pc = SchemaContext.createContext(c, Singletons.require(DBNative.class).getTypeCatalog()); pc.getConnection().getVariableSource().getGlobalVariableStore().setValue(KnownVariables.TEMPLATE_MODE, TemplateMode.OPTIONAL); return pc; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void checkLoad(CatalogDAO catalog, Persistable obj, String defaultDatabase) throws Exception { // this is all about loading, so force the global cache to be cleaned out SchemaSourceFactory.reset(); SchemaContext pc = buildContext(catalog); if (defaultDatabase != null) pc.setCurrentDatabase(pc.findDatabase(defaultDatabase)); Persistable loaded = obj.reload(pc); String diffs = obj.differs(pc,loaded, false); if (diffs != null) fail(diffs); } private void persistToCatalog(CatalogDAO catalog, SchemaContext sc, PECreateStatement<?, ?> pecs) throws Exception { List<CatalogEntity> catalogObjects = pecs.createCatalogObjects(sc); for(CatalogEntity ce : catalogObjects) { catalog.persistToCatalog(ce); } } /** * @param pc * @param catalog * @param sql * @param defaultDatabase * @return * @throws Exception */ private PECreateStatement<?,?> trip(SchemaContext pc, CatalogDAO catalog, String sql, String defaultDatabase) throws Exception { PECreateStatement<?,?> pecs = (PECreateStatement<?,?>)InvokeParser.parse(sql,pc).get(0); return pecs; } // you'll have to set the def db, project - the session context would do that for us - // i.e. this test does not handle use statements correctly @SuppressWarnings({ "rawtypes", "unchecked" }) private Pair<SchemaContext, Persistable<?,?>> roundTrip(String sql, String defaultDatabase) throws Exception { PECreateStatement firstTime = null; PECreateStatement secondTime = null; CatalogDAO catalog = CatalogDAOFactory.newInstance(); SchemaContext secondPC = null; try { catalog.begin(); SchemaContext firstPC = buildContext(catalog); if (defaultDatabase != null) firstPC.setCurrentDatabase(firstPC.findDatabase(defaultDatabase)); firstTime = trip(firstPC,catalog, sql, defaultDatabase); // don't persist yet String gen = firstTime.getSQL(firstPC,true, false); echo(gen); secondPC = buildContext(catalog); if (defaultDatabase != null) secondPC.setCurrentDatabase(secondPC.findDatabase(defaultDatabase)); secondTime = trip(secondPC, catalog, gen, defaultDatabase); // make sure the objects are the same String diffs = firstTime.getCreated().differs(firstPC,secondTime.getCreated(), false); if (diffs != null) fail(diffs); persistToCatalog(catalog, secondPC, secondTime); catalog.commit(); // now, we're going to load that persistable object and compare it to the second time catalog.begin(); checkLoad(catalog, secondTime.getCreated(), defaultDatabase); catalog.rollbackNoException(); } finally { catalog.close(); } return new Pair<SchemaContext, Persistable<?,?>>(secondPC, secondTime.getCreated()); } @Test public void simpleTest() throws Exception { roundTrip("create persistent site s1 url='jdbc:mysql://s1/db1' user='floyd' password='woof'", null); roundTrip("create persistent site s2 url='jdbc:mysql://s2/db1' user='floyd' password='woof'", null); roundTrip("create persistent group sg1 add s1, s2", null); roundTrip("create persistent site t1 url='jdbc:mysql://t1/db' user='floyd' password='woof'",null); roundTrip("create persistent site t2 url='jdbc:mysql://t1/db' user='floyd' password='woof'",null); roundTrip("create persistent group tg1 add t1, t2", null); roundTrip("create database mydb default persistent group sg1", null); // have to set up the template roundTrip(new TemplateBuilder("mytt").toCreateStatement(),null); roundTrip("create database ttdb default persistent group sg1 using template mytt strict", null); // use database mydb roundTrip("CREATE TABLE foo (`id` tinyint, `waa` char(20)) static distribute on (`id`)", "mydb"); } @Test public void createTableDVAlts() throws Exception { roundTrip("create persistent site s3 url='jdbc:mysql://s3/db1' user='floyd' password='woof'", null); roundTrip("create persistent site s4 url='jdbc:mysql://s4/db1' user='floyd' password='woof'", null); roundTrip("create persistent group sg2 add s3, s4", null); roundTrip("create database mydb2 default persistent group sg2", null); // use database mydb roundTrip("create range field_range (varchar,int) persistent group sg2", "mydb2"); roundTrip("create range openrange (int) persistent group sg2", "mydb2"); Pair<SchemaContext, Persistable<?,?>> results = roundTrip("CREATE TABLE A (`id` tinyint)", "mydb2"); PETable tab = (PETable)results.getSecond(); // the default distribution is random, make sure we set that assertEquals(tab.getDistributionVector(results.getFirst()).getModel(), DistributionVector.Model.RANDOM); roundTrip("CREATE TABLE B (`id` tinyint) broadcast distribute", "mydb2"); roundTrip("CREATE TABLE C (`id` tinyint) random distribute", "mydb2"); roundTrip("CREATE TABLE D (`id` tinyint) static distribute on (`id`)", "mydb2"); roundTrip("CREATE TABLE E (`id` int) range distribute on (`id`) using openrange", "mydb2"); roundTrip("CREATE TABLE F (`id` int) range distribute on (`id`) using openrange", "mydb2"); } @Test public void testTypes() throws Exception { roundTrip("create persistent site s5 url='jdbc:mysql://s5/db1' user='floyd' password='woof'", null); roundTrip("create persistent group sg3 add s5", null); roundTrip("create database mydb3 default persistent group sg3", null); NativeTypeCatalog tc = Singletons.require(DBNative.class).getTypeCatalog(); StringBuilder buf = new StringBuilder(); buf.append("CREATE TABLE typeRoundTrip ( ").append(PEConstants.LINE_SEPARATOR); int counter = 0; for (Iterator<Map.Entry<String, NativeType>> iter = tc.getTypeCatalogEntries().iterator(); iter .hasNext();) { Map.Entry<String, NativeType> me = iter.next(); NativeType nt = me.getValue(); if (nt.isUsedInCreate()) { if (counter > 3) continue; if (counter > 0) buf.append(", ").append(PEConstants.LINE_SEPARATOR); buf.append("`f").append(++counter).append("` ").append(nt.getTypeName()); if (nt.isUnsignedAttribute()) buf.append(", `f").append(++counter).append("` ").append(nt.getTypeName()) .append(" ").append(MysqlNativeType.MODIFIER_UNSIGNED); } } buf.append(") "); roundTrip(buf.toString(), "mydb3"); } @Test public void testKeys() throws Exception { roundTrip("create persistent site s6 url='jdbc:mysql://s3/db1' user='floyd' password='woof'", null); roundTrip("create persistent site s7 url='jdbc:mysql://s4/db1' user='floyd' password='woof'", null); roundTrip("create persistent group sg4 add s6, s7", null); roundTrip("create database mydb4 default persistent group sg4", null); roundTrip("create table keytest (`id` int not null, `fid` int not null, " + "`sid` int not null, `stuff` varchar(32), " + "primary key (`id`), key (`id`, `fid`), " + "unique key (`sid`), index (`stuff`), fulltext index (`stuff`)) engine = myisam broadcast distribute","mydb4"); roundTrip("create table fktest (`id` int not null, `fid` int, primary key (`id`), foreign key (fid) references keytest (sid) ON DELETE SET NULL ON UPDATE CASCADE) broadcast distribute","mydb4"); } @Test public void testContainers() throws Exception { roundTrip("create persistent site s8 url='jdbc:/mysql://s5/db1' user='floyd' password='woof'", null); roundTrip("create persistent site s9 url='jdbc:mysql://s4/db1' user='floyd' password='woof'", null); roundTrip("create persistent group sg5 add s8, s9", null); roundTrip("create database mydb5 default persistent group sg5", null); roundTrip("create range cont_range (int) persistent group sg5", "mydb5"); roundTrip("create container cont1 persistent group sg5 range distribute using cont_range","mydb5"); roundTrip("create table basetable (`id` int not null, primary key (`id`)) discriminate on (id) using container cont1","mydb5"); roundTrip("create table nonbasetable (`id` int not null) container distribute cont1","mydb5"); } }