blob: de6b0fbcb1fa46ff3bf4b8ff649569f78b5a05dd [file] [log] [blame]
<?php
/**
* File containing the ezcQuerySelectSqlite class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Database
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* SQLite specific implementation of ezcQuery.
*
* This class reimplements methods where SQLite differs from the standard
* implementation in ezcQuery. The only difference is the right join syntax.
*
* @see ezcQuery
* @package Database
* @version //autogentag//
*/
class ezcQuerySelectSqlite extends ezcQuerySelect
{
/**
* Store info for building emulation of right joins in FROM clause.
*
* This array contains null if there was no calls to rightJoin().
* When call to rightJoin() occurs an item of right join info added.
* Right join info is array that consists of two arrays: 'tables' and 'conditions'.
* These arrays filled with values from parameters of rightJoin().
* If from() was called then new right join info item added to $rightJoins.
*/
protected $rightJoins = array( null );
/**
* Store tables that appear in FROM clause.
*
* Used for building fromString every time when it requested
*/
protected $fromTables = array();
/**
* Constructs a new ezcQuerySelectSqlite object.
*
* @param PDO $db
*/
public function __construct( PDO $db )
{
parent::__construct( $db );
}
/**
* Resets the query object for reuse.
*
* @return void
*/
public function reset()
{
parent::reset();
$this->fromTables = array();
$this->rightJoins = array( null );
}
/**
* Select which tables you want to select from.
*
* from() accepts an arbitrary number of parameters. Each parameter
* must contain either the name of a table or an array containing
* the names of tables..
* from() could be invoked several times. All provided arguments
* added to the end of $fromString.
*
* Additional actions performed to emulate right joins in SQLite.
*
* Example:
* <code>
* // the following code will produce the SQL
* // SELECT id FROM t2 LEFT JOIN t1 ON t1.id = t2.id
* $q->select( 'id' )->from( $q->rightJoin( 't1', 't2', 't1.id', 't2.id' ) );
*
* // the following code will produce the same SQL
* // SELECT id FROM t2 LEFT JOIN t1 ON t1.id = t2.id
* $q->select( 'id' )->from( 't1' )->rightJoin( 't2', 't1.id', 't2.id' );
* </code>
*
* @throws ezcQueryVariableParameterException if called with no parameters.
* @param string|array(string) $... Either a string with a table name or an array of table names.
* @return a pointer to $this
*/
public function from()
{
$args = func_get_args();
$tables = self::arrayFlatten( $args );
if ( count( $tables ) < 1 )
{
throw new ezcQueryVariableParameterException( 'from', count( $args ), 1 );
}
$this->lastInvokedMethod = 'from';
$tables = $this->getIdentifiers( $tables );
$this->fromTables = array_merge( $this->fromTables, $tables );
$this->fromString ='FROM '.join( ', ', $this->fromTables );
// adding right join part of query to the end of fromString.
$rightJoinPart = $this->buildRightJoins();
if ( $rightJoinPart != '' )
{
$this->fromString .= ', '.$rightJoinPart;
}
// adding new empty entry to $rightJoins if last entry was already filled
$lastRightJoin = end( $this->rightJoins );
if ( $lastRightJoin != null )
{
$this->rightJoins[] = null; // adding empty stub to the rightJoins
// it could be filled by next rightJoin()
}
return $this;
}
/**
* Returns SQL string with part of FROM clause that performs right join emulation.
*
* SQLite don't support right joins directly but there is workaround.
* identical result could be acheived using right joins for tables in reverce order.
*
* String created from entries of $rightJoins.
* One entry is a complete table_reference clause of SQL FROM clause.
*
* <code>
* rightJoins[0][tables] = array( 'table1', 'table2', 'table3' )
* rightJoins[0][conditions] = array( condition1, condition2 )
* </code>
* forms SQL: 'table3 LEFT JOIN table2 condition2 ON LEFT JOIN table1 ON condition1'.
*
*
* @return string the SQL call including all right joins set in query.
*/
private function buildRightJoins()
{
$resultArray = array();
foreach ( $this->rightJoins as $rJoinPart )
{
$oneItemResult = '';
if ( $rJoinPart === null )
{
break; // this is last empty entry so cancel adding.
}
// reverse lists of tables and conditions to make LEFT JOIN
// that will produce result equal to original right join.
$reversedTables = array_reverse( $rJoinPart['tables'] );
$reversedConditions = array_reverse( $rJoinPart['conditions'] );
// adding first table.
list( $key, $val ) = each( $reversedTables );
$oneItemResult .= $val;
while ( list( $key, $nextCondition ) = each( $reversedConditions ) )
{
list( $key2, $nextTable ) = each( $reversedTables );
$oneItemResult .= " LEFT JOIN {$nextTable} ON {$nextCondition}";
}
$resultArray[] = $oneItemResult;
}
return join( ', ', $resultArray );
}
/**
* Returns the SQL for a right join or prepares $fromString for a right join.
*
* This method could be used in two forms:
*
* <b>rightJoin( 't2', $joinCondition )</b>
*
* Takes 2 string arguments and returns ezcQuery.
*
* The first parameter is the name of the table to join with. The table to
* which is joined should have been previously set with the from() method.
*
* The second parameter should be a string containing a join condition that
* is returned by an ezcQueryExpression.
*
* Example:
* <code>
* // the following code will produce the SQL
* // SELECT id FROM t1 LEFT JOIN t2 ON t1.id = t2.id
* $q->select( 'id' )->from( 't1' )->rightJoin( 't2', $q->expr->eq('t1.id', 't2.id' ) );
* </code>
*
* <b>rightJoin( 't2', 't1.id', 't2.id' )</b>
*
* Takes 3 string arguments and returns ezcQuery. This is a simplified form
* of the 2 parameter version. rightJoin( 't2', 't1.id', 't2.id' ) is
* equal to rightJoin( 't2', $this->expr->eq('t1.id', 't2.id' ) );
*
* The first parameter is the name of the table to join with. The table to
* which is joined should have been previously set with the from() method.
*
* The second parameter is the name of the column on the table set
* previously with the from() method and the third parameter the name of
* the column to join with on the table that was specified in the first
* parameter.
*
* Example:
* <code>
* // the following code will produce the SQL
* // SELECT id FROM t1 LEFT JOIN t2 ON t1.id = t2.id
* $q->select( 'id' )->from( 't1' )->rightJoin( 't2', 't1.id', 't2.id' );
* </code>
*
* @apichange Remove 4 argument version.
*
* @throws ezcQueryInvalidException if called with inconsistent parameters or if
* invoked without preceding call to from().
*
* @param string $table2,... The table to join with, followed by either the
* two join columns, or a join condition.
* @return ezcQuery
*/
public function rightJoin()
{
$args = func_get_args();
$passedArgsCount = func_num_args();
if ( $passedArgsCount < 2 || $passedArgsCount > 4 )
{
throw new ezcQueryInvalidException( 'SELECT', "Wrong argument count passed to {$type}Join(): {$passedArgsCount}" );
}
// deprecated syntax
if ( $passedArgsCount == 4 )
{
if ( is_string( $args[0] ) && is_string( $args[1] ) &&
is_string( $args[2] ) && is_string( $args[3] )
)
{
$table1 = $this->getIdentifier( $args[0] );
$table2 = $this->getIdentifier( $args[1] );
$column1 = $this->getIdentifier( $args[2] );
$column2 = $this->getIdentifier( $args[3] );
return "{$table2} LEFT JOIN {$table1} ON {$column1} = {$column2}";
}
else
{
throw new ezcQueryInvalidException( 'SELECT', 'Inconsistent types of arguments passed to rightJoin().' );
}
}
// using from()->rightJoin() syntax assumed, so check if last call was to from()
if ( $this->lastInvokedMethod != 'from' )
{
throw new ezcQueryInvalidException( 'SELECT', 'Invoking rightJoin() not immediately after from().' );
}
$table = '';
if ( !is_string( $args[0] ) )
{
throw new ezcQueryInvalidException( 'SELECT',
'Inconsistent type of first argument passed to rightJoin(). Should be string with name of table.' );
}
$table = $this->getIdentifier( $args[0] );
$condition = '';
if ( $passedArgsCount == 2 && is_string( $args[1] ) )
{
$condition = $args[1];
}
else if ( $passedArgsCount == 3 && is_string( $args[1] ) && is_string( $args[2] ) )
{
$arg1 = $this->getIdentifier( $args[1] );
$arg2 = $this->getIdentifier( $args[2] );
$condition = "{$arg1} = {$arg2}";
}
// If rightJoin info entry is empty than remove last table from
// fromTables list and add it at first place to the list of tables in
// correspondent rightJoin info entry.
// Subsequent calls to rightJoin() without from() will just add one
// table and one condition to the correspondent arrays.
if ( end( $this->rightJoins ) === null ) // fill last rightJoin info entry with table name.
{
$lastTable = array_pop ( $this->fromTables );
array_pop( $this->rightJoins );
$this->rightJoins[count( $this->rightJoins )]['tables'][] = $lastTable;
}
if ( $table != '' && $condition != '' )
{
$this->rightJoins[count( $this->rightJoins ) - 1]['tables'][] = $table;
$this->rightJoins[count( $this->rightJoins ) - 1]['conditions'][] = $condition;
}
// build fromString using fromTables and add right joins stuff to te end.
$this->fromString = 'FROM ' . join( ', ', $this->fromTables );
$this->fromString .= $this->buildRightJoins();
return $this;
}
}
?>