package com.tesora.dve.sql.transform;
/*
* #%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 java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.tesora.dve.sql.schema.ConnectionValues;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.statement.Statement;
import com.tesora.dve.sql.statement.dml.SelectStatement;
import com.tesora.dve.sql.transform.UniqueKeyCollector;
import com.tesora.dve.sql.transform.KeyCollector.AndedParts;
import com.tesora.dve.sql.transform.KeyCollector.EqualityPart;
import com.tesora.dve.sql.transform.KeyCollector.OredParts;
import com.tesora.dve.sql.transform.KeyCollector.Part;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.TestName;
public class UniqueKeyCollectorTest extends TransformTest {
public UniqueKeyCollectorTest() {
super("UniqueKeyCollectorTest");
}
private static final String[] schema = new String[] {
"create table SA (`id` integer not null, `blight` int, primary key (`id`))",
"create table CA (`fid` integer not null, `sid` integer not null, `blight` int, primary key(`fid`,`sid`))"
};
private void collectKeyPart(ConnectionValues cv, Map<String, Object> into, EqualityPart ep) {
into.put(ep.getColumn().getPEColumn().get().getName().get(), ep.getLiteral().getValue(cv));
}
private Map<String, Object> buildFakeKey(ConnectionValues cv, Part p) throws Throwable {
HashMap<String, Object> out = new HashMap<String,Object>();
if (p instanceof OredParts)
throw new Throwable("Cannot build fake key off of ored part");
if (p instanceof EqualityPart) {
collectKeyPart(cv,out, (EqualityPart)p);
} else if (p instanceof AndedParts) {
AndedParts ap = (AndedParts) p;
for(Part sp : ap.getParts()) {
collectKeyPart(cv,out, (EqualityPart)sp);
}
}
return out;
}
@SuppressWarnings("unused")
private void test(TestName tn,String q) throws Throwable {
test(tn,q,null,(Map[])null);
}
@SuppressWarnings("rawtypes")
private void test(TestName tn, String q, String o, Map ...keys) throws Throwable { // NOPMD by doug on 04/12/12 1:53 PM
test(tn,q,o,Collections.emptyList(),keys);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void test(TestName tn, String q, String o, List<Object> params, Map ...keys) throws Throwable {
SchemaContext db = buildSchema(tn,schema);
List<Statement> stmts = parse(db, q, params);
SelectStatement ss = (SelectStatement) stmts.get(0);
UniqueKeyCollector ukc = new UniqueKeyCollector(db,ss.getWhereClause());
ListSet<Part> pahts = ukc.getParts();
if (keys == null) {
System.out.println(q);
if (pahts == null) System.out.println("No parts.");
else {
for(Part p : pahts) {
System.out.println(p.getParent());
}
}
} else if (keys.length == 0) {
// no keys, make sure we don't get any
assertNull(pahts);
} else {
HashSet<Map<String,Object>> given = new HashSet<Map<String,Object>>();
for(Map hm : keys)
given.add(hm);
if (keys.length > 1) {
assertTrue(pahts.get(0) instanceof OredParts);
for(Part p : pahts.get(0).getParts()) {
Map<String,Object> hm = buildFakeKey(db.getValues(),p);
assertTrue(given.contains(hm));
}
} else {
assertEquals(pahts.size(), keys.length);
for(Part p : pahts) {
Map<String,Object> hm = buildFakeKey(db.getValues(),p);
assertTrue("should have key " + hm, given.contains(hm));
}
}
}
if (o != null) {
assertEquals(o.trim(),ss.getSQL(db, false, true).trim());
} else
System.out.println(ss.getSQL(db));
}
@Test
public void testSimple() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id = 1",
"SELECT * FROM `SA` AS sa WHERE sa.`id` = 1",
buildFakeKey(new Object[] { "id", new Long(1) }));
}
@Test
public void testSimpleP() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id = ?",
"SELECT * FROM `SA` AS sa WHERE sa.`id` = ?",
Collections.singletonList((Object)new Long(1)),
buildFakeKey(new Object[] { "id", new Long(1) }));
}
@Test
public void testManySimpleA() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id = 1 or sa.id = 2 or sa.id = 3",
"SELECT * FROM `SA` AS sa WHERE (sa.`id` = 1 or sa.`id` = 2 or sa.`id` = 3)",
buildFakeKey(new Object[] { "id", new Long(1)}),
buildFakeKey(new Object[] { "id", new Long(2)}),
buildFakeKey(new Object[] { "id", new Long(3)}));
}
@Test
public void testManySimpleB() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id in (1,2,3)",
"SELECT * FROM `SA` AS sa WHERE (sa.`id` = 1 OR sa.`id` = 2 OR sa.`id` = 3)",
buildFakeKey(new Object[] { "id", new Long(1)}),
buildFakeKey(new Object[] { "id", new Long(2)}),
buildFakeKey(new Object[] { "id", new Long(3)}));
}
@Test
public void testSinglePerverseC() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id in (1)",
"SELECT * FROM `SA` AS sa WHERE sa.`id` = 1",
buildFakeKey(new Object[] { "id", new Long(1)}));
}
@Test
public void testComplex() throws Throwable {
test(TestName.MULTI,"select * from CA ca where ca.fid = 1 and ca.sid = 1",
"SELECT * FROM `CA` AS ca WHERE (ca.`fid` = 1 AND ca.`sid` = 1)",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(1)}));
}
@Test
public void testComplexA() throws Throwable {
test(TestName.MULTI,"select * from CA ca where (ca.fid = 1 and ca.sid = 1) or (ca.fid = 2 and ca.sid = 2)",
"SELECT * FROM `CA` AS ca WHERE ( (ca.`fid` = 1 AND ca.`sid` = 1) or (ca.`fid` = 2 AND ca.`sid` = 2) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(1)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2)}));
}
@Test
public void testComplexB() throws Throwable {
test(TestName.MULTI,"select * from CA ca where ca.fid in (1,2,3) and ca.sid = 2",
"SELECT * FROM `CA` AS ca WHERE ( (ca.`fid` = 1 AND ca.`sid` = 2) OR (ca.`fid` = 2 AND ca.`sid` = 2) OR (ca.`fid` = 3 AND ca.`sid` = 2) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(2) }),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2) }),
buildFakeKey(new Object[] { "fid", new Long(3), "sid", new Long(2) }));
}
@Test
public void testComplexC() throws Throwable {
test(TestName.MULTI,"select * from CA ca where ca.fid in (1,2) and ca.sid in (2,4,6)",
"SELECT * FROM `CA` AS ca WHERE ( (ca.`fid` = 1 AND ca.`sid` = 2) OR (ca.`fid` = 1 AND ca.`sid` = 4) OR (ca.`fid` = 1 AND ca.`sid` = 6) OR (ca.`fid` = 2 AND ca.`sid` = 2) OR (ca.`fid` = 2 AND ca.`sid` = 4) OR (ca.`fid` = 2 AND ca.`sid` = 6) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(2) }),
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(4) }),
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(6) }),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2) }),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(4) }),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(6) }));
}
@Test
public void testNegativeA() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id in (1,2,3) or sa.blight = 15",
"SELECT * FROM `SA` AS sa WHERE (sa.`id` = 1 OR sa.`id` = 2 OR sa.`id` = 3) or sa.`blight` = 15",
new Map[] {});
}
@Test
public void testNegativeB() throws Throwable {
test(TestName.MULTI,"select * from SA sa where sa.id = 1 or (sa.id = 2 and (sa.blight = 4 or sa.blight = 6))",
"SELECT * FROM `SA` AS sa WHERE sa.`id` = 1 or (sa.`id` = 2 and (sa.`blight` = 4 or sa.`blight` = 6) )",
new Map[] {});
}
@Test
public void testSimpleMT() throws Throwable {
test(TestName.MULTIMT,"select * from SA sa where sa.id = 1",
"SELECT * FROM `SA` AS sa WHERE (sa.id = 1 AND sa.`___mtid` = 42)",
buildFakeKey(new Object[] { "id", new Long(1), "___mtid", new Long(42) }));
}
@Test
public void testManySimpleAMT() throws Throwable {
test(TestName.MULTIMT, "select * from SA sa where sa.id = 1 or sa.id = 2 or sa.id = 3",
"SELECT * FROM `SA` AS sa WHERE ( (sa.id = 1 AND sa.`___mtid` = 42) or (sa.id = 2 AND sa.`___mtid` = 42) or (sa.id = 3 AND sa.`___mtid` = 42) )",
buildFakeKey(new Object[] { "id", new Long(1), "___mtid", new Long(42)}),
buildFakeKey(new Object[] { "id", new Long(2), "___mtid", new Long(42)}),
buildFakeKey(new Object[] { "id", new Long(3), "___mtid", new Long(42)}));
}
@Test
public void testComplexMT() throws Throwable {
test(TestName.MULTIMT,"select * from CA ca where ca.fid = 1 and ca.sid = 1",
"SELECT * FROM `CA` AS ca WHERE (ca.fid = 1 AND ca.sid = 1 AND ca.`___mtid` = 42)",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(1), "___mtid", new Long(42)}));
}
@Test
public void testComplexAMT() throws Throwable {
test(TestName.MULTIMT,"select * from CA ca where (ca.fid = 1 and ca.sid = 1) or (ca.fid = 2 and ca.sid = 2)",
"SELECT * FROM `CA` AS ca WHERE ( (ca.fid = 1 AND ca.sid = 1 AND ca.`___mtid` = 42) or (ca.fid = 2 AND ca.sid = 2 AND ca.`___mtid` = 42) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(1), "___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2), "___mtid", new Long(42)}));
}
@Test
public void testComplexBMT() throws Throwable {
test(TestName.MULTIMT,"select * from CA ca where ca.fid in (1,2,3) and ca.sid = 2",
"SELECT * FROM `CA` AS ca WHERE ( (ca.fid = 1 AND ca.sid = 2 AND ca.`___mtid` = 42) OR (ca.fid = 2 AND ca.sid = 2 AND ca.`___mtid` = 42) OR (ca.fid = 3 AND ca.sid = 2 AND ca.`___mtid` = 42) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(2) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(3), "sid", new Long(2) ,"___mtid", new Long(42)}));
}
@Test
public void testComplexCMT() throws Throwable {
test(TestName.MULTIMT,"select * from CA ca where ca.fid in (1,2) and ca.sid in (2,4,6)",
"SELECT * FROM `CA` AS ca WHERE ( (ca.fid = 1 AND ca.sid = 2 AND ca.`___mtid` = 42) OR (ca.fid = 1 AND ca.sid = 4 AND ca.`___mtid` = 42) OR (ca.fid = 1 AND ca.sid = 6 AND ca.`___mtid` = 42) OR (ca.fid = 2 AND ca.sid = 2 AND ca.`___mtid` = 42) OR (ca.fid = 2 AND ca.sid = 4 AND ca.`___mtid` = 42) OR (ca.fid = 2 AND ca.sid = 6 AND ca.`___mtid` = 42) )",
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(2) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(4) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(1), "sid", new Long(6) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(2) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(4) ,"___mtid", new Long(42)}),
buildFakeKey(new Object[] { "fid", new Long(2), "sid", new Long(6) ,"___mtid", new Long(42)}));
}
}