// queryoptimizertests.cpp : query optimizer unit tests // /** * Copyright (C) 2009 10gen 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 . */ #include "../db/queryoptimizer.h" #include "dbtests.h" namespace QueryOptimizerTests { namespace FieldBoundTests { class Base { public: virtual ~Base() {} void run() { FieldBoundSet s( query() ); checkElt( lower(), s.bound( "a" ).lower() ); checkElt( upper(), s.bound( "a" ).upper() ); } protected: virtual BSONObj query() = 0; virtual BSONElement lower() { return minKey.firstElement(); } virtual BSONElement upper() { return maxKey.firstElement(); } private: static void checkElt( BSONElement expected, BSONElement actual ) { if ( expected.woCompare( actual, false ) ) { stringstream ss; ss << "expected: " << expected << ", got: " << actual; FAIL( ss.str() ); } } }; class Bad { public: virtual ~Bad() {} void run() { ASSERT_EXCEPTION( FieldBoundSet f( query() ), AssertionException ); } protected: virtual BSONObj query() = 0; }; class Empty : public Base { virtual BSONObj query() { return emptyObj; } }; class Eq : public Base { public: Eq() : o_( BSON( "a" << 1 ) ) {} virtual BSONObj query() { return o_; } virtual BSONElement lower() { return o_.firstElement(); } virtual BSONElement upper() { return o_.firstElement(); } BSONObj o_; }; class DupEq : public Eq { public: virtual BSONObj query() { return BSON( "a" << 1 << "b" << 2 << "a" << 1 ); } }; class Lt : public Base { public: Lt() : o_( BSON( "-" << 1 ) ) {} virtual BSONObj query() { return BSON( "a" << LT << 1 ); } virtual BSONElement upper() { return o_.firstElement(); } BSONObj o_; }; class Lte : public Lt { virtual BSONObj query() { return BSON( "a" << LTE << 1 ); } }; class Gt : public Base { public: Gt() : o_( BSON( "-" << 1 ) ) {} virtual BSONObj query() { return BSON( "a" << GT << 1 ); } virtual BSONElement lower() { return o_.firstElement(); } BSONObj o_; }; class Gte : public Gt { virtual BSONObj query() { return BSON( "a" << GTE << 1 ); } }; class TwoLt : public Lt { virtual BSONObj query() { return BSON( "a" << LT << 1 << LT << 5 ); } }; class TwoGt : public Gt { virtual BSONObj query() { return BSON( "a" << GT << 0 << GT << 1 ); } }; class EqGte : public Eq { virtual BSONObj query() { return BSON( "a" << 1 << "a" << GTE << 1 ); } }; class EqGteInvalid : public Bad { virtual BSONObj query() { return BSON( "a" << 1 << "a" << GTE << 2 ); } }; class Regex : public Base { public: Regex() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {} virtual BSONObj query() { BSONObjBuilder b; b.appendRegex( "a", "^abc" ); return b.obj(); } virtual BSONElement lower() { return o1_.firstElement(); } virtual BSONElement upper() { return o2_.firstElement(); } BSONObj o1_, o2_; }; class UnhelpfulRegex : public Base { virtual BSONObj query() { BSONObjBuilder b; b.appendRegex( "a", "abc" ); return b.obj(); } }; class In : public Base { public: In() : o1_( BSON( "-" << -3 ) ), o2_( BSON( "-" << 44 ) ) {} virtual BSONObj query() { vector< int > vals; vals.push_back( 4 ); vals.push_back( 8 ); vals.push_back( 44 ); vals.push_back( -1 ); vals.push_back( -3 ); vals.push_back( 0 ); BSONObjBuilder bb; bb.append( "$in", vals ); BSONObjBuilder b; b.append( "a", bb.done() ); return b.obj(); } virtual BSONElement lower() { return o1_.firstElement(); } virtual BSONElement upper() { return o2_.firstElement(); } BSONObj o1_, o2_; }; } // namespace FieldBoundTests namespace QueryPlanTests { class NoSpec { public: void run() { ASSERT_EXCEPTION( QueryPlan p( FieldBoundSet( emptyObj ), emptyObj, emptyObj ), AssertionException ); } }; class SimpleOrder { public: void run() { QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "b" << 1 ), BSON( "a" << 1 ) ); ASSERT( p3.scanAndOrderRequired() ); } }; class MoreIndexThanNeeded { public: void run() { QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); } }; class IndexSigns { public: void run() { QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) ); ASSERT( !p.scanAndOrderRequired() ); QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( p2.scanAndOrderRequired() ); } }; class IndexReverse { public: void run() { QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << -1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) ); ASSERT( p3.scanAndOrderRequired() ); } }; class EqualThenOrder { public: void run() { QueryPlan p( FieldBoundSet( BSON( "a" << 4 ) ), BSON( "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); } }; } // namespace QueryPlanTests class All : public UnitTest::Suite { public: All() { add< FieldBoundTests::Empty >(); add< FieldBoundTests::Eq >(); add< FieldBoundTests::DupEq >(); add< FieldBoundTests::Lt >(); add< FieldBoundTests::Lte >(); add< FieldBoundTests::Gt >(); add< FieldBoundTests::Gte >(); add< FieldBoundTests::TwoLt >(); add< FieldBoundTests::TwoGt >(); add< FieldBoundTests::EqGte >(); add< FieldBoundTests::EqGteInvalid >(); add< FieldBoundTests::Regex >(); add< FieldBoundTests::UnhelpfulRegex >(); add< FieldBoundTests::In >(); add< QueryPlanTests::NoSpec >(); add< QueryPlanTests::SimpleOrder >(); add< QueryPlanTests::MoreIndexThanNeeded >(); add< QueryPlanTests::IndexSigns >(); add< QueryPlanTests::IndexReverse >(); // add< QueryPlanTests::EqualThenOrder >(); } }; } // namespace QueryOptimizerTests UnitTest::TestPtr queryOptimizerTests() { return UnitTest::createSuite< QueryOptimizerTests::All >(); }