Changeset 4372

Show
Ignore:
Timestamp:
05/17/08 13:01:48 (8 months ago)
Author:
romanb
Message:

Fixed #1039. bruno.p.reis: please take a look at the changeset and verify that the generated SQL is correct. We can't test against a real oracle db yet.

Location:
branches/0.11
Files:
5 modified

Legend:

Unmodified
Added
Removed
  • branches/0.11/lib/Doctrine/Connection.php

    r4366 r4372  
    15041504     * Some dbms require specific functionality for this. Check the other connection adapters for examples 
    15051505     * 
    1506      * @return void 
     1506     * @return string 
    15071507     */ 
    15081508    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false) 
    15091509    { 
    15101510        return $query; 
     1511    } 
     1512     
     1513    /** 
     1514     * Creates dbms specific LIMIT/OFFSET SQL for the subqueries that are used in the 
     1515     * context of the limit-subquery algorithm. 
     1516     * 
     1517     * @return string 
     1518     */ 
     1519    public function modifyLimitSubquery(Doctrine_Table $rootTable, $query, $limit = false, 
     1520            $offset = false, $isManip = false) 
     1521    { 
     1522        return $this->modifyLimitQuery($query, $limit, $offset, $isManip); 
    15111523    } 
    15121524 
  • branches/0.11/lib/Doctrine/Connection/Db2.php

    r4252 r4372  
    5656 
    5757            $sql = 'WITH OFFSET AS(' . $select . ', ROW_NUMBER() ' . 
    58                'OVER(ORDER BY ' . $col[1] . ') AS dctrn_rownum FROM ' . $table . ')' . 
    59                $select . 'FROM OFFSET WHERE dctrn_rownum BETWEEN ' . $offset . 
     58               'OVER(ORDER BY ' . $col[1] . ') AS doctrine_rownum FROM ' . $table . ')' . 
     59               $select . 'FROM OFFSET WHERE doctrine_rownum BETWEEN ' . $offset . 
    6060                   'AND ' . ($offset + $limit - 1); 
    6161            return $sql; 
  • branches/0.11/lib/Doctrine/Connection/Oracle.php

    r4362 r4372  
    8383    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false) 
    8484    { 
     85        return $this->_createLimitSubquery($query, $limit, $offset); 
     86    } 
     87     
     88    private function _createLimitSubquery($query, $limit, $offset, $column = null) 
     89    { 
    8590        $limit = (int) $limit; 
    8691        $offset = (int) $offset; 
     
    9095            } 
    9196            if ($limit > 0) { 
    92                 // taken from http://svn.ez.no/svn/ezcomponents/packages/Database 
    9397                $max = $offset + $limit; 
     98                $column = $column === null ? '*' : $column; 
    9499                if ($offset > 0) { 
    95100                    $min = $offset + 1; 
    96                     $query = 'SELECT b.* FROM ( 
    97                                  SELECT a.*, ROWNUM AS doctrine_rownum FROM (' 
    98                                   . $query . ') a 
    99                               ) b 
    100                               WHERE b.doctrine_rownum BETWEEN ' . $min .  ' AND ' . $max; 
     101                    $query = 'SELECT b.'.$column.' FROM ('. 
     102                                 'SELECT a.*, ROWNUM AS doctrine_rownum FROM (' 
     103                                   . $query . ') a '. 
     104                              ') b '. 
     105                              'WHERE doctrine_rownum BETWEEN ' . $min .  ' AND ' . $max; 
    101106                } else { 
    102                     $query = 'SELECT a.* FROM (' . $query .') a WHERE ROWNUM <= ' . $max; 
     107                    $query = 'SELECT a.'.$column.' FROM (' . $query .') a WHERE ROWNUM <= ' . $max; 
    103108                } 
    104109            } 
     
    106111        return $query; 
    107112    } 
     113     
     114    /** 
     115     * Creates the SQL for Oracle that can be used in the subquery for the limit-subquery 
     116     * algorithm. 
     117     */ 
     118    public function modifyLimitSubquery(Doctrine_Table $rootTable, $query, $limit = false, 
     119            $offset = false, $isManip = false) 
     120    { 
     121        // NOTE: no composite key support 
     122        $columnNames = $rootTable->getIdentifierColumnNames(); 
     123        if (count($columnNames) > 1) { 
     124            throw new Doctrine_Connection_Exception("Composite keys in LIMIT queries are " 
     125                    . "currently not supported."); 
     126        } 
     127        $column = $columnNames[0]; 
     128        return $this->_createLimitSubquery($query, $limit, $offset, $column); 
     129    } 
    108130} 
  • branches/0.11/lib/Doctrine/Query.php

    r4274 r4372  
    12021202        $modifyLimit = true; 
    12031203        if ( ! empty($this->_sqlParts['limit']) || ! empty($this->_sqlParts['offset'])) { 
    1204  
    12051204            if ($needsSubQuery) { 
    12061205                $subquery = $this->getLimitSubquery(); 
     
    12101209                    case 'mysql': 
    12111210                        // mysql doesn't support LIMIT in subqueries 
    1212                         $list     = $this->_conn->execute($subquery, $params)->fetchAll(Doctrine::FETCH_COLUMN); 
     1211                        $list = $this->_conn->execute($subquery, $params)->fetchAll(Doctrine::FETCH_COLUMN); 
    12131212                        $subquery = implode(', ', array_map(array($this->_conn, 'quote'), $list)); 
    12141213                        break; 
     
    12641263    public function getLimitSubquery() 
    12651264    { 
    1266         $map    = reset($this->_queryComponents); 
    1267         $table  = $map['table']; 
     1265        $map = reset($this->_queryComponents); 
     1266        $table = $map['table']; 
    12681267        $componentAlias = key($this->_queryComponents); 
    12691268 
    12701269        // get short alias 
    1271         $alias      = $this->getTableAlias($componentAlias); 
     1270        $alias = $this->getTableAlias($componentAlias); 
    12721271        // what about composite keys? 
    12731272        $primaryKey = $alias . '.' . $table->getColumnName($table->getIdentifier()); 
    12741273 
    12751274        // initialize the base of the subquery 
    1276         $subquery   = 'SELECT DISTINCT ' . $this->_conn->quoteIdentifier($primaryKey); 
     1275        $subquery = 'SELECT DISTINCT ' . $this->_conn->quoteIdentifier($primaryKey); 
    12771276 
    12781277        $driverName = $this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME); 
     
    13321331 
    13331332        // add driver specific limit clause 
    1334         $subquery = $this->_conn->modifyLimitQuery($subquery, $this->_sqlParts['limit'], $this->_sqlParts['offset']); 
     1333        $subquery = $this->_conn->modifyLimitSubquery($table, $subquery, $this->_sqlParts['limit'], $this->_sqlParts['offset']); 
    13351334 
    13361335        $parts = $this->_tokenizer->quoteExplode($subquery, ' ', "'", "'"); 
  • branches/0.11/tests/Query/DriverTestCase.php

    r3884 r4372  
    100100        $q->from('User u')->limit(5)->offset(2); 
    101101 
    102         $this->assertEqual($q->getSql(), 'SELECT * FROM (SELECT a.*, ROWNUM dctrn_rownum FROM (SELECT e.id AS e__id, e.name AS e__name, e.loginname AS e__loginname, e.password AS e__password, e.type AS e__type, e.created AS e__created, e.updated AS e__updated, e.email_id AS e__email_id FROM entity e WHERE (e.type = 0)) a WHERE ROWNUM <= 7) WHERE dctrn_rownum >= 3'); 
     102        $this->assertEqual($q->getSql(), 'SELECT b.* FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT e.id AS e__id, e.name AS e__name, e.loginname AS e__loginname, e.password AS e__password, e.type AS e__type, e.created AS e__created, e.updated AS e__updated, e.email_id AS e__email_id FROM entity e WHERE (e.type = 0)) a ) b WHERE doctrine_rownum BETWEEN 3 AND 7'); 
    103103    } 
     104     
     105    public function testLimitOffsetLimitSubqueriesForOracle() 
     106    { 
     107        $this->dbh = new Doctrine_Adapter_Mock('oracle'); 
     108        $conn = $this->manager->openConnection($this->dbh); 
     109        $q = new Doctrine_Query($conn); 
     110        $q->from('User u')->innerJoin('u.Phonenumber p')->limit(5)->offset(2); 
     111         
     112        $correctSql = "SELECT e.id AS e__id, e.name AS e__name, e.loginname AS e__loginname, " 
     113                            . "e.password AS e__password, e.type AS e__type, e.created AS e__created, " 
     114                            . "e.updated AS e__updated, e.email_id AS e__email_id, p.id AS p__id, " 
     115                            . "p.phonenumber AS p__phonenumber, p.entity_id AS p__entity_id " 
     116                            . "FROM entity e " 
     117                            . "INNER JOIN phonenumber p ON e.id = p.entity_id " 
     118                            . "WHERE e.id IN (" 
     119                                . "SELECT b.id FROM (" 
     120                                    . "SELECT a.*, ROWNUM AS doctrine_rownum " 
     121                                    . "FROM (" 
     122                                        . "SELECT DISTINCT e2.id " 
     123                                        . "FROM entity e2 " 
     124                                        . "INNER JOIN phonenumber p2 ON e2.id = p2.entity_id " 
     125                                        . "WHERE (e2.type = 0)" 
     126                                    . ") a" 
     127                                . " ) b " 
     128                                . "WHERE doctrine_rownum BETWEEN 3 AND 7" 
     129                            . ") AND (e.type = 0)"; 
     130         
     131        $this->assertEqual($q->getSql(), $correctSql); 
     132    } 
     133     
    104134}