Changeset 4451 for trunk/lib/Doctrine

Show
Ignore:
Timestamp:
05/27/08 04:44:08 (8 months ago)
Author:
guilhermeblanco
Message:

Finished first parts of SELECT support. Two test cases added and passing. Introduced the concept of DCTRN in queryComponent. Added concept of queryField, which validates for already defined fields in SELECT.

Location:
trunk/lib/Doctrine/Query
Files:
1 added
9 modified

Legend:

Unmodified
Added
Removed
  • trunk/lib/Doctrine/Query/AbstractResult.php

    r4422 r4451  
    133133 
    134134    /** 
     135     * Get the component alias for a given query component 
     136     * 
     137     * @param array $queryComponent The query component 
     138     * @param string Component alias 
     139     */ 
     140    public function getComponentAlias($queryComponent) 
     141    { 
     142        return array_search($queryComponent, $this->_queryComponents);; 
     143    } 
     144 
     145 
     146    /** 
    135147     * Whether or not this object has a declaration for given component alias. 
    136148     * 
  • trunk/lib/Doctrine/Query/Parser.php

    r4422 r4451  
    115115    { 
    116116        $this->_scanner = new Doctrine_Query_Scanner($dql); 
    117         $this->_parserResult = new Doctrine_Query_ParserResult(); 
    118117        $this->_sqlBuilder = Doctrine_Query_SqlBuilder::fromConnection($connection); 
    119118        $this->_keywordTable = new Doctrine_Query_Token(); 
     119 
     120        $this->_parserResult = new Doctrine_Query_ParserResult( 
     121            '', 
     122            array( // queryComponent 
     123                'dctrn' => array( 
     124                    'metadata' => null, 
     125                    'parent'   => null, 
     126                    'relation' => null, 
     127                    'map'      => null, 
     128                    'scalar'   => null, 
     129                ), 
     130            ), 
     131            array( // tableAliasMap 
     132                'dctrn' => 'dctrn', 
     133            ) 
     134        ); 
    120135 
    121136        $this->free(true); 
  • trunk/lib/Doctrine/Query/ParserResult.php

    r4422 r4451  
    4444    protected $_tableAliasSeeds = array(); 
    4545 
     46    /** 
     47     * Simple array of keys representing the fields used in query. 
     48     * 
     49     * @var array $_queryFields 
     50     */ 
     51    protected $_queryFields = array(); 
     52 
    4653 
    4754    /** 
     
    6067    { 
    6168        return $this->_data; 
     69    } 
     70 
     71 
     72    /** 
     73     * Defines the mapping fields. 
     74     * 
     75     * @param array $queryFields Query fields. 
     76     */ 
     77    public function setQueryComponents(array $queryFields) 
     78    { 
     79        $this->_queryFields = $queryFields; 
     80    } 
     81 
     82 
     83    /** 
     84     * Sets the declaration for given field alias. 
     85     * 
     86     * @param string $fieldAlias The field alias to set the declaration to. 
     87     * @param string $queryField Alias declaration. 
     88     */ 
     89    public function setQueryField($fieldAlias, array $queryField) 
     90    { 
     91        $this->_queryFields[$fieldAlias] = $queryField; 
     92    } 
     93 
     94 
     95    /** 
     96     * Gets the mapping fields. 
     97     * 
     98     * @return array Query fields. 
     99     */ 
     100    public function getQueryFields() 
     101    { 
     102        return $this->_queryComponents; 
     103    } 
     104 
     105 
     106    /** 
     107     * Get the declaration for given field alias. 
     108     * 
     109     * @param string $fieldAlias The field alias the retrieve the declaration from. 
     110     * @return array Alias declaration. 
     111     */ 
     112    public function getQueryField($fieldAlias) 
     113    { 
     114        if ( ! isset($this->_queryFields[$fieldAlias])) { 
     115            throw new Doctrine_Query_Exception('Unknown query field ' . $fieldAlias); 
     116        } 
     117 
     118        return $this->_queryFields[$fieldAlias]; 
     119    } 
     120 
     121 
     122    /** 
     123     * Whether or not this object has a declaration for given field alias. 
     124     * 
     125     * @param string $fieldAlias Field alias the retrieve the declaration from. 
     126     * @return boolean True if this object has given alias, otherwise false. 
     127     */ 
     128    public function hasQueryField($fieldAlias) 
     129    { 
     130        return isset($this->_queryFields[$fieldAlias]); 
    62131    } 
    63132 
  • trunk/lib/Doctrine/Query/Production/IdentificationVariable.php

    r4422 r4451  
    2525 * @package     Doctrine 
    2626 * @subpackage  Query 
     27 * @author      Guilherme Blanco <guilhermeblanco@hotmail.com> 
    2728 * @author      Janne Vanhala <jpvanhal@cc.hut.fi> 
    2829 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL 
  • trunk/lib/Doctrine/Query/Production/PathExpression.php

    r4431 r4451  
    3838    protected $_fieldName; 
    3939 
    40     private $_queryComponent; 
     40    protected $_queryComponent; 
    4141 
    4242 
     
    6868 
    6969            // Check if we have more than one queryComponent defined 
    70             if (count($queryComponents) != 1) { 
     70            if (count($queryComponents) != 2) { 
    7171                $this->_parser->semanticalError("Undefined component alias for field '{$this->_fieldName}'", $this->_parser->token); 
    7272            } 
     
    7474            // Retrieve ClassMetadata 
    7575            $k = array_keys($queryComponents); 
    76             $componentAlias = $k[0]; 
     76            $componentAlias = $k[1]; 
    7777 
    7878            $this->_queryComponent = $queryComponents[$componentAlias]; 
     
    142142            // Retrieve ClassMetadata 
    143143            $k = array_keys($queryComponents); 
    144             $componentAlias = $k[0]; 
     144            $componentAlias = $k[1]; 
    145145        } 
    146146 
  • trunk/lib/Doctrine/Query/Production/PathExpressionEndingWithAsterisk.php

    r4422 r4451  
    3535    protected $_identifiers = array(); 
    3636 
     37    protected $_queryComponent; 
     38 
    3739 
    3840    public function syntax($paramHolder) 
     
    5658        if (($l = count($this->_identifiers)) > 0) { 
    5759            // We are dealing with component{.component}.* 
    58             $classMetadata = null; 
     60            $path = $this->_identifiers[0]; 
     61            $this->_queryComponent = $parserResult->getQueryComponent($path); 
    5962 
    60             for ($i = 0; $i < $l; $i++) { 
     63            // We should have a semantical error if the queryComponent does not exists yet 
     64            if ($this->_queryComponent === null) { 
     65                $this->_parser->semanticalError("Undefined component alias for '{$path}'", $this->_parser->token); 
     66            } 
     67 
     68            // Initializing ClassMetadata 
     69            $classMetadata = $this->_queryComponent['metadata']; 
     70 
     71            // Looping through relations 
     72            for ($i = 1; $i < $l; $i++) { 
    6173                $relationName = $this->_identifiers[$i]; 
     74                $path .= '.' . $relationName; 
    6275 
    63                 // We are still checking for relations 
    64                 if ( $classMetadata !== null && ! $classMetadata->hasRelation($relationName)) { 
     76                if ( ! $classMetadata->hasRelation($relationName)) { 
    6577                    $className = $classMetadata->getClassName(); 
    6678 
    67                     $this->_parser->semanticalError("Relation '{$relationName}' does not exist in component '{$className}'"); 
     79                    $this->_parser->semanticalError( 
     80                        "Relation '{$relationName}' does not exist in component '{$className}' when trying to get the path '{$path}'", 
     81                        $this->_parser->token 
     82                    ); 
     83                } 
    6884 
    69                     // Assigning new ClassMetadata 
    70                     $classMetadata = $classMetadata->getRelation($relationName)->getClassMetadata(); 
    71                 } elseif ( $classMetadata === null ) { 
    72                     $queryComponent = $parserResult->getQueryComponent($relationName); 
     85                // We inspect for queryComponent of relations, since we are using them 
     86                if ( ! $parserResult->hasQueryComponent($path)) { 
     87                    $this->_parser->semanticalError("Cannot use the path '{$path}' without defining it in FROM.", $this->_parser->token); 
     88                } 
    7389 
    74                     // We should have a semantical error if the queryComponent does not exists yet 
    75                     if ($queryComponent === null) { 
    76                         $this->_parser->semanticalError("Undefined component alias for relation '{$relationName}'"); 
    77                     } 
     90                // Assigning new queryComponent and classMetadata 
     91                $this->_queryComponent = $parserResult->getQueryComponent($path); 
    7892 
    79                     // Initializing ClassMetadata 
    80                     $classMetadata = $queryComponent['metadata']; 
    81                 } 
     93                $classMetadata = $this->_queryComponent['metadata']; 
    8294            } 
    8395        } else { 
     
    8698            $queryComponents = $parserResult->getQueryComponents(); 
    8799 
    88             if (count($queryComponents) != 1) { 
     100            if (count($queryComponents) != 2) { 
    89101                $this->_parser->semanticalError( 
    90102                    "Cannot use * as selector expression for multiple components." 
     
    95107            // since we have it on hands now. 
    96108            $k = array_keys($queryComponents); 
    97             $this->_identifiers[] = $k[0]; 
     109            $componentAlias = $k[1]; 
     110 
     111            $this->_queryComponent = $queryComponents[$componentAlias]; 
    98112        } 
    99113    } 
     
    102116    public function buildSql() 
    103117    { 
    104         return ''; 
     118        // Basic handy variables 
     119        $parserResult = $this->_parser->getParserResult(); 
     120 
     121        // Retrieving connection 
     122        $manager = Doctrine_EntityManager::getManager();  
     123        $conn = $manager->getConnection(); 
     124 
     125        // Looking for componentAlias to fetch 
     126        $componentAlias = implode('.', $this->_identifiers); 
     127 
     128        if (count($this->_identifiers) == 0) { 
     129            $queryComponents = $parserResult->getQueryComponents(); 
     130 
     131            // Retrieve ClassMetadata 
     132            $k = array_keys($queryComponents); 
     133            $componentAlias = $k[1]; 
     134        } 
     135 
     136        // Generating the SQL piece 
     137        $fields = $this->_queryComponent['metadata']->getMappedColumns(); 
     138        $tableAlias = $parserResult->getTableAliasFromComponentAlias($componentAlias); 
     139        $str = ''; 
     140 
     141        foreach ($fields as $fieldName => $fieldMap) { 
     142            $str .= ($str != '') ? ', ' : ''; 
     143 
     144            // DB Field name 
     145            $column = $tableAlias . '.' . $this->_queryComponent['metadata']->getColumnName($fieldName); 
     146            $column = $conn->quoteIdentifier($column); 
     147 
     148            // DB Field alias 
     149            $columnAlias = $tableAlias . '__' . $this->_queryComponent['metadata']->getColumnName($fieldName); 
     150            $columnAlias = $conn->quoteIdentifier($columnAlias); 
     151 
     152            $str .= $column . ' AS ' . $columnAlias; 
     153        } 
     154 
     155        return $str; 
    105156    } 
    106157} 
  • trunk/lib/Doctrine/Query/Production/RangeVariableDeclaration.php

    r4431 r4451  
    130130                'relation' => null, 
    131131                'map'      => null, 
    132                 'agg'      => null, 
     132                'scalar'   => null, 
    133133            ); 
    134134        } catch (Doctrine_Exception $e) { 
     
    174174        for ($i = 1, $l = count($this->_identifiers); $i < $l; $i++) { 
    175175            $relationName = $this->_identifiers[$i]; 
    176             $path = '.' . $relationName; 
     176            $path .= '.' . $relationName; 
    177177 
    178178            if ($parserResult->hasQueryComponent($path)) { 
     
    212212                        'relation' => $relation, 
    213213                        'map'      => null, 
    214                         'agg'      => null, 
     214                        'scalar'   => null, 
    215215                    ); 
    216216 
  • trunk/lib/Doctrine/Query/Production/SelectExpression.php

    r4431 r4451  
    3939    protected $_isSubselect; 
    4040 
    41     protected $_identificationVariable; 
     41    protected $_fieldIdentificationVariable; 
    4242 
    4343    private $__columnAliasInSql; 
     
    5252        if ($this->_isPathExpressionEndingWithAsterisk()) { 
    5353            $this->_leftExpression = $this->AST('PathExpressionEndingWithAsterisk', $paramHolder); 
     54 
     55            $fieldName = implode('.', $this->_leftExpression->getIdentifiers()) . '.*'; 
    5456        } elseif(($this->_isSubselect = $this->_isSubselect()) === true) { 
    5557            $this->_parser->match('('); 
    5658            $this->_leftExpression = $this->AST('Subselect', $paramHolder); 
    5759            $this->_parser->match(')'); 
     60 
     61            // [TODO] Any way to make it more fancy for user error? 
     62            $fieldName = '<Subselect>'; 
    5863        } else { 
    5964            $this->_leftExpression = $this->AST('Expression', $paramHolder); 
     65 
     66            // [TODO] Any way to make it more fancy for user error? 
     67            $fieldName = '<Expression>'; 
    6068        } 
    6169 
     
    6573 
    6674        if ($this->_isNextToken(Doctrine_Query_Token::T_IDENTIFIER)) { 
    67             $this->_identificationVariable = $this->AST('IdentificationVariable', $paramHolder); 
     75            $paramHolder->set('fieldName', $fieldName); 
     76 
     77            // Will return an identifier, with the semantical check already applied 
     78            $this->_fieldIdentificationVariable = $this->AST('FieldIdentificationVariable', $paramHolder); 
     79 
     80            $paramHolder->remove('fieldName'); 
    6881        } 
    6982    } 
     
    7285    public function semantical($paramHolder) 
    7386    { 
     87        $parserResult = $this->_parser->getParserResult(); 
     88 
    7489        // Here we inspect for duplicate IdentificationVariable, and if the 
    7590        // left expression needs the identification variable. If yes, check 
    7691        // its existance. 
    77         if ($this->_leftExpression instanceof Doctrine_Query_Production_PathExpressionEndingWithAsterisk && $this->_identificationVariable !== null) { 
     92        if ($this->_leftExpression instanceof Doctrine_Query_Production_PathExpressionEndingWithAsterisk && $this->_fieldIdentificationVariable !== null) { 
    7893            $this->_parser->semanticalError( 
    7994                "Cannot assign an identification variable to a path expression with asterisk (ie. foo.bar.* AS foobaz)." 
     
    8398        $this->_leftExpression->semantical($paramHolder); 
    8499 
    85         /*if ($this->_identificationVariable !== null) { 
    86             if ($this->_leftExpression instanceof Doctrine_Query_Production_PathExpression) { 
    87                 // We bring the queryComponent from the class instance 
    88                 // $queryComponent = $this->_leftExpression->getQueryComponent(); 
    89             } else { 
    90                 // We bring the default queryComponent 
    91                 // $queryComponent = $parserResult->getQueryComponent(null); 
    92             } 
    93  
    94             $idx = count($queryComponent['scalar']); 
    95             $this->__columnAliasInSql .= '__' . $idx; 
    96  
    97             $queryComponent['scalar'][$idx] = $this->_identificationVariable; 
    98  
    99             //$parserResult->setQueryComponent($componentAlias, $queryComponent); 
    100         }*/ 
    101  
    102         // We need to add scalar in queryComponent the item alias if identificationvariable is set. 
    103         //echo "SelectExpression:\n"; 
    104         //echo get_class($this->_leftExpression) . "\n"; 
    105  
    106         // The check for duplicate IdentificationVariable was already done 
     100        if($this->_fieldIdentificationVariable !== null) { 
     101            $this->_fieldIdentificationVariable->semantical($paramHolder); 
     102        } 
    107103    } 
    108104 
     
    110106    public function buildSql() 
    111107    { 
    112         return $this->_leftExpression->buildSql();// . ' AS ' . (($this->_identificationVariable !== null) ? $this->_identificationVariable : ''); 
     108        return $this->_leftExpression->buildSql() . $this->_buildColumnAliasInSql(); 
    113109    } 
    114110 
     
    125121        return $token['value'] === '*'; 
    126122    } 
     123 
     124 
     125    protected function _buildColumnAliasInSql() 
     126    { 
     127        // Retrieving parser result 
     128        $parserResult = $this->_parser->getParserResult(); 
     129 
     130        switch (get_class($this->_leftExpression)) { 
     131            case 'Doctrine_Query_Production_PathExpressionEndingWithAsterisk': 
     132                return ''; 
     133            break; 
     134 
     135            case 'Doctrine_Query_Production_PathExpression': 
     136                // We bring the queryComponent from the class instance 
     137                $queryComponent = $this->_leftExpression->getQueryComponent(); 
     138            break; 
     139 
     140            default: 
     141                // We bring the default queryComponent 
     142                $queryComponent = $parserResult->getQueryComponent('dctrn'); 
     143            break; 
     144        } 
     145 
     146        // Retrieving connection 
     147        $manager = Doctrine_EntityManager::getManager();  
     148        $conn = $manager->getConnection(); 
     149 
     150        $componentAlias = $parserResult->getComponentAlias($queryComponent); 
     151 
     152        if ($this->_fieldIdentificationVariable !== null) { 
     153            // We need to add scalar map in queryComponent if iidentificationvariable is set. 
     154            $idx = count($queryComponent['scalar']); 
     155            $queryComponent['scalar'][$idx] = $this->_fieldIdentificationVariable; 
     156            $parserResult->setQueryComponent($componentAlias, $queryComponent); 
     157 
     158            $columnAlias = $parserResult->getTableAliasFromComponentAlias($componentAlias) . '__' . $idx; 
     159        } elseif ($this->_leftExpression instanceof Doctrine_Query_Production_PathExpression) { 
     160            // We need to build the column alias based on column name 
     161            $columnAlias = $parserResult->getTableAliasFromComponentAlias($componentAlias)  
     162                         . '__' . $queryComponent['metadata']->getColumnName($this->_leftExpression->getFieldName()); 
     163        } else { 
     164            // The right thing should be return the index alone... but we need a better solution. 
     165            // Possible we can search the index for mapped values and add our item there. 
     166 
     167            // [TODO] Find another return value. We cant return 0 here. 
     168            $columnAlias = 'idx__' . 0; 
     169        } 
     170 
     171        return ' AS ' . $conn->quoteIdentifier($columnAlias); 
     172    } 
    127173} 
  • trunk/lib/Doctrine/Query/Production/VariableDeclaration.php

    r4431 r4451  
    9797                    'relation' => null, 
    9898                    'map'      => null, 
    99                     'agg'      => null, 
     99                    'scalar'   => null, 
    100100                ); 
    101101            } catch (Doctrine_Exception $e) {