001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.db4ounit.common.fieldindex;
022:
023: import com.db4o.*;
024: import com.db4o.config.*;
025: import com.db4o.db4ounit.common.btree.BTreeAssert;
026: import com.db4o.db4ounit.common.foundation.IntArrays4;
027: import com.db4o.internal.*;
028: import com.db4o.internal.btree.*;
029: import com.db4o.internal.fieldindex.*;
030: import com.db4o.internal.query.processor.*;
031: import com.db4o.query.*;
032:
033: import db4ounit.Assert;
034:
035: public class FieldIndexProcessorTestCase extends
036: FieldIndexProcessorTestCaseBase {
037:
038: public static void main(String[] args) {
039: new FieldIndexProcessorTestCase().runSolo();
040: }
041:
042: protected void configure(Configuration config) {
043: super .configure(config);
044: indexField(config, NonIndexedFieldIndexItem.class, "indexed");
045: }
046:
047: protected void store() {
048: storeItems(new int[] { 3, 4, 7, 9 });
049: storeComplexItems(new int[] { 3, 4, 7, 9 }, new int[] { 2, 2,
050: 8, 8 });
051: }
052:
053: public void testIdentity() {
054: Query query = createComplexItemQuery();
055: query.descend("foo").constrain(new Integer(3));
056: ComplexFieldIndexItem item = (ComplexFieldIndexItem) query
057: .execute().next();
058:
059: query = createComplexItemQuery();
060: query.descend("child").constrain(item).identity();
061: assertExpectedFoos(ComplexFieldIndexItem.class,
062: new int[] { 4 }, query);
063: }
064:
065: public void testSingleIndexNotSmaller() {
066: final Query query = createItemQuery();
067: query.descend("foo").constrain(new Integer(5)).smaller().not();
068: assertExpectedFoos(FieldIndexItem.class, new int[] { 7, 9 },
069: query);
070: }
071:
072: public void testSingleIndexNotGreater() {
073: final Query query = createItemQuery();
074: query.descend("foo").constrain(new Integer(4)).greater().not();
075: assertExpectedFoos(FieldIndexItem.class, new int[] { 3, 4 },
076: query);
077: }
078:
079: public void testSingleIndexSmallerOrEqual() {
080: final Query query = createItemQuery();
081: query.descend("foo").constrain(new Integer(7)).smaller()
082: .equal();
083: assertExpectedFoos(FieldIndexItem.class, new int[] { 3, 4, 7 },
084: query);
085: }
086:
087: public void testSingleIndexGreaterOrEqual() {
088: final Query query = createItemQuery();
089: query.descend("foo").constrain(new Integer(7)).greater()
090: .equal();
091: assertExpectedFoos(FieldIndexItem.class, new int[] { 7, 9 },
092: query);
093: }
094:
095: public void testSingleIndexRange() {
096: final Query query = createItemQuery();
097: query.descend("foo").constrain(new Integer(3)).greater();
098: query.descend("foo").constrain(new Integer(9)).smaller();
099: assertExpectedFoos(FieldIndexItem.class, new int[] { 4, 7 },
100: query);
101: }
102:
103: public void testSingleIndexAndRange() {
104: final Query query = createItemQuery();
105: Constraint c1 = query.descend("foo").constrain(new Integer(3))
106: .greater();
107: Constraint c2 = query.descend("foo").constrain(new Integer(9))
108: .smaller();
109: c1.and(c2);
110: assertExpectedFoos(FieldIndexItem.class, new int[] { 4, 7 },
111: query);
112: }
113:
114: public void testSingleIndexOr() {
115: final Query query = createItemQuery();
116: Constraint c1 = query.descend("foo").constrain(new Integer(4))
117: .smaller();
118: Constraint c2 = query.descend("foo").constrain(new Integer(7))
119: .greater();
120: c1.or(c2);
121: assertExpectedFoos(FieldIndexItem.class, new int[] { 3, 9 },
122: query);
123: }
124:
125: public void testExplicitAndOverOr() {
126: assertAndOverOrQuery(true);
127: }
128:
129: public void testImplicitAndOverOr() {
130: assertAndOverOrQuery(false);
131: }
132:
133: private void assertAndOverOrQuery(boolean explicitAnd) {
134: Query query = createItemQuery();
135: Constraint c1 = query.descend("foo").constrain(new Integer(3));
136: Constraint c2 = query.descend("foo").constrain(new Integer(9));
137: Constraint c3 = query.descend("foo").constrain(new Integer(3));
138: Constraint c4 = query.descend("foo").constrain(new Integer(7));
139: Constraint cc1 = c1.or(c2);
140: Constraint cc2 = c3.or(c4);
141: if (explicitAnd) {
142: cc1.and(cc2);
143: }
144: assertExpectedFoos(FieldIndexItem.class, new int[] { 3 }, query);
145: }
146:
147: public void testSingleIndexOrRange() {
148: Query query = createItemQuery();
149: Constraint c1 = query.descend("foo").constrain(new Integer(1))
150: .greater();
151: Constraint c2 = query.descend("foo").constrain(new Integer(4))
152: .smaller();
153: Constraint c3 = query.descend("foo").constrain(new Integer(4))
154: .greater();
155: Constraint c4 = query.descend("foo").constrain(new Integer(10))
156: .smaller();
157: Constraint cc1 = c1.and(c2);
158: Constraint cc2 = c3.and(c4);
159: cc1.or(cc2);
160: assertExpectedFoos(FieldIndexItem.class, new int[] { 3, 7, 9 },
161: query);
162: }
163:
164: public void testImplicitAndOnOrs() {
165: Query query = createItemQuery();
166: Constraint c1 = query.descend("foo").constrain(new Integer(4))
167: .smaller();
168: Constraint c2 = query.descend("foo").constrain(new Integer(3))
169: .greater();
170: Constraint c3 = query.descend("foo").constrain(new Integer(4))
171: .greater();
172: c1.or(c2);
173: c1.or(c3);
174:
175: assertExpectedFoos(FieldIndexItem.class,
176: new int[] { 3, 4, 7, 9 }, query);
177: }
178:
179: public void testTwoLevelDescendOr() {
180: Query query = createComplexItemQuery();
181: Constraint c1 = query.descend("child").descend("foo")
182: .constrain(new Integer(4)).smaller();
183: Constraint c2 = query.descend("child").descend("foo")
184: .constrain(new Integer(4)).greater();
185: c1.or(c2);
186: assertExpectedFoos(ComplexFieldIndexItem.class, new int[] { 4,
187: 9 }, query);
188: }
189:
190: public void _testOrOnDifferentFields() {
191: final Query query = createComplexItemQuery();
192: Constraint c1 = query.descend("foo").constrain(new Integer(3));
193: Constraint c2 = query.descend("bar").constrain(new Integer(8));
194: c1.or(c2);
195: assertExpectedFoos(ComplexFieldIndexItem.class, new int[] { 3,
196: 7, 9 }, query);
197: }
198:
199: public void testCantOptimizeOrInvolvingNonIndexedField() {
200: final Query query = createQuery(NonIndexedFieldIndexItem.class);
201: final Constraint c1 = query.descend("indexed").constrain(
202: new Integer(1));
203: final Constraint c2 = query.descend("foo").constrain(
204: new Integer(2));
205: c1.or(c2);
206: assertCantOptimize(query);
207: }
208:
209: public void testCantOptimizeDifferentLevels() {
210: final Query query = createComplexItemQuery();
211: Constraint c1 = query.descend("child").descend("foo")
212: .constrain(new Integer(4)).smaller();
213: Constraint c2 = query.descend("foo").constrain(new Integer(7))
214: .greater();
215: c1.or(c2);
216: assertCantOptimize(query);
217: }
218:
219: public void testCantOptimizeJoinOnNonIndexedFields() {
220: final Query query = createQuery(NonIndexedFieldIndexItem.class);
221: final Constraint c1 = query.descend("foo").constrain(
222: new Integer(1));
223: final Constraint c2 = query.descend("foo").constrain(
224: new Integer(2));
225: c1.or(c2);
226: assertCantOptimize(query);
227: }
228:
229: private void assertCantOptimize(Query query) {
230: final FieldIndexProcessorResult result = executeProcessor(query);
231: Assert
232: .areSame(FieldIndexProcessorResult.NO_INDEX_FOUND,
233: result);
234: }
235:
236: public void testIndexSelection() {
237: Query query = createComplexItemQuery();
238: query.descend("bar").constrain(new Integer(2));
239: query.descend("foo").constrain(new Integer(3));
240:
241: assertBestIndex("foo", query);
242:
243: query = createComplexItemQuery();
244: query.descend("foo").constrain(new Integer(3));
245: query.descend("bar").constrain(new Integer(2));
246:
247: assertBestIndex("foo", query);
248: }
249:
250: private void assertBestIndex(String expectedFieldIndex,
251: final Query query) {
252: IndexedNode node = selectBestIndex(query);
253: assertComplexItemIndex(expectedFieldIndex, node);
254: }
255:
256: public void testDoubleDescendingOnQuery() {
257: final Query query = createComplexItemQuery();
258: query.descend("child").descend("foo").constrain(new Integer(3));
259: assertExpectedFoos(ComplexFieldIndexItem.class,
260: new int[] { 4 }, query);
261: }
262:
263: public void testTripleDescendingOnQuery() {
264: final Query query = createComplexItemQuery();
265: query.descend("child").descend("child").descend("foo")
266: .constrain(new Integer(3));
267: assertExpectedFoos(ComplexFieldIndexItem.class,
268: new int[] { 7 }, query);
269: }
270:
271: public void testMultiTransactionSmallerWithCommit() {
272: final Transaction transaction = newTransaction();
273: fillTransactionWith(transaction, 0);
274:
275: int[] expectedZeros = newBTreeNodeSizedArray(0);
276: assertSmaller(transaction, expectedZeros, 3);
277:
278: transaction.commit();
279:
280: fillTransactionWith(transaction, 5);
281: assertSmaller(IntArrays4.concat(expectedZeros,
282: new int[] { 3, 4 }), 7);
283: }
284:
285: public void testMultiTransactionWithRollback() {
286: final Transaction transaction = newTransaction();
287: fillTransactionWith(transaction, 0);
288:
289: int[] expectedZeros = newBTreeNodeSizedArray(0);
290: assertSmaller(transaction, expectedZeros, 3);
291:
292: transaction.rollback();
293:
294: assertSmaller(transaction, new int[0], 3);
295:
296: fillTransactionWith(transaction, 5);
297: assertSmaller(new int[] { 3, 4 }, 7);
298: }
299:
300: public void testMultiTransactionSmaller() {
301: final Transaction transaction = newTransaction();
302: fillTransactionWith(transaction, 0);
303:
304: int[] expected = newBTreeNodeSizedArray(0);
305: assertSmaller(transaction, expected, 3);
306:
307: fillTransactionWith(transaction, 5);
308: assertSmaller(new int[] { 3, 4 }, 7);
309: }
310:
311: public void testMultiTransactionGreater() {
312: fillTransactionWith(systemTrans(), 10);
313: fillTransactionWith(systemTrans(), 5);
314: assertGreater(new int[] { 4, 7, 9 }, 3);
315: removeFromTransaction(systemTrans(), 5);
316: assertGreater(new int[] { 4, 7, 9 }, 3);
317: removeFromTransaction(systemTrans(), 10);
318: assertGreater(new int[] { 4, 7, 9 }, 3);
319: }
320:
321: public void testSingleIndexEquals() {
322: final int expectedBar = 3;
323: assertExpectedFoos(FieldIndexItem.class,
324: new int[] { expectedBar }, createQuery(expectedBar));
325: }
326:
327: public void testSingleIndexSmaller() {
328: assertSmaller(new int[] { 3, 4 }, 7);
329: }
330:
331: public void testSingleIndexGreater() {
332: assertGreater(new int[] { 4, 7, 9 }, 3);
333: }
334:
335: private void assertGreater(int[] expectedFoos, int greaterThan) {
336: final Query query = createItemQuery();
337: query.descend("foo").constrain(new Integer(greaterThan))
338: .greater();
339: assertExpectedFoos(FieldIndexItem.class, expectedFoos, query);
340: }
341:
342: private void assertExpectedFoos(Class itemClass,
343: final int[] expectedFoos, final Query query) {
344: final Transaction trans = transactionFromQuery(query);
345: final int[] expectedIds = mapToObjectIds(createQuery(trans,
346: itemClass), expectedFoos);
347: assertExpectedIDs(expectedIds, query);
348: }
349:
350: private void assertExpectedIDs(final int[] expectedIds,
351: final Query query) {
352: final FieldIndexProcessorResult result = executeProcessor(query);
353: if (expectedIds.length == 0) {
354: Assert.areSame(
355: FieldIndexProcessorResult.FOUND_INDEX_BUT_NO_MATCH,
356: result);
357: return;
358: }
359:
360: assertTreeInt(expectedIds, result.toTreeInt());
361: }
362:
363: private FieldIndexProcessorResult executeProcessor(final Query query) {
364: return createProcessor(query).run();
365: }
366:
367: private Transaction transactionFromQuery(Query query) {
368: return ((QQuery) query).getTransaction();
369: }
370:
371: private BTree btree() {
372: return fieldIndexBTree(FieldIndexItem.class, "foo");
373: }
374:
375: private void store(final Transaction trans,
376: final FieldIndexItem item) {
377: stream().set(trans, item);
378: }
379:
380: private void fillTransactionWith(Transaction trans, final int bar) {
381: for (int i = 0; i < BTreeAssert.fillSize(btree()); ++i) {
382: store(trans, new FieldIndexItem(bar));
383: }
384: }
385:
386: private int[] newBTreeNodeSizedArray(int value) {
387: final BTree btree = btree();
388: return BTreeAssert.newBTreeNodeSizedArray(btree, value);
389: }
390:
391: private void removeFromTransaction(Transaction trans, final int foo) {
392: final ObjectSet found = createItemQuery(trans).execute();
393: while (found.hasNext()) {
394: FieldIndexItem item = (FieldIndexItem) found.next();
395: if (item.foo == foo) {
396: stream().delete(trans, item);
397: }
398: }
399: }
400:
401: private void assertSmaller(final int[] expectedFoos,
402: final int smallerThan) {
403: assertSmaller(trans(), expectedFoos, smallerThan);
404: }
405:
406: private void assertSmaller(final Transaction transaction,
407: final int[] expectedFoos, final int smallerThan) {
408: final Query query = createItemQuery(transaction);
409: query.descend("foo").constrain(new Integer(smallerThan))
410: .smaller();
411: assertExpectedFoos(FieldIndexItem.class, expectedFoos, query);
412: }
413:
414: }
|