Changeset 6052

Show
Ignore:
Timestamp:
07/10/09 13:53:48 (14 months ago)
Author:
jwage
Message:

[2.0] More work on the QueryBuilder? and Expr classes.

Location:
trunk
Files:
3 added
2 removed
16 modified

Legend:

Unmodified
Added
Removed
  • trunk/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php

    r5909 r6052  
    15391539    } 
    15401540 
     1541    public function modifyLimitQuery($query, $max, $first) 
     1542    { 
     1543        if ( ! is_null($first)) { 
     1544            $query .= ' OFFSET ' . $first; 
     1545        } 
     1546 
     1547        if ( ! is_null($max)) { 
     1548            $query .= ' LIMIT ' . $max; 
     1549        } 
     1550 
     1551        return $query; 
     1552    } 
     1553 
    15411554    /** 
    15421555     * Gets the SQL snippet used to declare a VARCHAR column type. 
  • trunk/lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php

    r5898 r6052  
    426426        return 'mssql'; 
    427427    } 
     428 
     429    /** 
     430     * Adds an adapter-specific LIMIT clause to the SELECT statement. 
     431     * 
     432     * @param string $query 
     433     * @param mixed $limit 
     434     * @param mixed $offset 
     435     * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html 
     436     * @return string 
     437     */ 
     438    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false) 
     439    { 
     440        if ($limit > 0) { 
     441            $count = intval($limit); 
     442            $offset = intval($offset); 
     443 
     444            if ($offset < 0) { 
     445                throw new Doctrine_Connection_Exception("LIMIT argument offset=$offset is not valid"); 
     446            } 
     447 
     448            $orderby = stristr($query, 'ORDER BY'); 
     449 
     450            if ($orderby !== false) { 
     451                // Ticket #1835: Fix for ORDER BY alias 
     452                // Ticket #2050: Fix for multiple ORDER BY clause 
     453                $order = str_ireplace('ORDER BY', '', $orderby); 
     454                $orders = explode(',', $order); 
     455 
     456                for ($i = 0; $i < count($orders); $i++) { 
     457                    $sorts[$i] = (stripos($orders[$i], ' desc') !== false) ? 'desc' : 'asc'; 
     458                    $orders[$i] = trim(preg_replace('/\s+(ASC|DESC)$/i', '', $orders[$i])); 
     459 
     460                    // find alias in query string 
     461                    $helper_string = stristr($query, $orders[$i]); 
     462 
     463                    $from_clause_pos = strpos($helper_string, ' FROM '); 
     464                    $fields_string = substr($helper_string, 0, $from_clause_pos + 1); 
     465 
     466                    $field_array = explode(',', $fields_string); 
     467                    $field_array = array_shift($field_array); 
     468                    $aux2 = spliti(' as ', $field_array); 
     469 
     470                    $aliases[$i] = trim(end($aux2)); 
     471                } 
     472            } 
     473 
     474            // Ticket #1259: Fix for limit-subquery in MSSQL 
     475            $selectRegExp = 'SELECT\s+'; 
     476            $selectReplace = 'SELECT '; 
     477 
     478            if (preg_match('/^SELECT(\s+)DISTINCT/i', $query)) { 
     479                $selectRegExp .= 'DISTINCT\s+'; 
     480                $selectReplace .= 'DISTINCT '; 
     481            } 
     482 
     483            $query = preg_replace('/^'.$selectRegExp.'/i', $selectReplace . 'TOP ' . ($count + $offset) . ' ', $query); 
     484            $query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); 
     485 
     486            if ($orderby !== false) { 
     487                $query .= ' ORDER BY ';  
     488 
     489                for ($i = 0, $l = count($orders); $i < $l; $i++) {  
     490                    if ($i > 0) { // not first order clause  
     491                        $query .= ', ';  
     492                    }  
     493 
     494                    $query .= $this->quoteIdentifier('inner_tbl') . '.' . $aliases[$i] . ' ';  
     495                    $query .= (stripos($sorts[$i], 'asc') !== false) ? 'DESC' : 'ASC'; 
     496                } 
     497            } 
     498 
     499            $query .= ') AS ' . $this->quoteIdentifier('outer_tbl'); 
     500 
     501            if ($orderby !== false) { 
     502                $query .= ' ORDER BY ';  
     503 
     504                for ($i = 0, $l = count($orders); $i < $l; $i++) {  
     505                    if ($i > 0) { // not first order clause  
     506                        $query .= ', ';  
     507                    }  
     508 
     509                    $query .= $this->quoteIdentifier('outer_tbl') . '.' . $aliases[$i] . ' ' . $sorts[$i]; 
     510                } 
     511            } 
     512        } 
     513 
     514        return $query; 
     515    } 
    428516} 
  • trunk/lib/Doctrine/DBAL/Platforms/OraclePlatform.php

    r5899 r6052  
    470470        return 'oracle'; 
    471471    } 
     472 
     473    /** 
     474     * Adds an driver-specific LIMIT clause to the query 
     475     * 
     476     * @param string $query         query to modify 
     477     * @param integer $limit        limit the number of rows 
     478     * @param integer $offset       start reading from given offset 
     479     * @return string               the modified query 
     480     */ 
     481    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false) 
     482    { 
     483        $limit = (int) $limit; 
     484        $offset = (int) $offset; 
     485        if (preg_match('/^\s*SELECT/i', $query)) { 
     486            if ( ! preg_match('/\sFROM\s/i', $query)) { 
     487                $query .= " FROM dual"; 
     488            } 
     489            if ($limit > 0) { 
     490                $max = $offset + $limit; 
     491                $column = $column === null ? '*' : $this->quoteIdentifier($column); 
     492                if ($offset > 0) { 
     493                    $min = $offset + 1; 
     494                    $query = 'SELECT b.'.$column.' FROM ('. 
     495                                 'SELECT a.*, ROWNUM AS doctrine_rownum FROM (' 
     496                                   . $query . ') a '. 
     497                              ') b '. 
     498                              'WHERE doctrine_rownum BETWEEN ' . $min .  ' AND ' . $max; 
     499                } else { 
     500                    $query = 'SELECT a.'.$column.' FROM (' . $query .') a WHERE ROWNUM <= ' . $max; 
     501                } 
     502            } 
     503        } 
     504        return $query; 
     505    } 
    472506} 
  • trunk/lib/Doctrine/ORM/Query/Expr.php

    r6048 r6052  
    3434class Expr 
    3535{ 
    36     public static function andx() 
     36    public static function andx($x = null) 
    3737    { 
    3838        return new Expr\Andx(func_get_args()); 
    3939    } 
    4040 
    41     public static function orx() 
     41    public static function orx($x = null) 
    4242    { 
    4343        return new Expr\Orx(func_get_args()); 
    4444    } 
    4545 
    46     public static function select() 
     46    public static function select($select = null) 
    4747    { 
    4848        return new Expr\Select(func_get_args()); 
    4949    } 
    5050 
    51     public static function selectField($field, $alias = null) 
    52     { 
    53         return new Expr\SelectField($field, $alias); 
     51    public static function orderBy($sort = null, $order = null) 
     52    { 
     53        return new Expr\OrderBy($sort, $order); 
     54    } 
     55 
     56    public static function groupBy($groupBy = null) 
     57    { 
     58        return new Expr\GroupBy(func_get_args()); 
     59    } 
     60 
     61    public static function having($having = null) 
     62    { 
     63        return new Expr\Having(func_get_args()); 
    5464    } 
    5565 
     
    106116    public static function countDistinct() 
    107117    { 
    108         return new Expr\CountDistinctFunction(func_get_args()); 
     118        return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')'; 
    109119    } 
    110120 
  • trunk/lib/Doctrine/ORM/Query/Expr/Andx.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for building and clauses 
     25 * Expression class for building DQL and parts 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
  • trunk/lib/Doctrine/ORM/Query/Expr/Base.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Abstract class for building DQL expressions 
     25 * Abstract base Expr class for building DQL parts 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
     
    6969    } 
    7070 
    71     public function __tostring() 
     71    public function __toString() 
    7272    { 
    7373        return $this->_preSeparator . implode($this->_separator, $this->_parts) . $this->_postSeparator; 
  • trunk/lib/Doctrine/ORM/Query/Expr/Comparison.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for comparison statements 
     25 * Expression class for DQL comparison expressions 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
  • trunk/lib/Doctrine/ORM/Query/Expr/Func.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for building comparison clauses 
     25 * Expression class for generating DQL functions 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
  • trunk/lib/Doctrine/ORM/Query/Expr/Math.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for math statements 
     25 * Expression class for DQL math statements 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
  • trunk/lib/Doctrine/ORM/Query/Expr/Orx.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for building and clauses 
     25 * Expression class for building DQL OR clauses 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
  • trunk/lib/Doctrine/ORM/Query/Expr/Select.php

    r6048 r6052  
    2323 
    2424/** 
    25  * Expression class for building DQL select clauses 
     25 * Expression class for building DQL select statements 
    2626 * 
    2727 * @author      Jonathan H. Wage <jonwage@gmail.com> 
     
    3636    protected $_preSeparator = ''; 
    3737    protected $_postSeparator = ''; 
    38     protected $_allowedClasses = array( 
    39         'Doctrine\ORM\Query\Expr\SelectField' 
    40     ); 
    4138} 
  • trunk/lib/Doctrine/ORM/Query/SqlWalker.php

    r5973 r6052  
    135135        $sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : ''; 
    136136 
     137        $q = $this->getQuery(); 
     138        $sql = $this->getConnection()->getDatabasePlatform() 
     139            ->modifyLimitQuery($sql, $q->getMaxResults(), $q->getFirstResult()); 
     140 
    137141        return $sql; 
    138142    } 
  • trunk/lib/Doctrine/ORM/QueryBuilder.php

    r6048 r6052  
    5050     * @var EntityManager $em Instance of an EntityManager to use for query 
    5151     */ 
    52     protected $_em; 
     52    private $_em; 
    5353 
    5454    /** 
    5555     * @var array $dqlParts The array of DQL parts collected 
    5656     */ 
    57     protected $_dqlParts = array( 
     57    private $_dqlParts = array( 
    5858        'select' => array(), 
    5959        'from' => array(), 
     
    6161        'groupBy' => array(), 
    6262        'having' => array(), 
    63         'orderBy' => array(), 
    64         'limit' => array(),  
    65         'offset' => array() 
     63        'orderBy' => array() 
    6664    ); 
    6765 
     
    6967     * @var integer $type  The type of query this is. Can be select, update or delete 
    7068     */ 
    71     protected $_type = self::SELECT; 
     69    private $_type = self::SELECT; 
    7270 
    7371    /** 
    7472     * @var integer $state The state of the query object. Can be dirty or clean. 
    7573     */ 
    76     protected $_state = self::STATE_CLEAN; 
     74    private $_state = self::STATE_CLEAN; 
    7775 
    7876    /** 
    7977     * @var string $dql The complete DQL string for this query 
    8078     */ 
    81     protected $_dql; 
     79    private $_dql; 
    8280 
    8381    /** 
    8482     * @var array $params Parameters of this query. 
    8583     */ 
    86     protected $_params = array(); 
     84    private $_params = array(); 
     85 
     86    /** 
     87     * @var integer The first result to return (the "offset"). 
     88     */ 
     89    private $_firstResult = null; 
     90     
     91    /** 
     92     * @var integer The maximum number of results to return (the "limit"). 
     93     */ 
     94    private $_maxResults = null; 
    8795 
    8896    public function __construct(EntityManager $entityManager) 
     
    144152        $q->setDql($this->getDql()); 
    145153        $q->setParameters($this->getParameters()); 
     154        $q->setFirstResult($this->getFirstResult()); 
     155        $q->setMaxResults($this->getMaxResults()); 
    146156 
    147157        return $q; 
     
    225235    } 
    226236 
    227     public function select($select) 
     237    public function select($select = null) 
    228238    { 
    229239        $selects = func_get_args(); 
     
    350360    public function groupBy($groupBy) 
    351361    { 
    352         return $this->add('groupBy', $groupBy, false); 
     362        return $this->add('groupBy', Expr::groupBy($groupBy), false); 
     363    } 
     364 
     365    public function addGroupBy($groupBy) 
     366    { 
     367        return $this->add('groupBy', Expr::groupBy($groupBy), true); 
    353368    } 
    354369 
    355370    public function having($having) 
    356371    { 
    357         return $this->add('having', $having, false); 
     372        return $this->add('having', Expr::having($having), false); 
    358373    } 
    359374 
     
    364379        } 
    365380 
    366         return $this->add('having', $having, true); 
     381        return $this->add('having', Expr::having($having), true); 
    367382    } 
    368383 
     
    373388        } 
    374389 
    375         return $this->add('having', $having, true); 
     390        return $this->add('having', Expr::having($having), true); 
    376391    } 
    377392 
    378393    public function orderBy($sort, $order) 
    379394    { 
    380         return $this->add('orderBy', $sort . ' ' . $order, false); 
     395        return $this->add('orderBy', Expr::orderBy($sort, $order), false); 
    381396    } 
    382397 
    383398    public function addOrderBy($sort, $order) 
    384399    { 
    385         return $this->add('orderBy', $sort . ' ' . $order, true); 
    386     } 
    387  
    388     public function limit($limit) 
    389     { 
    390         return $this->add('limit', $limit); 
    391     } 
    392  
    393     public function offset($offset) 
    394     { 
    395         return $this->add('offset', $offset); 
     400        return $this->add('orderBy', Expr::orderBy($sort, $order), true); 
     401    } 
     402 
     403    /** 
     404     * Sets the position of the first result to retrieve (the "offset"). 
     405     * 
     406     * @param integer $firstResult The first result to return. 
     407     * @return Query This query object. 
     408     */ 
     409    public function setFirstResult($firstResult) 
     410    { 
     411        $this->_firstResult = $firstResult; 
     412        return $this; 
     413    } 
     414     
     415    /** 
     416     * Gets the position of the first result the query object was set to retrieve (the "offset"). 
     417     * Returns NULL if {@link setFirstResult} was not applied to this query. 
     418     *  
     419     * @return integer The position of the first result. 
     420     */ 
     421    public function getFirstResult() 
     422    { 
     423        return $this->_firstResult; 
     424    } 
     425 
     426    /** 
     427     * Sets the maximum number of results to retrieve (the "limit"). 
     428     *  
     429     * @param integer $maxResults 
     430     * @return Query This query object. 
     431     */ 
     432    public function setMaxResults($maxResults) 
     433    { 
     434        $this->_maxResults = $maxResults; 
     435        return $this; 
     436    } 
     437     
     438    /** 
     439     * Gets the maximum number of results the query object was set to retrieve (the "limit"). 
     440     * Returns NULL if {@link setMaxResults} was not applied to this query. 
     441     *  
     442     * @return integer Maximum number of results. 
     443     */ 
     444    public function getMaxResults() 
     445    { 
     446        return $this->_maxResults; 
    396447    } 
    397448 
     
    415466              . $this->_getReducedDqlQueryPart('from', array('pre' => ' ', 'separator' => ' ')) 
    416467              . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) 
    417               . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')) 
    418               . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) 
    419               . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); 
     468              . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); 
    420469    } 
    421470 
     
    440489              . $this->_getReducedDqlQueryPart('set', array('pre' => ' SET ', 'separator' => ', ')) 
    441490              . $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' ')) 
    442               . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')) 
    443               . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) 
    444               . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); 
     491              . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); 
    445492    } 
    446493 
     
    470517              . $this->_getReducedDqlQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) 
    471518              . $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING ', 'separator' => ' ')) 
    472               . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')) 
    473               . $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' ')) 
    474               . $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' ')); 
     519              . $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); 
    475520    } 
    476521 
  • trunk/tests/Doctrine/Tests/ORM/Functional/AllTests.php

    r6007 r6052  
    3535        $suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManySelfReferentialAssociationTest'); 
    3636        $suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManySelfReferentialAssociationTest'); 
     37        $suite->addTestSuite('Doctrine\Tests\ORM\Functional\QueryBuilderTest'); 
    3738 
    3839        return $suite; 
  • trunk/tests/Doctrine/Tests/ORM/Query/ExprTest.php

    r6048 r6052  
    246246    { 
    247247        $selectExpr = Expr::select(); 
    248         $selectExpr->add(Expr::selectField('u.id')); 
    249         $selectExpr->add(Expr::selectField('u.username', 'my_test')); 
    250  
    251         $this->assertEquals('u.id, u.username AS my_test', (string) $selectExpr); 
     248        $selectExpr->add('u.id'); 
     249        $selectExpr->add('u.username'); 
     250 
     251        $this->assertEquals('u.id, u.username', (string) $selectExpr); 
     252    } 
     253 
     254    public function testExprBaseCount() 
     255    { 
     256        $selectExpr = Expr::select(); 
     257        $selectExpr->add('u.id'); 
     258        $selectExpr->add('u.username'); 
     259 
     260        $this->assertEquals($selectExpr->count(), 2); 
     261    } 
     262 
     263    public function testOrderByCountExpr() 
     264    { 
     265        $orderByExpr = Expr::orderBy(); 
     266        $orderByExpr->add('u.username', 'DESC'); 
     267 
     268        $this->assertEquals($orderByExpr->count(), 1); 
     269        $this->assertEquals('u.username DESC', (string) $orderByExpr); 
     270    } 
     271 
     272    public function testOrderByOrder() 
     273    { 
     274        $orderByExpr = Expr::orderBy('u.username', 'DESC'); 
     275        $this->assertEquals('u.username DESC', (string) $orderByExpr); 
     276    } 
     277 
     278    public function testOrderByDefaultOrderIsAsc() 
     279    { 
     280        $orderByExpr = Expr::orderBy('u.username'); 
     281        $this->assertEquals('u.username ASC', (string) $orderByExpr); 
     282    } 
     283 
     284    /** 
     285     * @expectedException Doctrine\Common\DoctrineException 
     286     */ 
     287    public function testAddThrowsException() 
     288    { 
     289        $orExpr = Expr::orx(); 
     290        $orExpr->add(Expr::quot(5, 2)); 
    252291    } 
    253292} 
  • trunk/tests/Doctrine/Tests/ORM/QueryBuilderTest.php

    r6048 r6052  
    6666    } 
    6767 
     68    public function testEmptySelectSetsType() 
     69    { 
     70        $qb = QueryBuilder::create($this->_em) 
     71            ->delete('Doctrine\Tests\Models\CMS\CmsUser', 'u') 
     72            ->select(); 
     73 
     74        $this->assertEquals($qb->getType(), QueryBuilder::SELECT); 
     75    } 
     76 
    6877    public function testDeleteSetsType() 
    6978    { 
     
    211220            ->select('u') 
    212221            ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') 
    213             ->groupBy('u.id'); 
    214  
    215         $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id'); 
     222            ->groupBy('u.id') 
     223            ->addGroupBy('u.username'); 
     224 
     225        $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id, u.username'); 
    216226    } 
    217227 
     
    310320    } 
    311321 
     322 
     323    public function testGetParameters() 
     324    { 
     325        $qb = QueryBuilder::create($this->_em) 
     326            ->select('u') 
     327            ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') 
     328            ->where('u.id = :id'); 
     329 
     330        $qb->setParameters(array('id' => 1)); 
     331        $this->assertEquals(array('id' => 1, 'test' => 1), $qb->getParameters(array('test' => 1))); 
     332    } 
     333 
     334    public function testGetParameter() 
     335    { 
     336        $qb = QueryBuilder::create($this->_em) 
     337            ->select('u') 
     338            ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') 
     339            ->where('u.id = :id'); 
     340 
     341        $qb->setParameters(array('id' => 1)); 
     342        $this->assertEquals(1, $qb->getParameter('id')); 
     343    } 
     344 
    312345    public function testMultipleWhere() 
    313346    { 
     
    354387    } 
    355388 
    356     public function testLimit() 
    357     { 
    358         /* 
    359         TODO: Limit fails. Is this not implemented in the DQL parser? Will look tomorrow. 
    360         $qb = QueryBuilder::create($this->_em) 
    361             ->select('u') 
    362             ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') 
    363             ->limit(10) 
    364             ->offset(0); 
    365  
    366         $this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LIMIT 10'); 
    367         */ 
     389    public function testGetEntityManager() 
     390    { 
     391        $qb = QueryBuilder::create($this->_em); 
     392        $this->assertEquals($this->_em, $qb->getEntityManager()); 
     393    } 
     394 
     395    public function testInitialStateIsClean() 
     396    { 
     397        $qb = QueryBuilder::create($this->_em); 
     398        $this->assertEquals(QueryBuilder::STATE_CLEAN, $qb->getState()); 
     399    } 
     400 
     401    public function testAlteringQueryChangesStateToDirty() 
     402    { 
     403        $qb = QueryBuilder::create($this->_em) 
     404            ->select('u') 
     405            ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u'); 
     406 
     407        $this->assertEquals(QueryBuilder::STATE_DIRTY, $qb->getState()); 
    368408    } 
    369409}