Project

General

Profile

Feature #23374 » 15457_dbal.diff

Administrator Admin, 2010-08-14 19:13

View differences:

class.ux_t3lib_db.php (working copy)
return $query;
}
/**************************************
*
* Prepared Query Support
*
**************************************/
/**
* Creates a SELECT prepared SQL statement.
*
* @param string See exec_SELECTquery()
* @param string See exec_SELECTquery()
* @param string See exec_SELECTquery()
* @param string See exec_SELECTquery()
* @param string See exec_SELECTquery()
* @param string See exec_SELECTquery()
* @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as t3lib_db_PreparedStatement::PARAM_AUTOTYPE.
* @return t3lib_db_PreparedStatement Prepared statement
*/
public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) {
if ($this->debug) {
$pt = t3lib_div::milliseconds();
}
// Map table / field names if needed:
$ORIG_tableName = $from_table; // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
$parsedFromTable = array();
$queryComponents = array();
if ($tableArray = $this->map_needMapping($ORIG_tableName, FALSE, $parsedFromTable)) {
$from = $parsedFromTable ? $parsedFromTable : $from_table;
$queryComponents = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
} else {
$from = $from_table;
$queryComponents = $this->getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
}
$queryComponents['ORIG_tableName'] = $ORIG_tableName;
if (!$this->runningNative()) {
// Quotes all fields
$queryComponents['SELECT'] = $this->_quoteFieldNames($queryComponents['SELECT']);
$queryComponents['FROM'] = $this->_quoteFromTables($queryComponents['FROM']);
$queryComponents['WHERE'] = $this->_quoteWhereClause($queryComponents['WHERE']);
$queryComponents['GROUPBY'] = $this->_quoteGroupBy($queryComponents['GROUPBY']);
$queryComponents['ORDERBY'] = $this->_quoteOrderBy($queryComponents['ORDERBY']);
}
// TODO: Find a way to already compile the query into a string while preserving the information
// where each parameter appears. This will prevent compiling and compiling again the
// query in method stmt_execute() and will allow caching of the query into Memcached and
// as such completely bypass the parsing of the already seen before SELECT query.
$stmt = t3lib_div::makeInstance('t3lib_db_PreparedStatement', '', $from, $queryComponents);
/* @var $stmt t3lib_db_PreparedStatement */
// Bind values to parameters
foreach ($input_parameters as $key => $value) {
$stmt->bindValue($key, $value, t3lib_db_PreparedStatement::PARAM_AUTOTYPE);
}
if ($this->debug) {
$data = array(
'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters),
'ORIG_from_table' => $ORIG_tableName,
);
$this->debugHandler(
'prepare_SELECTquery',
t3lib_div::milliseconds() - $pt,
$data
);
}
// Return prepared statement
return $stmt;
}
/**
* Returns the parsed query components.
*
* @param string $select_fields
* @param string $from_table
* @param string $where_clause
* @param string $groupBy
* @param string $orderBy
* @param string $limit
* @return array
*/
protected function getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit) {
$queryComponents = array(
'SELECT' => '',
'FROM' => '',
'WHERE' => '',
'GROUPBY' => '',
'ORDERBY' => '',
'LIMIT' => '',
'parameters' => array(),
);
$this->lastHandlerKey = $this->handler_getFromTableList($from_table);
$hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) {
// Possibly rewrite the LIMIT to be PostgreSQL-compatible
$splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values:
if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise:
$numrows = $splitLimit[1];
$offset = $splitLimit[0];
$limit = $numrows . ' OFFSET ' . $offset;
}
}
$queryComponents['LIMIT'] = $limit;
$queryComponents['SELECT'] = $this->SQLparser->parseFieldList($select_fields);
if ($this->SQLparser->parse_error) {
die($this->SQLparser->parse_error . ' in ' . __FILE__ . ' : ' . __LINE__);
}
$queryComponents['FROM'] = $this->SQLparser->parseFromTables($from_table);
$queryComponents['WHERE'] = $this->SQLparser->parseWhereClause($where_clause, '', $queryComponents['parameters']);
if (!is_array($queryComponents['WHERE'])) {
die('Could not parse where clause in ' . __FILE__ . ' : ' . __LINE__);
}
$queryComponents['GROUPBY'] = $this->SQLparser->parseFieldList($groupBy);
$queryComponents['ORDERBY'] = $this->SQLparser->parseFieldList($orderBy);
// Return the query components
return $queryComponents;
}
/**
* Executes a prepared query.
*
* @param string $query The query to execute
* @param array $queryComponents The components of the query to execute
* @return pointer MySQL result pointer / DBAL object
* @access protected This method may only be called by t3lib_db_PreparedStatement
*/
public function stmt_execute($query, array $queryComponents) {
if ($this->debug) {
$pt = t3lib_div::milliseconds();
}
$select_fields = $this->SQLparser->compileFieldList($queryComponents['SELECT']);
$from_table = $this->SQLparser->compileFromTables($queryComponents['FROM']);
$where_clause = $this->SQLparser->compileWhereClause($queryComponents['WHERE']);
$groupBy = $this->SQLparser->compileFieldList($queryComponents['GROUPBY']);
$orderBy = $this->SQLparser->compileFieldList($queryComponents['ORDERBY']);
$limit = $queryComponents['LIMIT'];
$ORIG_tableName = $queryComponents['ORIG_tableName'];
// Get handler key and select API:
$this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
$hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
switch ($hType) {
case 'native':
$this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
$sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
$this->resourceIdToTableNameMap[(string)$sqlResult] = $ORIG_tableName;
break;
case 'adodb':
if ($limit != '') {
$splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values:
if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise:
$numrows = $splitLimit[1];
$offset = $splitLimit[0];
} else {
$numrows = $splitLimit[0];
$offset = 0;
}
$sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy), $numrows, $offset);
$this->lastQuery = $sqlResult->sql;
} else {
$this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
$sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery);
}
$sqlResult->TYPO3_DBAL_handlerType = 'adodb'; // Setting handler type in result object (for later recognition!)
$sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
break;
case 'userdefined':
$sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
if (is_object($sqlResult)) {
$sqlResult->TYPO3_DBAL_handlerType = 'userdefined'; // Setting handler type in result object (for later recognition!)
$sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
}
break;
}
if ($this->printErrors && $this->sql_error()) {
debug(array($this->lastQuery, $this->sql_error()));
}
if ($this->debug) {
$data = array(
'handlerType' => $hType,
'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit),
'ORIG_from_table' => $ORIG_tableName,
);
if ($this->conf['debugOptions']['numberRows']) {
$data['numberRows'] = $this->sql_num_rows($sqlResult);
}
$this->debugHandler(
'stmt_execute',
t3lib_div::milliseconds() - $pt,
$data
);
}
// Return result handler.
return $sqlResult;
}
/**************************************
*
* Functions for quoting table/field names
tests/sqlParserGeneralTest.php (working copy)
$this->assertEquals($expected, $actual);
}
///////////////////////////////////////
// Tests concerning prepared queries
///////////////////////////////////////
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function namedPlaceholderIsSupported() {
$sql = 'SELECT * FROM pages WHERE pid = :pid ORDER BY title';
$expected = 'SELECT * FROM pages WHERE pid = :pid ORDER BY title';
$actual = $this->cleanSql($this->fixture->debug_testSQL($sql));
$this->assertEquals($expected, $actual);
}
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function questionMarkPlaceholderIsSupported() {
$sql = 'SELECT * FROM pages WHERE pid = ? ORDER BY title';
$expected = 'SELECT * FROM pages WHERE pid = ? ORDER BY title';
$actual = $this->cleanSql($this->fixture->debug_testSQL($sql));
$this->assertEquals($expected, $actual);
}
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function parametersAreReferenced() {
$sql = 'SELECT * FROM pages WHERE pid = :pid1 OR pid = :pid2';
$components = $this->fixture->_callRef('parseSELECT', $sql);
$this->assertTrue(is_array($components['parameters']), 'References to parameters not found');
$this->assertEquals(2, count($components['parameters']));
$this->assertTrue(is_array($components['parameters']), 'References to parameters not found');
$this->assertTrue(isset($components['parameters'][':pid1']));
$this->assertTrue(isset($components['parameters'][':pid2']));
}
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function sameParameterIsReferencedInSubquery() {
$sql = 'SELECT * FROM pages WHERE uid = :pageId OR uid IN (SELECT uid FROM pages WHERE pid = :pageId)';
$pageId = 12;
$components = $this->fixture->_callRef('parseSELECT', $sql);
$components['parameters'][':pageId'][0] = $pageId;
$query = $this->cleanSql($this->fixture->_callRef('compileSELECT', $components));
$expected = 'SELECT * FROM pages WHERE uid = 12 OR uid IN (SELECT uid FROM pages WHERE pid = 12)';
$this->assertEquals($expected, $query);
}
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function namedParametersMayBeSafelyReplaced() {
$sql = 'SELECT * FROM pages WHERE pid = :pid AND title NOT LIKE \':pid\'';
$pid = 12;
$components = $this->fixture->_callRef('parseSELECT', $sql);
$components['parameters'][':pid'][0] = $pid;
$query = $this->cleanSql($this->fixture->_callRef('compileSELECT', $components));
$expected = 'SELECT * FROM pages WHERE pid = ' . $pid . ' AND title NOT LIKE \':pid\'';
$this->assertEquals($expected, $query);
}
/**
* @test
* @see http://bugs.typo3.org/view.php?id=15457
*/
public function questionMarkParametersMayBeSafelyReplaced() {
$sql = 'SELECT * FROM pages WHERE pid = ? AND timestamp < ? AND title != \'How to test?\'';
$parameterValues = array(12, 1281782690);
$components = $this->fixture->_callRef('parseSELECT', $sql);
for ($i = 0; $i < count($components['parameters']['?']); $i++) {
$components['parameters']['?'][$i][0] = $parameterValues[$i];
}
$query = $this->cleanSql($this->fixture->_callRef('compileSELECT', $components));
$expected = 'SELECT * FROM pages WHERE pid = 12 AND timestamp < 1281782690 AND title != \'How to test?\'';
$this->assertEquals($expected, $query);
}
}
?>
(1-1/5)