/*
* Copyright 2009-2016 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZooDB. If not, see <http://www.gnu.org/licenses/>.
*
* See the README and COPYING files for further information.
*/
package org.zoodb.test.jdo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Collection;
import javax.jdo.JDOHelper;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.zoodb.test.testutil.TestTools;
/**
* Tests for query paths.
*
* @author ztilmann
*
*/
public class Test_128_QueryPath {
private Object oid1;
private Object oid2;
private Object oid3;
private Object oid4;
private Object oid5;
@BeforeClass
public static void setUp() {
TestTools.removeDb();
TestTools.createDb();
TestTools.defineSchema(TestClass.class);
TestTools.defineSchema(TestQueryClass.class);
}
@Before
public void before() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
pm.newQuery(TestClass.class).deletePersistentAll();
TestClass t1 = new TestClass();
t1.setData(1, false, 'c', (byte)127, (short)32001, 1234567890L, "xyz5", new byte[]{1,2},
-1.1f, 35);
pm.makePersistent(t1);
TestClass t2 = new TestClass();
t2.setData(12, false, 'd', (byte)126, (short)32002, 1234567890L, "xyz4", new byte[]{1,2},
-0.1f, 34);
pm.makePersistent(t2);
TestClass t3 = new TestClass();
t3.setData(123, false, 'x', (byte)125, (short)32003, 1234567891L, "xyz1", new byte[]{1,2},
0.1f, 3.0);
pm.makePersistent(t3);
TestClass t4 = new TestClass();
t4.setData(1234, false, 'f', (byte)124, (short)32004, 1234567890L, "xyz2", new byte[]{1,2},
1.1f, -0.01);
pm.makePersistent(t4);
TestClass t5 = new TestClass();
t5.setData(12345, false, 'g', (byte)123, (short)32005, 1234567890L, "xyz3", new byte[]{1,2},
11.1f, -35);
pm.makePersistent(t5);
//large loop
t1.setRef2(t2);
t2.setRef2(t3);
t3.setRef2(t1);
//null
t4.setRef2(null);
//small loop
t5.setRef2(t5);
oid1 = pm.getObjectId(t1);
oid2 = pm.getObjectId(t2);
oid3 = pm.getObjectId(t3);
oid4 = pm.getObjectId(t4);
oid5 = pm.getObjectId(t5);
pm.currentTransaction().commit();
TestTools.closePM();
}
@After
public void afterTest() {
TestTools.closePM();
}
@AfterClass
public static void tearDown() {
TestTools.removeDb();
}
@Test
public void testPathFunctionFail() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
checkSetFilterFails(pm, "_ref2");
checkSetFilterFails(pm, "_ref2");
checkSetFilterFails(pm, "_ref2 == 3");
checkSetFilterFails(pm, "_ref2 = 3");
checkSetFilterFails(pm, "_ref2 == 'null'");
checkSetFilterFails(pm, "_ref2 > _ref1");
checkSetFilterFails(pm, "_ref2.");
checkSetFilterFails(pm, "_ref2. == 3");
checkSetFilterFails(pm, "_ref2. = 3");
checkSetFilterFails(pm, "_ref2. == 'null'");
checkSetFilterFails(pm, "_ref2. > _ref1");
checkSetFilterFails(pm, "_ref2._ref2");
checkSetFilterFails(pm, "_ref2._ref2 == 3");
checkSetFilterFails(pm, "_ref2._ref2 = 3");
checkSetFilterFails(pm, "_ref2._ref2 == 'null'");
checkSetFilterFails(pm, "_ref2._ref2 > _ref1");
checkSetFilterFails(pm, "this.this._ref._int > 1");
checkSetFilterFails(pm, "this.this._int > 1");
//no ref
checkSetFilterFails(pm, "this.contains(1)");
checkSetFilterFails(pm, "contains(1)");
//no 'map' is only defined in sub-class
checkSetFilterFails(pm, "_ref2.map.contains(123)");
TestTools.closePM();
}
private void checkSetFilterFails(PersistenceManager pm, String s) {
Query q1 = pm.newQuery(TestClass.class);
try {
q1.setFilter(s);
q1.compile();
fail();
} catch (JDOUserException e) {
//good, we got an JDOUSerException()
}
try {
Query q2 = pm.newQuery(TestClass.class, s);
q2.compile();
fail();
} catch (JDOUserException e) {
//good, we got an JDOUSerException()
}
}
@Test
public void testRefSingleString() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("_ref2._string.matches('xyz1')");
checkOid(q, oid2);
q.setFilter("this._ref2._string.matches('xyz')");
checkOid(q);
q.setFilter("this._ref2._string.matches('.*3.*')");
checkOid(q, oid5);
q.setFilter("_ref2._string.matches('.*y.*')");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._string.startsWith('xyz1')");
checkOid(q, oid2);
q.setFilter("_ref2._string.startsWith('xyz')");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._string.startsWith('xyz12')");
checkOid(q);
q.setFilter("_ref2._string.endsWith('xyz1')");
checkOid(q, oid2);
q.setFilter("_ref2._string.endsWith('yz1')");
checkOid(q, oid2);
q.setFilter("_ref2._string.endsWith('xyz12')");
checkOid(q);
pm.currentTransaction().commit();
TestTools.closePM();
}
@Test
public void testRefDoubleString() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("_ref2._ref2._string.matches('xyz1')");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._string.matches('xyz')");
checkOid(q);
q.setFilter("_ref2._ref2._string.matches('.*3.*')");
checkOid(q, oid5);
q.setFilter("_ref2._ref2._string.matches('.*y.*')");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._ref2._string.startsWith('xyz1')");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._string.startsWith('xyz')");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._ref2._string.startsWith('xyz12')");
checkOid(q);
q.setFilter("_ref2._ref2._string.endsWith('xyz1')");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._string.endsWith('yz1')");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._string.endsWith('xyz12')");
checkOid(q);
TestTools.closePM();
}
@Test
public void testLoops() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("_ref2._int == 12345");
checkOid(q, oid5);
q.setFilter("_ref2._ref2._int == 12345");
checkOid(q, oid5);
q.setFilter("_ref2._ref2._ref2._int == 12345");
checkOid(q, oid5);
q.setFilter("_ref2._int == 1");
checkOid(q, oid3);
q.setFilter("_ref2._ref2._int == 1");
checkOid(q, oid2);
q.setFilter("_ref2._ref2._ref2._int == 1");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._ref2._ref2._int == 1");
checkOid(q, oid3);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._int == 1");
checkOid(q, oid2);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._int == 1");
checkOid(q, oid1);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2._int == 1");
checkOid(q, oid3);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2._int < 1");
checkOid(q);
q.setFilter("_ref2._int == _ref2._int");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._int != _ref2._int");
checkOid(q);
TestTools.closePM();
}
@Test
public void testNull() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2 != null");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2 == null");
checkOid(q);
q.setFilter("_ref2 != null");
checkOid(q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2 == null");
checkOid(q, oid4);
q.setFilter("_ref2 != :oid");
checkOidWithParam(null, q, oid1, oid2, oid3, oid5);
q.setFilter("_ref2 == :oid");
checkOidWithParam(null, q, oid4);
TestTools.closePM();
}
@Test
public void testRefComparison() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2 == :obj");
checkOidWithParam(oid1, q, oid3);
q.setFilter("_ref2._ref2._ref2._ref2._ref2._ref2._ref2 != :obj");
checkOidWithParam(oid1, q, oid1, oid2, oid5);
q.setFilter("_ref2 == :obj");
checkOidWithParam(oid1, q, oid3);
q.setFilter("_ref2 != :obj");
checkOidWithParam(oid1, q, oid1, oid2, oid4, oid5);
TestTools.closePM();
}
private void checkOid(Query q, Object ... matches) {
Collection<?> c = (Collection<?>) q.execute();
for (int i = 0; i < matches.length; i++) {
boolean match = false;
for (Object o: c) {
if (JDOHelper.getObjectId(o).equals(matches[i])) {
match = true;
break;
}
}
assertTrue("p=" + i, match);
}
assertEquals(matches.length, c.size());
}
private void checkOidWithParam(Object param1, Query q, Object ... matches) {
Object o1 = param1 == null ? null : q.getPersistenceManager().getObjectById(param1);
Collection<?> c = (Collection<?>) q.execute(o1);
for (int i = 0; i < matches.length; i++) {
boolean match = false;
for (Object o: c) {
if (JDOHelper.getObjectId(o).equals(matches[i])) {
match = true;
break;
}
}
assertTrue("p=" + i, match);
}
assertEquals(matches.length, c.size());
}
@Test
public void testList() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.isEmpty()");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains(1234)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains(1234L)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains(123)");
checkOid(q);
}
@Test
public void testListTC() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listTC.isEmpty()");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listTC.contains(:o1)");
checkOidWithParam(oids[1], q, oids[1]);
//use OID as parameter, TODO doesn't work yet
//q = pm.newQuery(TestQueryClass.class);
//q.setFilter("listTC.contains(:oid1)");
//checkString(q, oid1, "1111");
}
@Test
public void testMap() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.coll.isEmpty()");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.coll.contains('coll')");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.coll.contains(null)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains('123')");
checkOid(q);
TestTools.closePM();
}
@Test
public void testCollections() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.isEmpty()");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.isEmpty() == true");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsKey('key')");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.set.contains(12)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.set.contains(true)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsKey(null)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsKey('123')");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("this.map.containsValue(ref)");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("this.map.containsValue(_ref2)");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue(ref.ref)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue(_ref2._ref2)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue(ref)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue(_ref2)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue(null)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.map.containsValue('123')");
checkOid(q);
TestTools.closePM();
}
@Test
public void testThis() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClass.class);
q.setFilter("this == this");
checkOid(q, oid1, oid2, oid3, oid4, oid5);
q = pm.newQuery(TestClass.class);
q.setFilter("this._ref2._ref2 == this._ref2._ref2");
checkOid(q, oid1, oid2, oid3, oid5);
q = pm.newQuery(TestClass.class);
q.setFilter("this != this");
checkOid(q);
q = pm.newQuery(TestClass.class);
q.setFilter("this == _ref2");
checkOid(q, oid5);
q = pm.newQuery(TestClass.class);
q.setFilter("this == this._ref2");
checkOid(q, oid5);
TestTools.closePM();
}
@Test
public void testRhsRefs() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("listTC.contains(_ref2)");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("map.containsKey(_ref2)");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("map.containsValue(_ref2)");
checkOid(q, oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("_ref2._ref2 == this");
checkOid(q, oids[1], oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listTC.contains(this)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listTC.contains(_ref2._ref2)");
checkOid(q, oids[1]);
System.err.println("TODO We need a test that check object equality versus identity");
//Create two object whose equal() function returns true, but identity is different.
//This should also speed up queries, because we only need to compare OIDs instead of
//materialising the object and calling equals().
TestTools.closePM();
}
private Object[] populateTQC() {
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
//nulls
TestQueryClass tN = new TestQueryClass();
tN.setString("NULL");
pm.makePersistent(tN);
Object oid0 = JDOHelper.getObjectId(tN);
//empty list
TestQueryClass t1 = new TestQueryClass();
t1.init();
t1.setString("0000");
pm.makePersistent(t1);
Object oid1 = JDOHelper.getObjectId(t1);
//list
TestQueryClass t2 = new TestQueryClass();
t2.init();
t2.setString("1111");
t2.addInt(123);
t2.addObj(new Integer(1234));
t2.addTC(t1);
t2.addToMap("key", t1);
t2.addToSet("123");
t2.addToSet(12);
t2.addToColl("coll");
t2.setRef2(t1);
pm.makePersistent(t2);
Object oid2 = JDOHelper.getObjectId(t2);
t1.setRef2(t2);
t2.setRef2(t1);
t1.setRef(t2);
t2.setRef(t1);
pm.currentTransaction().commit();
TestTools.closePM();
return new Object[]{oid0, oid1, oid2};
}
@Test
public void testCrossClassPath() {
TestTools.defineSchema(TestClassSmall.class, TestClassSmallA.class, TestClassSmallB.class);
PersistenceManager pm1 = TestTools.openPM();
pm1.currentTransaction().begin();
TestClassSmallA a1 = new TestClassSmallA();
TestClassSmallB b1 = new TestClassSmallB();
a1.setB(b1);
b1.setA(a1);
pm1.makePersistent(a1);
pm1.makePersistent(b1);
Object oidA = pm1.getObjectId(a1);
Object oidB = pm1.getObjectId(b1);
pm1.currentTransaction().commit();
TestTools.closePM();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestClassSmallA.class);
q.setFilter("b.a.b == :oid");
checkOidWithParam(oidB, q, oidA);
q = pm.newQuery(TestClassSmallA.class);
q.setFilter("b.a.b != null");
checkOid(q, oidA);
TestTools.closePM();
}
@Test
public void testNot() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("!ref.listObj.contains(1234)");
checkOid(q, oids[0], oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("!!ref.listObj.contains(1234)");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("!(ref.listObj.contains(1234))");
checkOid(q, oids[0], oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("!!(ref.listObj.contains(1234))");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("!(!(ref.listObj.contains(1234)))");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("(!(!(ref.listObj.contains(1234))))");
checkOid(q, oids[1]);
}
@Test
public void testBraces() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains((1234))");
checkOid(q, oids[1]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("ref.listObj.contains((1234)) == true");
checkOid(q, oids[1]);
//TODO
System.err.println("TODO skipping Test_128_queryPath.testBraces()");
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("(this.ref.listObj.contains(1234L)) == (true)");
// checkOid(q);
}
@Test
public void testMath() {
Object[] oids = populateTQC();
PersistenceManager pm = TestTools.openPM();
pm.currentTransaction().begin();
Query q = null;
q = pm.newQuery(TestQueryClass.class);
q.setFilter("1 == 1");
checkOid(q, oids[0], oids[1], oids[2]);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("1 != 1");
checkOid(q);
q = pm.newQuery(TestQueryClass.class);
q.setFilter("this.ref.listObj.contains(1234L) == (1 == 1)");
checkOid(q);
//TODO
System.err.println("TODO skipping Test_128_queryPath.testMath()");
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("this.ref.listObj.contains(1234L) == (1+1-1 == 1)");
// checkOid(q);
//
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("this.ref.listObj.contains(1234L) == (1+2+3 == 7)");
// checkOid(q);
//
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("(1+2+3 == 7)");
// checkOid(q);
//
// //check operator precedence
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("this.ref.listObj.contains(1234L) == (1+2*3 == 7)");
// checkOid(q);
//
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("this.ref.listObj.contains(1234L) == (1*2+3 == 6)");
// checkOid(q);
//
// q = pm.newQuery(TestQueryClass.class);
// q.setFilter("this.ref.listObj.contains(1234L) == (1 == 1*1)");
// checkOid(q);
}
}