|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2004-2009 Kasper Skårhøj (kasperYYYY@typo3.com) 00006 * (c) 2004-2009 Karsten Dambekalns <karsten@typo3.org> 00007 * (c) 2009-2010 Xavier Perseguers <typo3@perseguers.ch> 00008 * All rights reserved 00009 * 00010 * This script is part of the TYPO3 project. The TYPO3 project is 00011 * free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * The GNU General Public License can be found at 00017 * http://www.gnu.org/copyleft/gpl.html. 00018 * A copy is found in the textfile GPL.txt and important notices to the license 00019 * from the author is found in LICENSE.txt distributed with these scripts. 00020 * 00021 * 00022 * This script is distributed in the hope that it will be useful, 00023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00025 * GNU General Public License for more details. 00026 * 00027 * This copyright notice MUST APPEAR in all copies of the script! 00028 ***************************************************************/ 00029 /** 00030 * Contains a database abstraction layer class for TYPO3 00031 * 00032 * $Id: class.ux_t3lib_db.php 42596 2011-01-25 20:02:07Z xperseguers $ 00033 * 00034 * @author Kasper Skårhøj <kasper@typo3.com> 00035 * @author Karsten Dambekalns <k.dambekalns@fishfarm.de> 00036 * @author Xavier Perseguers <typo3@perseguers.ch> 00037 */ 00038 /** 00039 * [CLASS/FUNCTION INDEX of SCRIPT] 00040 * 00041 * 00042 * 00043 * 161: class ux_t3lib_DB extends t3lib_DB 00044 * 229: public function __construct() 00045 * 260: protected function initInternalVariables() 00046 * 283: public function clearCachedFieldInfo() 00047 * 294: public function cacheFieldInfo() 00048 * 342: protected function analyzeFields($parsedExtSQL) 00049 * 375: protected function mapCachedFieldInfo($fieldInfo) 00050 * 00051 * SECTION: Query Building (Overriding parent methods) 00052 * 438: public function exec_INSERTquery($table, $fields_values, $no_quote_fields = '') 00053 * 575: public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) 00054 * 600: public function exec_UPDATEquery($table,$where,$fields_values,$no_quote_fields = '') 00055 * 692: public function exec_DELETEquery($table, $where) 00056 * 759: public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') 00057 * 856: public function exec_TRUNCATEquery($table) 00058 * 914: protected function exec_query(array $queryParts) 00059 * 00060 * SECTION: Query building 00061 * 978: public function INSERTquery($table, $fields_values, $no_quote_fields = '') 00062 * 1052: public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) 00063 * 1085: public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = '') 00064 * 1170: public function DELETEquery($table, $where) 00065 * 1196: public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') 00066 * 1229: protected function SELECTqueryFromArray(array $params) 00067 * 1267: protected function compileSelectParameters(array $params) 00068 * 1283: public function TRUNCATEquery($table) 00069 * 00070 * SECTION: Prepared Query Support 00071 * 1314: public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) 00072 * 1416: protected function getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit) 00073 * 1465: protected function precompileSELECTquery(array $components) 00074 * 1523: public function exec_PREPAREDquery($query, array $precompiledParts) 00075 * 00076 * SECTION: Functions for quoting table/field names 00077 * 1619: protected function quoteSELECTsubquery(array $components) 00078 * 1634: public function quoteSelectFields($select_fields) 00079 * 1644: public function quoteFieldNames($select_fields) 00080 * 1664: protected function _quoteFieldNames(array $select_fields) 00081 * 1701: public function quoteFromTables($from_table) 00082 * 1717: protected function _quoteFromTables(array $from_table) 00083 * 1746: public function quoteWhereClause($where_clause) 00084 * 1767: protected function _quoteWhereClause(array $where_clause) 00085 * 1843: protected function quoteGroupBy($groupBy) 00086 * 1860: protected function _quoteGroupBy(array $groupBy) 00087 * 1877: protected function quoteOrderBy($orderBy) 00088 * 1894: protected function _quoteOrderBy(array $orderBy) 00089 * 00090 * SECTION: Various helper functions 00091 * 1919: public function fullQuoteStr($str, $table) 00092 * 1932: public function quoteStr($str, $table) 00093 * 1965: public function quoteName($name, $handlerKey = NULL, $useBackticks = FALSE) 00094 * 1984: public function MetaType($type, $table, $max_length = -1) 00095 * 2015: public function MySQLMetaType($t) 00096 * 2062: public function MySQLActualType($meta) 00097 * 00098 * SECTION: SQL wrapper functions (Overriding parent methods) 00099 * 2105: public function sql_error() 00100 * 2125: public function sql_errno() 00101 * 2146: public function sql_num_rows(&$res) 00102 * 2170: public function sql_fetch_assoc(&$res) 00103 * 2233: public function sql_fetch_row(&$res) 00104 * 2279: public function sql_free_result(&$res) 00105 * 2308: public function sql_insert_id() 00106 * 2328: public function sql_affected_rows() 00107 * 2350: public function sql_data_seek(&$res, $seek) 00108 * 2375: public function sql_field_metatype($table, $field) 00109 * 2405: public function sql_field_type(&$res,$pointer) 00110 * 00111 * SECTION: Legacy functions, bound to _DEFAULT handler. (Overriding parent methods) 00112 * 2459: public function sql($db,$query) 00113 * 2477: public function sql_query($query) 00114 * 2516: public function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password) 00115 * 2534: public function sql_select_db($TYPO3_db) 00116 * 00117 * SECTION: SQL admin functions 00118 * 2566: public function admin_get_dbs() 00119 * 2607: public function admin_get_tables() 00120 * 2673: public function admin_get_fields($tableName) 00121 * 2742: public function admin_get_keys($tableName) 00122 * 2847: public function admin_get_charsets() 00123 * 2857: public function admin_query($query) 00124 * 00125 * SECTION: Handler management 00126 * 2941: public function handler_getFromTableList($tableList) 00127 * 2989: public function handler_init($handlerKey) 00128 * 3107: public function isConnected() 00129 * 3127: public function runningNative() 00130 * 3138: public function runningADOdbDriver($driver) 00131 * 00132 * SECTION: Table/Field mapping 00133 * 3165: protected function map_needMapping($tableList, $fieldMappingOnly = FALSE, array &$parsedTableList = array()) 00134 * 3215: protected function map_assocArray($input, $tables, $rev = FALSE) 00135 * 3263: protected function map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy) 00136 * 3357: protected function getMappingKey($tableName) 00137 * 3371: protected function getFreeMappingKey($tableName) 00138 * 3387: protected function map_sqlParts(&$sqlPartArray, $defaultTable) 00139 * 3549: protected function map_subquery(&$parsedQuery) 00140 * 3589: protected function map_genericQueryParsed(&$parsedQuery) 00141 * 3654: protected function map_fieldNamesInArray($table,&$fieldArray) 00142 * 00143 * SECTION: Debugging 00144 * 3695: public function debugHandler($function,$execTime,$inData) 00145 * 3790: public function debug_WHERE($table, $where, $script = '') 00146 * 3813: public function debug_log($query,$ms,$data,$join,$errorFlag, $script='') 00147 * 3846: public function debug_explain($query) 00148 * 00149 * TOTAL FUNCTIONS: 82 00150 * (This index is automatically created/updated by the extension "extdeveval") 00151 * 00152 */ 00153 /** 00154 * TYPO3 database abstraction layer 00155 * 00156 * @author Kasper Skårhøj <kasper@typo3.com> 00157 * @author Karsten Dambekalns <k.dambekalns@fishfarm.de> 00158 * @package TYPO3 00159 * @subpackage tx_dbal 00160 */ 00161 class ux_t3lib_DB extends t3lib_DB { 00162 00163 // Internal, static: 00164 var $printErrors = FALSE; // Enable output of SQL errors after query executions. Set through TYPO3_CONF_VARS, see init() 00165 var $debug = FALSE; // Enable debug mode. Set through TYPO3_CONF_VARS, see init() 00166 var $conf = array(); // Configuration array, copied from TYPO3_CONF_VARS in constructor. 00167 00168 var $mapping = array(); // See manual. 00169 var $table2handlerKeys = array(); // See manual. 00170 var $handlerCfg = array( // See manual. 00171 '_DEFAULT' => array( 00172 'type' => 'native', 00173 'config' => array( 00174 'username' => '', // Set by default (overridden) 00175 'password' => '', // Set by default (overridden) 00176 'host' => '', // Set by default (overridden) 00177 'database' => '', // Set by default (overridden) 00178 'driver' => '', // ONLY "adodb" type; eg. "mysql" 00179 'sequenceStart' => 1, // ONLY "adodb", first number in sequences/serials/... 00180 'useNameQuote' => 0 // ONLY "adodb", whether to use NameQuote() method from ADOdb to quote names 00181 ) 00182 ), 00183 ); 00184 00185 00186 // Internal, dynamic: 00187 var $handlerInstance = array(); // Contains instance of the handler objects as they are created. Exception is the native mySQL calls which are registered as an array with keys "handlerType" = "native" and "link" pointing to the link resource for the connection. 00188 var $lastHandlerKey = ''; // Storage of the handler key of last ( SELECT) query - used for subsequent fetch-row calls etc. 00189 var $lastQuery = ''; // Storage of last SELECT query 00190 var $lastParsedAndMappedQueryArray = array(); // Query array, the last one parsed 00191 00192 var $resourceIdToTableNameMap = array(); // Mapping of resource ids to table names. 00193 00194 // Internal, caching: 00195 var $cache_handlerKeyFromTableList = array(); // Caching handlerKeys for table lists 00196 var $cache_mappingFromTableList = array(); // Caching mapping information for table lists 00197 var $cache_autoIncFields = array(); // parsed SQL from standard DB dump file 00198 var $cache_fieldType = array(); // field types for tables/fields 00199 var $cache_primaryKeys = array(); // primary keys 00200 00201 /** 00202 * SQL parser 00203 * 00204 * @var tx_dbal_sqlengine 00205 */ 00206 var $SQLparser; 00207 00208 /** 00209 * Installer 00210 * 00211 * @var t3lib_install 00212 */ 00213 var $Installer; 00214 00215 /** 00216 * Cache for queries 00217 * 00218 * @var t3lib_cache_frontend_VariableFrontend 00219 */ 00220 protected $queryCache; 00221 00222 00223 /** 00224 * Constructor. 00225 * Creates SQL parser object and imports configuration from $TYPO3_CONF_VARS['EXTCONF']['dbal'] 00226 */ 00227 public function __construct() { 00228 // Set SQL parser object for internal use: 00229 $this->SQLparser = t3lib_div::makeInstance('tx_dbal_sqlengine'); 00230 $this->Installer = t3lib_div::makeInstance('t3lib_install'); 00231 00232 if (TYPO3_UseCachingFramework) { 00233 tx_dbal_querycache::initializeCachingFramework(); 00234 00235 try { 00236 $this->queryCache = $GLOBALS['typo3CacheManager']->getCache( 00237 'dbal' 00238 ); 00239 } catch (t3lib_cache_exception_NoSuchCache $e) { 00240 tx_dbal_querycache::initDbalCache(); 00241 00242 $this->queryCache = $GLOBALS['typo3CacheManager']->getCache( 00243 'dbal' 00244 ); 00245 } 00246 } 00247 00248 // Set internal variables with configuration: 00249 $this->conf = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']; 00250 $this->initInternalVariables(); 00251 } 00252 00253 /** 00254 * Setting internal variables from $this->conf. 00255 * 00256 * @return void 00257 */ 00258 protected function initInternalVariables() { 00259 // Set outside configuration: 00260 if (isset($this->conf['mapping'])) { 00261 $this->mapping = $this->conf['mapping']; 00262 } 00263 if (isset($this->conf['table2handlerKeys'])) { 00264 $this->table2handlerKeys = $this->conf['table2handlerKeys']; 00265 } 00266 if (isset($this->conf['handlerCfg'])) { 00267 $this->handlerCfg = $this->conf['handlerCfg']; 00268 } 00269 00270 $this->cacheFieldInfo(); 00271 // Debugging settings: 00272 $this->printErrors = $this->conf['debugOptions']['printErrors'] ? TRUE : FALSE; 00273 $this->debug = $this->conf['debugOptions']['enabled'] ? TRUE : FALSE; 00274 } 00275 00276 /** 00277 * Clears the cached field information file. 00278 * 00279 * @return void 00280 */ 00281 public function clearCachedFieldInfo() { 00282 if (file_exists(PATH_typo3conf . 'temp_fieldInfo.php')) { 00283 unlink(PATH_typo3conf . 'temp_fieldInfo.php'); 00284 } 00285 } 00286 00287 /** 00288 * Caches the field information. 00289 * 00290 * @return void 00291 */ 00292 public function cacheFieldInfo() { 00293 $extSQL = ''; 00294 $parsedExtSQL = array(); 00295 00296 // try to fetch cached file first 00297 // file is removed when admin_query() is called 00298 if (file_exists(PATH_typo3conf . 'temp_fieldInfo.php')) { 00299 $fdata = unserialize(t3lib_div::getUrl(PATH_typo3conf . 'temp_fieldInfo.php')); 00300 $this->cache_autoIncFields = $fdata['incFields']; 00301 $this->cache_fieldType = $fdata['fieldTypes']; 00302 $this->cache_primaryKeys = $fdata['primaryKeys']; 00303 } else { 00304 // handle stddb.sql, parse and analyze 00305 $extSQL = t3lib_div::getUrl(PATH_site . 't3lib/stddb/tables.sql'); 00306 $parsedExtSQL = $this->Installer->getFieldDefinitions_fileContent($extSQL); 00307 $this->analyzeFields($parsedExtSQL); 00308 00309 // loop over all installed extensions 00310 foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $ext => $v) { 00311 if (!is_array($v) || !isset($v['ext_tables.sql'])) { 00312 continue; 00313 } 00314 00315 // fetch db dump (if any) and parse it, then analyze 00316 $extSQL = t3lib_div::getUrl($v['ext_tables.sql']); 00317 $parsedExtSQL = $this->Installer->getFieldDefinitions_fileContent($extSQL); 00318 $this->analyzeFields($parsedExtSQL); 00319 } 00320 00321 $cachedFieldInfo = array('incFields' => $this->cache_autoIncFields, 'fieldTypes' => $this->cache_fieldType, 'primaryKeys' => $this->cache_primaryKeys); 00322 $cachedFieldInfo = serialize($this->mapCachedFieldInfo($cachedFieldInfo)); 00323 00324 // write serialized content to file 00325 t3lib_div::writeFile(PATH_typo3conf . 'temp_fieldInfo.php', $cachedFieldInfo); 00326 00327 if (strcmp(t3lib_div::getUrl(PATH_typo3conf . 'temp_fieldInfo.php'), $cachedFieldInfo)) { 00328 die('typo3conf/temp_fieldInfo.php was NOT updated properly (written content didn\'t match file content) - maybe write access problem?'); 00329 } 00330 } 00331 } 00332 00333 /** 00334 * Analyzes fields and adds the extracted information to the field type, auto increment and primary key info caches. 00335 * 00336 * @param array $parsedExtSQL The output produced by t3lib_install::getFieldDefinitions_fileContent() 00337 * @return void 00338 * @see t3lib_install::getFieldDefinitions_fileContent() 00339 */ 00340 protected function analyzeFields($parsedExtSQL) { 00341 foreach ($parsedExtSQL as $table => $tdef) { 00342 if (is_array($tdef['fields'])) { 00343 foreach ($tdef['fields'] as $field => $fdef) { 00344 $fdef = $this->SQLparser->parseFieldDef($fdef); 00345 $this->cache_fieldType[$table][$field]['type'] = $fdef['fieldType']; 00346 $this->cache_fieldType[$table][$field]['metaType'] = $this->MySQLMetaType($fdef['fieldType']); 00347 $this->cache_fieldType[$table][$field]['notnull'] = (isset($fdef['featureIndex']['NOTNULL']) && !$this->SQLparser->checkEmptyDefaultValue($fdef['featureIndex'])) ? 1 : 0; 00348 if (isset($fdef['featureIndex']['DEFAULT'])) { 00349 $default = $fdef['featureIndex']['DEFAULT']['value'][0]; 00350 if (isset($fdef['featureIndex']['DEFAULT']['value'][1])) { 00351 $default = $fdef['featureIndex']['DEFAULT']['value'][1] . $default . $fdef['featureIndex']['DEFAULT']['value'][1]; 00352 } 00353 $this->cache_fieldType[$table][$field]['default'] = $default; 00354 } 00355 if (isset($fdef['featureIndex']['AUTO_INCREMENT'])) { 00356 $this->cache_autoIncFields[$table] = $field; 00357 } 00358 if (isset($tdef['keys']['PRIMARY'])) { 00359 $this->cache_primaryKeys[$table] = substr($tdef['keys']['PRIMARY'], 13, -1); 00360 } 00361 } 00362 } 00363 } 00364 } 00365 00366 /** 00367 * This function builds all definitions for mapped tables and fields 00368 * @see cacheFieldInfo() 00369 */ 00370 protected function mapCachedFieldInfo($fieldInfo) { 00371 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'])) { 00372 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'] as $mappedTable => $mappedConf) { 00373 if (array_key_exists($mappedTable, $fieldInfo['incFields'])) { 00374 $mappedTableAlias = $mappedConf['mapTableName']; 00375 if (isset($mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]])) { 00376 $fieldInfo['incFields'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]]; 00377 } else { 00378 $fieldInfo['incFields'][$mappedTableAlias] = $fieldInfo['incFields'][$mappedTable]; 00379 } 00380 } 00381 00382 if (array_key_exists($mappedTable, $fieldInfo['fieldTypes'])) { 00383 foreach ($fieldInfo['fieldTypes'][$mappedTable] as $field => $fieldConf) { 00384 $tempMappedFieldConf[$mappedConf['mapFieldNames'][$field]] = $fieldConf; 00385 } 00386 00387 $fieldInfo['fieldTypes'][$mappedConf['mapTableName']] = $tempMappedFieldConf; 00388 } 00389 00390 if (array_key_exists($mappedTable, $fieldInfo['primaryKeys'])) { 00391 $mappedTableAlias = $mappedConf['mapTableName']; 00392 if (isset($mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]])) { 00393 $fieldInfo['primaryKeys'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]]; 00394 } else { 00395 $fieldInfo['primaryKeys'][$mappedTableAlias] = $fieldInfo['primaryKeys'][$mappedTable]; 00396 } 00397 } 00398 } 00399 } 00400 00401 return $fieldInfo; 00402 } 00403 00404 00405 /************************************ 00406 * 00407 * Query Building (Overriding parent methods) 00408 * These functions are extending counterparts in the parent class. 00409 * 00410 **************************************/ 00411 00412 /* From the ADOdb documentation, this is what we do (_Execute for SELECT, _query for the other actions) 00413 00414 Execute() is the default way to run queries. You can use the low-level functions _Execute() and _query() to reduce query overhead. 00415 Both these functions share the same parameters as Execute(). 00416 00417 If you do not have any bind parameters or your database supports binding (without emulation), then you can call _Execute() directly. 00418 Calling this function bypasses bind emulation. Debugging is still supported in _Execute(). 00419 00420 If you do not require debugging facilities nor emulated binding, and do not require a recordset to be returned, then you can call _query. 00421 This is great for inserts, updates and deletes. Calling this function bypasses emulated binding, debugging, and recordset handling. Either 00422 the resultid, TRUE or FALSE are returned by _query(). 00423 */ 00424 00425 /** 00426 * Inserts a record for $table from the array with field/value pairs $fields_values. 00427 * 00428 * @param string Table name 00429 * @param array Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$insertFields" with 'fieldname'=>'value' and pass it to this function as argument. 00430 * @param mixed List/array of keys NOT to quote (eg. SQL functions) 00431 * @return mixed Result from handler, usually TRUE when success and FALSE on failure 00432 */ 00433 public function exec_INSERTquery($table, $fields_values, $no_quote_fields = '') { 00434 00435 if ($this->debug) { 00436 $pt = t3lib_div::milliseconds(); 00437 } 00438 00439 // Do field mapping if needed: 00440 $ORIG_tableName = $table; 00441 if ($tableArray = $this->map_needMapping($table)) { 00442 00443 // Field mapping of array: 00444 $fields_values = $this->map_assocArray($fields_values, $tableArray); 00445 00446 // Table name: 00447 if ($this->mapping[$table]['mapTableName']) { 00448 $table = $this->mapping[$table]['mapTableName']; 00449 } 00450 } 00451 // Select API: 00452 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 00453 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 00454 switch ($hType) { 00455 case 'native': 00456 $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields); 00457 if (is_string($this->lastQuery)) { 00458 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 00459 } else { 00460 $sqlResult = mysql_query($this->lastQuery[0], $this->handlerInstance[$this->lastHandlerKey]['link']); 00461 $new_id = $this->sql_insert_id(); 00462 $where = $this->cache_autoIncFields[$table] . '=' . $new_id; 00463 foreach ($this->lastQuery[1] as $field => $content) { 00464 mysql_query('UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table) . ' WHERE ' . $this->quoteWhereClause($where), $this->handlerInstance[$this->lastHandlerKey]['link']); 00465 } 00466 } 00467 break; 00468 case 'adodb': 00469 // auto generate ID for auto_increment fields if not present (static import needs this!) 00470 // should we check the table name here (static_*)? 00471 if (isset($this->cache_autoIncFields[$table])) { 00472 if (isset($fields_values[$this->cache_autoIncFields[$table]])) { 00473 $new_id = $fields_values[$this->cache_autoIncFields[$table]]; 00474 if ($table != 'tx_dbal_debuglog') { 00475 $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id; 00476 } 00477 } else { 00478 $new_id = $this->handlerInstance[$this->lastHandlerKey]->GenID($table . '_' . $this->cache_autoIncFields[$table], $this->handlerInstance[$this->lastHandlerKey]->sequenceStart); 00479 $fields_values[$this->cache_autoIncFields[$table]] = $new_id; 00480 if ($table != 'tx_dbal_debuglog') { 00481 $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id; 00482 } 00483 } 00484 } 00485 00486 $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields); 00487 if (is_string($this->lastQuery)) { 00488 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE); 00489 } else { 00490 $this->handlerInstance[$this->lastHandlerKey]->StartTrans(); 00491 if (strlen($this->lastQuery[0])) { 00492 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE); 00493 } 00494 if (is_array($this->lastQuery[1])) { 00495 foreach ($this->lastQuery[1] as $field => $content) { 00496 if (empty($content)) continue; 00497 00498 if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) { 00499 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id)); 00500 } elseif (isset($this->cache_primaryKeys[$table])) { 00501 $where = ''; 00502 $pks = explode(',', $this->cache_primaryKeys[$table]); 00503 foreach ($pks as $pk) { 00504 if (isset($fields_values[$pk])) 00505 $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND '; 00506 } 00507 $where = $this->quoteWhereClause($where . '1=1'); 00508 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $where); 00509 } else { 00510 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE); 00511 die('Could not update BLOB >>>> no WHERE clause found!'); // should never ever happen 00512 } 00513 } 00514 } 00515 if (is_array($this->lastQuery[2])) { 00516 foreach ($this->lastQuery[2] as $field => $content) { 00517 if (empty($content)) continue; 00518 00519 if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) { 00520 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id)); 00521 } elseif (isset($this->cache_primaryKeys[$table])) { 00522 $where = ''; 00523 $pks = explode(',', $this->cache_primaryKeys[$table]); 00524 foreach ($pks as $pk) { 00525 if (isset($fields_values[$pk])) 00526 $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND '; 00527 } 00528 $where = $this->quoteWhereClause($where . '1=1'); 00529 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $where); 00530 } else { 00531 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE); 00532 die('Could not update CLOB >>>> no WHERE clause found!'); // should never ever happen 00533 } 00534 } 00535 } 00536 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(); 00537 } 00538 break; 00539 case 'userdefined': 00540 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_INSERTquery($table, $fields_values, $no_quote_fields); 00541 break; 00542 } 00543 00544 if ($this->printErrors && $this->sql_error()) { 00545 debug(array($this->lastQuery, $this->sql_error())); 00546 } 00547 00548 if ($this->debug) { 00549 $this->debugHandler( 00550 'exec_INSERTquery', 00551 t3lib_div::milliseconds() - $pt, 00552 array( 00553 'handlerType' => $hType, 00554 'args' => array($table, $fields_values), 00555 'ORIG_tablename' => $ORIG_tableName 00556 ) 00557 ); 00558 } 00559 // Return output: 00560 return $sqlResult; 00561 } 00562 00563 /** 00564 * Creates and executes an INSERT SQL-statement for $table with multiple rows. 00565 * This method uses exec_INSERTquery() and is just a syntax wrapper to it. 00566 * 00567 * @param string Table name 00568 * @param array Field names 00569 * @param array Table rows. Each row should be an array with field values mapping to $fields 00570 * @param string/array See fullQuoteArray() 00571 * @return mixed Result from last handler, usually TRUE when success and FALSE on failure 00572 */ 00573 public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) { 00574 if ((string) $this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') { 00575 return parent::exec_INSERTmultipleRows($table, $fields, $rows, $no_quote_fields); 00576 } 00577 00578 foreach ($rows as $row) { 00579 $fields_values = array(); 00580 foreach ($fields as $key => $value) { 00581 $fields_values[$value] = $row[$key]; 00582 } 00583 $res = $this->exec_INSERTquery($table, $fields_values, $no_quote_fields); 00584 } 00585 00586 return $res; 00587 } 00588 00589 /** 00590 * Updates a record from $table 00591 * 00592 * @param string Database tablename 00593 * @param string WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! 00594 * @param array Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$updateFields" with 'fieldname'=>'value' and pass it to this function as argument. 00595 * @param mixed List/array of keys NOT to quote (eg. SQL functions) 00596 * @return mixed Result from handler, usually TRUE when success and FALSE on failure 00597 */ 00598 public function exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields = '') { 00599 if ($this->debug) { 00600 $pt = t3lib_div::milliseconds(); 00601 } 00602 00603 // Do table/field mapping: 00604 $ORIG_tableName = $table; 00605 if ($tableArray = $this->map_needMapping($table)) { 00606 00607 // Field mapping of array: 00608 $fields_values = $this->map_assocArray($fields_values, $tableArray); 00609 00610 // Where clause table and field mapping: 00611 $whereParts = $this->SQLparser->parseWhereClause($where); 00612 $this->map_sqlParts($whereParts, $tableArray[0]['table']); 00613 $where = $this->SQLparser->compileWhereClause($whereParts, FALSE); 00614 00615 // Table name: 00616 if ($this->mapping[$table]['mapTableName']) { 00617 $table = $this->mapping[$table]['mapTableName']; 00618 } 00619 } 00620 00621 // Select API 00622 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 00623 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 00624 switch ($hType) { 00625 case 'native': 00626 $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields); 00627 if (is_string($this->lastQuery)) { 00628 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 00629 } 00630 else { 00631 $sqlResult = mysql_query($this->lastQuery[0], $this->handlerInstance[$this->lastHandlerKey]['link']); 00632 foreach ($this->lastQuery[1] as $field => $content) { 00633 mysql_query('UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table) . ' WHERE ' . $this->quoteWhereClause($where), $this->handlerInstance[$this->lastHandlerKey]['link']); 00634 } 00635 } 00636 break; 00637 case 'adodb': 00638 $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields); 00639 if (is_string($this->lastQuery)) { 00640 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE); 00641 } else { 00642 $this->handlerInstance[$this->lastHandlerKey]->StartTrans(); 00643 if (strlen($this->lastQuery[0])) { 00644 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE); 00645 } 00646 if (is_array($this->lastQuery[1])) { 00647 foreach ($this->lastQuery[1] as $field => $content) { 00648 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where)); 00649 } 00650 } 00651 if (is_array($this->lastQuery[2])) { 00652 foreach ($this->lastQuery[2] as $field => $content) { 00653 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where)); 00654 } 00655 } 00656 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(); 00657 } 00658 break; 00659 case 'userdefined': 00660 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields); 00661 break; 00662 } 00663 00664 if ($this->printErrors && $this->sql_error()) { 00665 debug(array($this->lastQuery, $this->sql_error())); 00666 } 00667 00668 if ($this->debug) { 00669 $this->debugHandler( 00670 'exec_UPDATEquery', 00671 t3lib_div::milliseconds() - $pt, 00672 array( 00673 'handlerType' => $hType, 00674 'args' => array($table, $where, $fields_values), 00675 'ORIG_from_table' => $ORIG_tableName 00676 ) 00677 ); 00678 } 00679 00680 // Return result: 00681 return $sqlResult; 00682 } 00683 00684 /** 00685 * Deletes records from table 00686 * 00687 * @param string Database tablename 00688 * @param string WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! 00689 * @return mixed Result from handler 00690 */ 00691 public function exec_DELETEquery($table, $where) { 00692 if ($this->debug) { 00693 $pt = t3lib_div::milliseconds(); 00694 } 00695 00696 // Do table/field mapping: 00697 $ORIG_tableName = $table; 00698 if ($tableArray = $this->map_needMapping($table)) { 00699 00700 // Where clause: 00701 $whereParts = $this->SQLparser->parseWhereClause($where); 00702 $this->map_sqlParts($whereParts, $tableArray[0]['table']); 00703 $where = $this->SQLparser->compileWhereClause($whereParts, FALSE); 00704 00705 // Table name: 00706 if ($this->mapping[$table]['mapTableName']) { 00707 $table = $this->mapping[$table]['mapTableName']; 00708 } 00709 } 00710 00711 // Select API 00712 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 00713 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 00714 switch ($hType) { 00715 case 'native': 00716 $this->lastQuery = $this->DELETEquery($table, $where); 00717 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 00718 break; 00719 case 'adodb': 00720 $this->lastQuery = $this->DELETEquery($table, $where); 00721 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE); 00722 break; 00723 case 'userdefined': 00724 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_DELETEquery($table, $where); 00725 break; 00726 } 00727 00728 if ($this->printErrors && $this->sql_error()) { 00729 debug(array($this->lastQuery, $this->sql_error())); 00730 } 00731 00732 if ($this->debug) { 00733 $this->debugHandler( 00734 'exec_DELETEquery', 00735 t3lib_div::milliseconds() - $pt, 00736 array( 00737 'handlerType' => $hType, 00738 'args' => array($table, $where), 00739 'ORIG_from_table' => $ORIG_tableName 00740 ) 00741 ); 00742 } 00743 00744 // Return result: 00745 return $sqlResult; 00746 } 00747 00748 /** 00749 * Selects records from Data Source 00750 * 00751 * @param string $select_fields List of fields to select from the table. This is what comes right after "SELECT ...". Required value. 00752 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value. 00753 * @param string $where_clause Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQquoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT! 00754 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string. 00755 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string. 00756 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string. 00757 * @return mixed Result from handler. Typically object from DBAL layers. 00758 */ 00759 public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') { 00760 if ($this->debug) { 00761 $pt = t3lib_div::milliseconds(); 00762 } 00763 00764 // Map table / field names if needed: 00765 $ORIG_tableName = $from_table; // Saving table names in $ORIG_from_table since $from_table is transformed beneath: 00766 $parsedFromTable = array(); 00767 $remappedParameters = array(); 00768 if ($tableArray = $this->map_needMapping($ORIG_tableName, FALSE, $parsedFromTable)) { 00769 $from = $parsedFromTable ? $parsedFromTable : $from_table; 00770 $remappedParameters = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy); 00771 } 00772 00773 // Get handler key and select API: 00774 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 00775 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 00776 switch ($hType) { 00777 case 'native': 00778 if (count($remappedParameters) > 0) { 00779 list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters); 00780 } 00781 $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit); 00782 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 00783 $this->resourceIdToTableNameMap[(string) $sqlResult] = $ORIG_tableName; 00784 break; 00785 case 'adodb': 00786 if ($limit != '') { 00787 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values: 00788 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise: 00789 $numrows = $splitLimit[1]; 00790 $offset = $splitLimit[0]; 00791 } else { 00792 $numrows = $splitLimit[0]; 00793 $offset = 0; 00794 } 00795 00796 if (count($remappedParameters) > 0) { 00797 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTqueryFromArray($remappedParameters), $numrows, $offset); 00798 } else { 00799 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy), $numrows, $offset); 00800 } 00801 $this->lastQuery = $sqlResult->sql; 00802 } else { 00803 if (count($remappedParameters) > 0) { 00804 $this->lastQuery = $this->SELECTqueryFromArray($remappedParameters); 00805 } else { 00806 $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy); 00807 } 00808 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery); 00809 } 00810 00811 $sqlResult->TYPO3_DBAL_handlerType = 'adodb'; // Setting handler type in result object (for later recognition!) 00812 $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName; 00813 break; 00814 case 'userdefined': 00815 if (count($remappedParameters) > 0) { 00816 list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters); 00817 } 00818 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit); 00819 if (is_object($sqlResult)) { 00820 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined'; // Setting handler type in result object (for later recognition!) 00821 $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName; 00822 } 00823 break; 00824 } 00825 00826 if ($this->printErrors && $this->sql_error()) { 00827 debug(array($this->lastQuery, $this->sql_error())); 00828 } 00829 00830 if ($this->debug) { 00831 $data = array( 00832 'handlerType' => $hType, 00833 'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit), 00834 'ORIG_from_table' => $ORIG_tableName, 00835 ); 00836 if ($this->conf['debugOptions']['numberRows']) { 00837 $data['numberRows'] = $this->sql_num_rows($sqlResult); 00838 } 00839 $this->debugHandler( 00840 'exec_SELECTquery', 00841 t3lib_div::milliseconds() - $pt, 00842 $data 00843 ); 00844 } 00845 00846 // Return result handler. 00847 return $sqlResult; 00848 } 00849 00850 /** 00851 * Truncates a table. 00852 * 00853 * @param string Database tablename 00854 * @return mixed Result from handler 00855 */ 00856 public function exec_TRUNCATEquery($table) { 00857 if ($this->debug) { 00858 $pt = t3lib_div::milliseconds(); 00859 } 00860 00861 // Do table/field mapping: 00862 $ORIG_tableName = $table; 00863 if ($tableArray = $this->map_needMapping($table)) { 00864 // Table name: 00865 if ($this->mapping[$table]['mapTableName']) { 00866 $table = $this->mapping[$table]['mapTableName']; 00867 } 00868 } 00869 00870 // Select API 00871 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 00872 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 00873 switch ($hType) { 00874 case 'native': 00875 $this->lastQuery = $this->TRUNCATEquery($table); 00876 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 00877 break; 00878 case 'adodb': 00879 $this->lastQuery = $this->TRUNCATEquery($table); 00880 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE); 00881 break; 00882 case 'userdefined': 00883 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_TRUNCATEquery($table); 00884 break; 00885 } 00886 00887 if ($this->printErrors && $this->sql_error()) { 00888 debug(array($this->lastQuery, $this->sql_error())); 00889 } 00890 00891 if ($this->debug) { 00892 $this->debugHandler( 00893 'exec_TRUNCATEquery', 00894 t3lib_div::milliseconds() - $pt, 00895 array( 00896 'handlerType' => $hType, 00897 'args' => array($table), 00898 'ORIG_from_table' => $ORIG_tableName 00899 ) 00900 ); 00901 } 00902 00903 // Return result: 00904 return $sqlResult; 00905 } 00906 00907 /** 00908 * Executes a query. 00909 * EXPERIMENTAL since TYPO3 4.4. 00910 * 00911 * @param array $queryParts SQL parsed by method parseSQL() of t3lib_sqlparser 00912 * @return pointer Result pointer / DBAL object 00913 * @see ux_t3lib_db::sql_query() 00914 */ 00915 protected function exec_query(array $queryParts) { 00916 switch ($queryParts['type']) { 00917 case 'SELECT': 00918 $selectFields = $this->SQLparser->compileFieldList($queryParts['SELECT']); 00919 $fromTables = $this->SQLparser->compileFromTables($queryParts['FROM']); 00920 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1'; 00921 $groupBy = isset($queryParts['GROUPBY']) ? $this->SQLparser->compileFieldList($queryParts['GROUPBY']) : ''; 00922 $orderBy = isset($queryParts['ORDERBY']) ? $this->SQLparser->compileFieldList($queryParts['ORDERBY']) : ''; 00923 $limit = isset($queryParts['LIMIT']) ? $this->SQLparser->compileWhereClause($queryParts['LIMIT']) : ''; 00924 return $this->exec_SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy, $limit); 00925 00926 case 'UPDATE': 00927 $table = $queryParts['TABLE']; 00928 $fields = array(); 00929 foreach ($queryParts['FIELDS'] as $fN => $fV) { 00930 $fields[$fN] = $fV[0]; 00931 } 00932 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1'; 00933 return $this->exec_UPDATEquery($table, $whereClause, $fields); 00934 00935 case 'INSERT': 00936 $table = $queryParts['TABLE']; 00937 $values = array(); 00938 if (isset($queryParts['VALUES_ONLY']) && is_array($queryParts['VALUES_ONLY'])) { 00939 $fields = $GLOBALS['TYPO3_DB']->cache_fieldType[$table]; 00940 $fc = 0; 00941 foreach ($fields as $fn => $fd) { 00942 $values[$fn] = $queryParts['VALUES_ONLY'][$fc++][0]; 00943 } 00944 } else { 00945 foreach ($queryParts['FIELDS'] as $fN => $fV) { 00946 $values[$fN] = $fV[0]; 00947 } 00948 } 00949 return $this->exec_INSERTquery($table, $values); 00950 00951 case 'DELETE': 00952 $table = $queryParts['TABLE']; 00953 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1'; 00954 return $this->exec_DELETEquery($table, $whereClause); 00955 00956 case 'TRUNCATETABLE': 00957 $table = $queryParts['TABLE']; 00958 return $this->exec_TRUNCATEquery($table); 00959 } 00960 } 00961 00962 00963 /************************************** 00964 * 00965 * Query building 00966 * 00967 **************************************/ 00968 00969 /** 00970 * Creates an INSERT SQL-statement for $table from the array with field/value pairs $fields_values. 00971 * Usage count/core: 4 00972 * 00973 * @param string See exec_INSERTquery() 00974 * @param array See exec_INSERTquery() 00975 * @param mixed See exec_INSERTquery() 00976 * @return mixed Full SQL query for INSERT as string or array (unless $fields_values does not contain any elements in which case it will be FALSE). If BLOB fields will be affected and one is not running the native type, an array will be returned, where 0 => plain SQL, 1 => fieldname/value pairs of BLOB fields 00977 */ 00978 public function INSERTquery($table, $fields_values, $no_quote_fields = '') { 00979 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure). 00980 if (is_array($fields_values) && count($fields_values)) { 00981 00982 if (is_string($no_quote_fields)) { 00983 $no_quote_fields = explode(',', $no_quote_fields); 00984 } elseif (!is_array($no_quote_fields)) { 00985 $no_quote_fields = array(); 00986 } 00987 00988 $blobfields = array(); 00989 $nArr = array(); 00990 foreach ($fields_values as $k => $v) { 00991 if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') { 00992 // we skip the field in the regular INSERT statement, it is only in blobfields 00993 $blobfields[$this->quoteFieldNames($k)] = $v; 00994 } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') { 00995 // we skip the field in the regular INSERT statement, it is only in clobfields 00996 $clobfields[$this->quoteFieldNames($k)] = $v; 00997 } else { 00998 // Add slashes old-school: 00999 // cast numerical values 01000 $mt = $this->sql_field_metatype($table, $k); 01001 if ($mt{0} == 'I') { 01002 $v = (int) $v; 01003 } else if ($mt{0} == 'F') { 01004 $v = (double) $v; 01005 } 01006 01007 $nArr[$this->quoteFieldNames($k)] = (!in_array($k, $no_quote_fields)) ? $this->fullQuoteStr($v, $table) : $v; 01008 } 01009 } 01010 01011 if (count($blobfields) || count($clobfields)) { 01012 if (count($nArr)) { 01013 $query[0] = 'INSERT INTO ' . $this->quoteFromTables($table) . ' 01014 ( 01015 ' . implode(', 01016 ', array_keys($nArr)) . ' 01017 ) VALUES ( 01018 ' . implode(', 01019 ', $nArr) . ' 01020 )'; 01021 } 01022 if (count($blobfields)) $query[1] = $blobfields; 01023 if (count($clobfields)) $query[2] = $clobfields; 01024 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query[0]; 01025 } else { 01026 $query = 'INSERT INTO ' . $this->quoteFromTables($table) . ' 01027 ( 01028 ' . implode(', 01029 ', array_keys($nArr)) . ' 01030 ) VALUES ( 01031 ' . implode(', 01032 ', $nArr) . ' 01033 )'; 01034 01035 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query; 01036 } 01037 01038 return $query; 01039 } 01040 } 01041 01042 /** 01043 * Creates an INSERT SQL-statement for $table with multiple rows. 01044 * This method will create multiple INSERT queries concatenated with ';' 01045 * 01046 * @param string Table name 01047 * @param array Field names 01048 * @param array Table rows. Each row should be an array with field values mapping to $fields 01049 * @param string/array See fullQuoteArray() 01050 * @return array Full SQL query for INSERT as array of strings (unless $fields_values does not contain any elements in which case it will be FALSE). If BLOB fields will be affected and one is not running the native type, an array will be returned for each row, where 0 => plain SQL, 1 => fieldname/value pairs of BLOB fields. 01051 */ 01052 public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) { 01053 if ((string) $this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') { 01054 return parent::INSERTmultipleRows($table, $fields, $rows, $no_quote_fields); 01055 } 01056 01057 $result = array(); 01058 01059 foreach ($rows as $row) { 01060 $fields_values = array(); 01061 foreach ($fields as $key => $value) { 01062 $fields_values[$value] = $row[$key]; 01063 } 01064 $rowQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields); 01065 if (is_array($rowQuery)) { 01066 $result[] = $rowQuery; 01067 } else { 01068 $result[][0] = $rowQuery; 01069 } 01070 } 01071 01072 return $result; 01073 } 01074 01075 /** 01076 * Creates an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values. 01077 * Usage count/core: 6 01078 * 01079 * @param string See exec_UPDATEquery() 01080 * @param string See exec_UPDATEquery() 01081 * @param array See exec_UPDATEquery() 01082 * @param mixed See exec_UPDATEquery() 01083 * @return mixed Full SQL query for UPDATE as string or array (unless $fields_values does not contain any elements in which case it will be FALSE). If BLOB fields will be affected and one is not running the native type, an array will be returned, where 0 => plain SQL, 1 => fieldname/value pairs of BLOB fields 01084 */ 01085 public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = '') { 01086 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure). 01087 if (is_string($where)) { 01088 $fields = array(); 01089 $blobfields = array(); 01090 $clobfields = array(); 01091 if (is_array($fields_values) && count($fields_values)) { 01092 01093 if (is_string($no_quote_fields)) { 01094 $no_quote_fields = explode(',', $no_quote_fields); 01095 } elseif (!is_array($no_quote_fields)) { 01096 $no_quote_fields = array(); 01097 } 01098 01099 $nArr = array(); 01100 foreach ($fields_values as $k => $v) { 01101 if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') { 01102 // we skip the field in the regular UPDATE statement, it is only in blobfields 01103 $blobfields[$this->quoteFieldNames($k)] = $v; 01104 } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') { 01105 // we skip the field in the regular UPDATE statement, it is only in clobfields 01106 $clobfields[$this->quoteFieldNames($k)] = $v; 01107 } else { 01108 // Add slashes old-school: 01109 // cast numeric values 01110 $mt = $this->sql_field_metatype($table, $k); 01111 if ($mt{0} == 'I') { 01112 $v = (int) $v; 01113 } else if ($mt{0} == 'F') { 01114 $v = (double) $v; 01115 } 01116 $nArr[] = $this->quoteFieldNames($k) . '=' . ((!in_array($k, $no_quote_fields)) ? $this->fullQuoteStr($v, $table) : $v); 01117 } 01118 } 01119 } 01120 01121 if (count($blobfields) || count($clobfields)) { 01122 if (count($nArr)) { 01123 $query[0] = 'UPDATE ' . $this->quoteFromTables($table) . ' 01124 SET 01125 ' . implode(', 01126 ', $nArr) . 01127 (strlen($where) > 0 ? ' 01128 WHERE 01129 ' . $this->quoteWhereClause($where) : ''); 01130 } 01131 if (count($blobfields)) { 01132 $query[1] = $blobfields; 01133 } 01134 if (count($clobfields)) { 01135 $query[2] = $clobfields; 01136 } 01137 if ($this->debugOutput || $this->store_lastBuiltQuery) { 01138 $this->debug_lastBuiltQuery = $query[0]; 01139 } 01140 } else { 01141 $query = 'UPDATE ' . $this->quoteFromTables($table) . ' 01142 SET 01143 ' . implode(', 01144 ', $nArr) . 01145 (strlen($where) > 0 ? ' 01146 WHERE 01147 ' . $this->quoteWhereClause($where) : ''); 01148 01149 if ($this->debugOutput || $this->store_lastBuiltQuery) { 01150 $this->debug_lastBuiltQuery = $query; 01151 } 01152 } 01153 return $query; 01154 } else { 01155 throw new InvalidArgumentException( 01156 'TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 01157 1270853880 01158 ); 01159 } 01160 } 01161 01162 /** 01163 * Creates a DELETE SQL-statement for $table where $where-clause 01164 * Usage count/core: 3 01165 * 01166 * @param string See exec_DELETEquery() 01167 * @param string See exec_DELETEquery() 01168 * @return string Full SQL query for DELETE 01169 */ 01170 public function DELETEquery($table, $where) { 01171 if (is_string($where)) { 01172 $table = $this->quoteFromTables($table); 01173 $where = $this->quoteWhereClause($where); 01174 01175 $query = parent::DELETEquery($table, $where); 01176 01177 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query; 01178 return $query; 01179 } else { 01180 die('<strong>TYPO3 Fatal Error:</strong> "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !'); 01181 } 01182 } 01183 01184 /** 01185 * Creates a SELECT SQL-statement 01186 * Usage count/core: 11 01187 * 01188 * @param string See exec_SELECTquery() 01189 * @param string See exec_SELECTquery() 01190 * @param string See exec_SELECTquery() 01191 * @param string See exec_SELECTquery() 01192 * @param string See exec_SELECTquery() 01193 * @param string See exec_SELECTquery() 01194 * @return string Full SQL query for SELECT 01195 */ 01196 public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') { 01197 $this->lastHandlerKey = $this->handler_getFromTableList($from_table); 01198 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 01199 if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) { 01200 // Possibly rewrite the LIMIT to be PostgreSQL-compatible 01201 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values: 01202 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise: 01203 $numrows = $splitLimit[1]; 01204 $offset = $splitLimit[0]; 01205 $limit = $numrows . ' OFFSET ' . $offset; 01206 } 01207 } 01208 01209 $select_fields = $this->quoteFieldNames($select_fields); 01210 $from_table = $this->quoteFromTables($from_table); 01211 $where_clause = $this->quoteWhereClause($where_clause); 01212 $groupBy = $this->quoteGroupBy($groupBy); 01213 $orderBy = $this->quoteOrderBy($orderBy); 01214 01215 // Call parent method to build actual query 01216 $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit); 01217 01218 if ($this->debugOutput || $this->store_lastBuiltQuery) { 01219 $this->debug_lastBuiltQuery = $query; 01220 } 01221 01222 return $query; 01223 } 01224 01225 /** 01226 * Creates a SELECT SQL-statement to be used with an ADOdb backend. 01227 * 01228 * @param array parsed parameters: array($select_fields, $from_table, $where_clause, $groupBy, $orderBy) 01229 * @return string Full SQL query for SELECT 01230 */ 01231 protected function SELECTqueryFromArray(array $params) { 01232 // $select_fields 01233 $params[0] = $this->_quoteFieldNames($params[0]); 01234 // $from_table 01235 $params[1] = $this->_quoteFromTables($params[1]); 01236 // $where_clause 01237 if (count($params[2]) > 0) { 01238 $params[2] = $this->_quoteWhereClause($params[2]); 01239 } 01240 // $group_by 01241 if (count($params[3]) > 0) { 01242 $params[3] = $this->_quoteGroupBy($params[3]); 01243 } 01244 // $order_by 01245 if (count($params[4]) > 0) { 01246 $params[4] = $this->_quoteOrderBy($params[4]); 01247 } 01248 01249 // Compile the SELECT parameters 01250 list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($params); 01251 01252 // Call parent method to build actual query 01253 $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy); 01254 01255 if ($this->debugOutput || $this->store_lastBuiltQuery) { 01256 $this->debug_lastBuiltQuery = $query; 01257 } 01258 01259 return $query; 01260 } 01261 01262 /** 01263 * Compiles and returns an array of SELECTquery parameters (without $limit) to 01264 * be used with SELECTquery() or exec_SELECTquery(). 01265 * 01266 * @param array $params 01267 * @return array array($select_fields, $from_table, $where_clause, $groupBy, $orderBy) 01268 */ 01269 protected function compileSelectParameters(array $params) { 01270 $select_fields = $this->SQLparser->compileFieldList($params[0]); 01271 $from_table = $this->SQLparser->compileFromTables($params[1]); 01272 $where_clause = (count($params[2]) > 0) ? $this->SQLparser->compileWhereClause($params[2]) : ''; 01273 $groupBy = (count($params[3]) > 0) ? $this->SQLparser->compileFieldList($params[3]) : ''; 01274 $orderBy = (count($params[4]) > 0) ? $this->SQLparser->compileFieldList($params[4]) : ''; 01275 01276 return array($select_fields, $from_table, $where_clause, $groupBy, $orderBy); 01277 } 01278 01279 /** 01280 * Creates a TRUNCATE TABLE SQL-statement 01281 * 01282 * @param string See exec_TRUNCATEquery() 01283 * @return string Full SQL query for TRUNCATE TABLE 01284 */ 01285 public function TRUNCATEquery($table) { 01286 $table = $this->quoteFromTables($table); 01287 01288 // Call parent method to build actual query 01289 $query = parent::TRUNCATEquery($table); 01290 01291 if ($this->debugOutput || $this->store_lastBuiltQuery) { 01292 $this->debug_lastBuiltQuery = $query; 01293 } 01294 01295 return $query; 01296 } 01297 01298 /************************************** 01299 * 01300 * Prepared Query Support 01301 * 01302 **************************************/ 01303 01304 /** 01305 * Creates a SELECT prepared SQL statement. 01306 * 01307 * @param string See exec_SELECTquery() 01308 * @param string See exec_SELECTquery() 01309 * @param string See exec_SELECTquery() 01310 * @param string See exec_SELECTquery() 01311 * @param string See exec_SELECTquery() 01312 * @param string See exec_SELECTquery() 01313 * @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. 01314 * @return t3lib_db_PreparedStatement Prepared statement 01315 */ 01316 public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) { 01317 if ($this->debug) { 01318 $pt = t3lib_div::milliseconds(); 01319 } 01320 01321 $precompiledParts = array(); 01322 01323 if ($this->queryCache) { 01324 $cacheKey = 'prepare_SELECTquery-' . tx_dbal_querycache::getCacheKey(array( 01325 'selectFields' => $select_fields, 01326 'fromTable' => $from_table, 01327 'whereClause' => $where_clause, 01328 'groupBy' => $groupBy, 01329 'orderBy' => $orderBy, 01330 'limit' => $limit, 01331 )); 01332 if ($this->queryCache->has($cacheKey)) { 01333 $precompiledParts = $this->queryCache->get($cacheKey); 01334 if ($this->debug) { 01335 $data = array( 01336 'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters), 01337 'precompiledParts' => $precompiledParts, 01338 ); 01339 $this->debugHandler( 01340 'prepare_SELECTquery (cache hit)', 01341 t3lib_div::milliseconds() - $pt, 01342 $data 01343 ); 01344 } 01345 } 01346 } 01347 01348 if (count($precompiledParts) == 0) { 01349 // Map table / field names if needed: 01350 $ORIG_tableName = $from_table; // Saving table names in $ORIG_from_table since $from_table is transformed beneath: 01351 $parsedFromTable = array(); 01352 $queryComponents = array(); 01353 if ($tableArray = $this->map_needMapping($ORIG_tableName, FALSE, $parsedFromTable)) { 01354 $from = $parsedFromTable ? $parsedFromTable : $from_table; 01355 $components = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy); 01356 $queryComponents['SELECT'] = $components[0]; 01357 $queryComponents['FROM'] = $components[1]; 01358 $queryComponents['WHERE'] = $components[2]; 01359 $queryComponents['GROUPBY'] = $components[3]; 01360 $queryComponents['ORDERBY'] = $components[4]; 01361 $queryComponents['parameters'] = $components[5]; 01362 } else { 01363 $queryComponents = $this->getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit); 01364 } 01365 01366 $queryComponents['ORIG_tableName'] = $ORIG_tableName; 01367 01368 if (!$this->runningNative()) { 01369 // Quotes all fields 01370 $queryComponents['SELECT'] = $this->_quoteFieldNames($queryComponents['SELECT']); 01371 $queryComponents['FROM'] = $this->_quoteFromTables($queryComponents['FROM']); 01372 $queryComponents['WHERE'] = $this->_quoteWhereClause($queryComponents['WHERE']); 01373 $queryComponents['GROUPBY'] = $this->_quoteGroupBy($queryComponents['GROUPBY']); 01374 $queryComponents['ORDERBY'] = $this->_quoteOrderBy($queryComponents['ORDERBY']); 01375 } 01376 01377 $precompiledParts = $this->precompileSELECTquery($queryComponents); 01378 if ($this->queryCache) { 01379 try { 01380 $this->queryCache->set($cacheKey, $precompiledParts); 01381 } catch (t3lib_cache_Exception $e) { 01382 if ($this->debug) { 01383 t3lib_div::devLog($e->getMessage(), 'dbal', 1); 01384 } 01385 } 01386 } 01387 } 01388 01389 $preparedStatement = t3lib_div::makeInstance('t3lib_db_PreparedStatement', '', $from_table, $precompiledParts); 01390 /* @var $preparedStatement t3lib_db_PreparedStatement */ 01391 01392 // Bind values to parameters 01393 foreach ($input_parameters as $key => $value) { 01394 $preparedStatement->bindValue($key, $value, t3lib_db_PreparedStatement::PARAM_AUTOTYPE); 01395 } 01396 01397 if ($this->debug) { 01398 $data = array( 01399 'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters), 01400 'ORIG_from_table' => $ORIG_tableName, 01401 ); 01402 $this->debugHandler( 01403 'prepare_SELECTquery', 01404 t3lib_div::milliseconds() - $pt, 01405 $data 01406 ); 01407 } 01408 01409 // Return prepared statement 01410 return $preparedStatement; 01411 } 01412 01413 /** 01414 * Returns the parsed query components. 01415 * 01416 * @param string $select_fields 01417 * @param string $from_table 01418 * @param string $where_clause 01419 * @param string $groupBy 01420 * @param string $orderBy 01421 * @param string $limit 01422 * @return array 01423 */ 01424 protected function getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit) { 01425 $queryComponents = array( 01426 'SELECT' => '', 01427 'FROM' => '', 01428 'WHERE' => '', 01429 'GROUPBY' => '', 01430 'ORDERBY' => '', 01431 'LIMIT' => '', 01432 'parameters' => array(), 01433 ); 01434 01435 $this->lastHandlerKey = $this->handler_getFromTableList($from_table); 01436 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 01437 if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) { 01438 // Possibly rewrite the LIMIT to be PostgreSQL-compatible 01439 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values: 01440 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise: 01441 $numrows = $splitLimit[1]; 01442 $offset = $splitLimit[0]; 01443 $limit = $numrows . ' OFFSET ' . $offset; 01444 } 01445 } 01446 $queryComponents['LIMIT'] = $limit; 01447 01448 $queryComponents['SELECT'] = $this->SQLparser->parseFieldList($select_fields); 01449 if ($this->SQLparser->parse_error) { 01450 die($this->SQLparser->parse_error . ' in ' . __FILE__ . ' : ' . __LINE__); 01451 } 01452 01453 $queryComponents['FROM'] = $this->SQLparser->parseFromTables($from_table); 01454 01455 $queryComponents['WHERE'] = $this->SQLparser->parseWhereClause($where_clause, '', $queryComponents['parameters']); 01456 if (!is_array($queryComponents['WHERE'])) { 01457 die('Could not parse where clause in ' . __FILE__ . ' : ' . __LINE__); 01458 } 01459 01460 $queryComponents['GROUPBY'] = $this->SQLparser->parseFieldList($groupBy); 01461 $queryComponents['ORDERBY'] = $this->SQLparser->parseFieldList($orderBy); 01462 01463 // Return the query components 01464 return $queryComponents; 01465 } 01466 01467 /** 01468 * Precompiles a SELECT prepared SQL statement. 01469 * 01470 * @param array $components 01471 * @return array Precompiled SQL statement 01472 */ 01473 protected function precompileSELECTquery(array $components) { 01474 $parameterWrap = '__' . dechex(time()) . '__'; 01475 foreach ($components['parameters'] as $key => $params) { 01476 if ($key === '?') { 01477 foreach ($params as $index => $param) { 01478 $components['parameters'][$key][$index][0] = $parameterWrap . $param[0] . $parameterWrap; 01479 } 01480 } else { 01481 $components['parameters'][$key][0] = $parameterWrap . $params[0] . $parameterWrap; 01482 } 01483 } 01484 01485 $select_fields = $this->SQLparser->compileFieldList($components['SELECT']); 01486 $from_table = $this->SQLparser->compileFromTables($components['FROM']); 01487 $where_clause = $this->SQLparser->compileWhereClause($components['WHERE']); 01488 $groupBy = $this->SQLparser->compileFieldList($components['GROUPBY']); 01489 $orderBy = $this->SQLparser->compileFieldList($components['ORDERBY']); 01490 $limit = $components['LIMIT']; 01491 $precompiledParts = array(); 01492 01493 $this->lastHandlerKey = $this->handler_getFromTableList($components['ORIG_tableName']); 01494 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 01495 $precompiledParts['handler'] = $hType; 01496 $precompiledParts['ORIG_tableName'] = $components['ORIG_tableName']; 01497 01498 switch ($hType) { 01499 case 'native': 01500 $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit); 01501 $precompiledParts['queryParts'] = explode($parameterWrap, $query); 01502 break; 01503 case 'adodb': 01504 $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy); 01505 $precompiledParts['queryParts'] = explode($parameterWrap, $query); 01506 $precompiledParts['LIMIT'] = $limit; 01507 break; 01508 case 'userdefined': 01509 $precompiledParts['queryParts'] = array( 01510 'SELECT' => $select_fields, 01511 'FROM' => $from_table, 01512 'WHERE' => $where_clause, 01513 'GROUPBY' => $groupBy, 01514 'ORDERBY' => $orderBy, 01515 'LIMIT' => $limit, 01516 ); 01517 break; 01518 } 01519 01520 return $precompiledParts; 01521 } 01522 01523 /** 01524 * Executes a prepared query. 01525 * 01526 * @param string $query The query to execute 01527 * @param array $queryComponents The components of the query to execute 01528 * @return pointer MySQL result pointer / DBAL object 01529 * @access protected This method may only be called by t3lib_db_PreparedStatement 01530 */ 01531 public function exec_PREPAREDquery($query, array $precompiledParts) { 01532 if ($this->debug) { 01533 $pt = t3lib_div::milliseconds(); 01534 } 01535 01536 // Get handler key and select API: 01537 switch ($precompiledParts['handler']) { 01538 case 'native': 01539 $this->lastQuery = $query; 01540 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']); 01541 $this->resourceIdToTableNameMap[(string) $sqlResult] = $precompiledParts['ORIG_tableName']; 01542 break; 01543 case 'adodb': 01544 $limit = $precompiledParts['LIMIT']; 01545 if ($this->runningADOdbDriver('postgres')) { 01546 // Possibly rewrite the LIMIT to be PostgreSQL-compatible 01547 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values: 01548 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise: 01549 $numrows = $splitLimit[1]; 01550 $offset = $splitLimit[0]; 01551 $limit = $numrows . ' OFFSET ' . $offset; 01552 } 01553 } 01554 if ($limit != '') { 01555 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values: 01556 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise: 01557 $numrows = $splitLimit[1]; 01558 $offset = $splitLimit[0]; 01559 } else { 01560 $numrows = $splitLimit[0]; 01561 $offset = 0; 01562 } 01563 01564 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($query, $numrows, $offset); 01565 $this->lastQuery = $sqlResult->sql; 01566 } else { 01567 $this->lastQuery = $query; 01568 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery); 01569 } 01570 01571 $sqlResult->TYPO3_DBAL_handlerType = 'adodb'; // Setting handler type in result object (for later recognition!) 01572 $sqlResult->TYPO3_DBAL_tableList = $precompiledParts['ORIG_tableName']; 01573 break; 01574 case 'userdefined': 01575 $queryParts = $precompiledParts['queryParts']; 01576 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery( 01577 $queryParts['SELECT'], 01578 $queryParts['FROM'], 01579 $queryParts['WHERE'], 01580 $queryParts['GROUPBY'], 01581 $queryParts['ORDERBY'], 01582 $queryParts['LIMIT'] 01583 ); 01584 if (is_object($sqlResult)) { 01585 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined'; // Setting handler type in result object (for later recognition!) 01586 $sqlResult->TYPO3_DBAL_tableList = $precompiledParts['ORIG_tableName']; 01587 } 01588 break; 01589 } 01590 01591 if ($this->printErrors && $this->sql_error()) { 01592 debug(array($this->lastQuery, $this->sql_error())); 01593 } 01594 01595 if ($this->debug) { 01596 $data = array( 01597 'handlerType' => $precompiledParts['handler'], 01598 'args' => $precompiledParts, 01599 'ORIG_from_table' => $precompiledParts['ORIG_tableName'], 01600 ); 01601 if ($this->conf['debugOptions']['numberRows']) { 01602 $data['numberRows'] = $this->sql_num_rows($sqlResult); 01603 } 01604 $this->debugHandler( 01605 'exec_PREPAREDquery', 01606 t3lib_div::milliseconds() - $pt, 01607 $data 01608 ); 01609 } 01610 01611 // Return result handler. 01612 return $sqlResult; 01613 } 01614 01615 /************************************** 01616 * 01617 * Functions for quoting table/field names 01618 * 01619 **************************************/ 01620 01621 /** 01622 * Quotes components of a SELECT subquery. 01623 * 01624 * @param array $components Array of SQL query components 01625 * @return array 01626 */ 01627 protected function quoteSELECTsubquery(array $components) { 01628 $components['SELECT'] = $this->_quoteFieldNames($components['SELECT']); 01629 $components['FROM'] = $this->_quoteFromTables($components['FROM']); 01630 $components['WHERE'] = $this->_quoteWhereClause($components['WHERE']); 01631 return $components; 01632 } 01633 01634 /** 01635 * Quotes field (and table) names with the quote character suitable for the DB being used 01636 * Use quoteFieldNames instead! 01637 * 01638 * @param string List of fields to be selected from DB 01639 * @return string Quoted list of fields to be selected from DB 01640 * @deprecated since TYPO3 4.0, will be removed in TYPO3 4.6 01641 */ 01642 public function quoteSelectFields($select_fields) { 01643 t3lib_div::logDeprecatedFunction(); 01644 01645 $this->quoteFieldNames($select_fields); 01646 } 01647 01648 /** 01649 * Quotes field (and table) names with the quote character suitable for the DB being used 01650 * 01651 * @param string List of fields to be used in query to DB 01652 * @return string Quoted list of fields to be in query to DB 01653 */ 01654 public function quoteFieldNames($select_fields) { 01655 if ($select_fields == '') { 01656 return ''; 01657 } 01658 if ($this->runningNative()) { 01659 return $select_fields; 01660 } 01661 01662 $select_fields = $this->SQLparser->parseFieldList($select_fields); 01663 if ($this->SQLparser->parse_error) { 01664 die($this->SQLparser->parse_error . ' in ' . __FILE__ . ' : ' . __LINE__); 01665 } 01666 $select_fields = $this->_quoteFieldNames($select_fields); 01667 01668 return $this->SQLparser->compileFieldList($select_fields); 01669 } 01670 01671 /** 01672 * Quotes field (and table) names in a SQL SELECT clause acccording to DB rules 01673 * 01674 * @param array $select_fields The parsed fields to quote 01675 * @return array 01676 * @see quoteFieldNames() 01677 */ 01678 protected function _quoteFieldNames(array $select_fields) { 01679 foreach ($select_fields as $k => $v) { 01680 if ($select_fields[$k]['field'] != '' && $select_fields[$k]['field'] != '*' && !is_numeric($select_fields[$k]['field'])) { 01681 $select_fields[$k]['field'] = $this->quoteName($select_fields[$k]['field']); 01682 } 01683 if ($select_fields[$k]['table'] != '' && !is_numeric($select_fields[$k]['table'])) { 01684 $select_fields[$k]['table'] = $this->quoteName($select_fields[$k]['table']); 01685 } 01686 if ($select_fields[$k]['as'] != '') { 01687 $select_fields[$k]['as'] = $this->quoteName($select_fields[$k]['as']); 01688 } 01689 if (isset($select_fields[$k]['func_content.']) && $select_fields[$k]['func_content.'][0]['func_content'] != '*') { 01690 $select_fields[$k]['func_content.'][0]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content.'][0]['func_content']); 01691 $select_fields[$k]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content']); 01692 } 01693 if (isset($select_fields[$k]['flow-control'])) { 01694 // Quoting flow-control statements 01695 if ($select_fields[$k]['flow-control']['type'] === 'CASE') { 01696 if (isset($select_fields[$k]['flow-control']['case_field'])) { 01697 $select_fields[$k]['flow-control']['case_field'] = $this->quoteFieldNames($select_fields[$k]['flow-control']['case_field']); 01698 } 01699 foreach ($select_fields[$k]['flow-control']['when'] as $key => $when) { 01700 $select_fields[$k]['flow-control']['when'][$key]['when_value'] = $this->_quoteWhereClause($when['when_value']); 01701 } 01702 } 01703 } 01704 } 01705 01706 return $select_fields; 01707 } 01708 01709 /** 01710 * Quotes table names with the quote character suitable for the DB being used 01711 * 01712 * @param string List of tables to be selected from DB 01713 * @return string Quoted list of tables to be selected from DB 01714 */ 01715 public function quoteFromTables($from_table) { 01716 if ($from_table == '') { 01717 return ''; 01718 } 01719 if ($this->runningNative()) { 01720 return $from_table; 01721 } 01722 01723 $from_table = $this->SQLparser->parseFromTables($from_table); 01724 $from_table = $this->_quoteFromTables($from_table); 01725 return $this->SQLparser->compileFromTables($from_table); 01726 } 01727 01728 /** 01729 * Quotes table names in a SQL FROM clause acccording to DB rules 01730 * 01731 * @param array $from_table The parsed FROM clause to quote 01732 * @return array 01733 * @see quoteFromTables() 01734 */ 01735 protected function _quoteFromTables(array $from_table) { 01736 foreach ($from_table as $k => $v) { 01737 $from_table[$k]['table'] = $this->quoteName($from_table[$k]['table']); 01738 if ($from_table[$k]['as'] != '') { 01739 $from_table[$k]['as'] = $this->quoteName($from_table[$k]['as']); 01740 } 01741 if (is_array($v['JOIN'])) { 01742 foreach ($v['JOIN'] as $joinCnt => $join) { 01743 $from_table[$k]['JOIN'][$joinCnt]['withTable'] = $this->quoteName($join['withTable']); 01744 $from_table[$k]['JOIN'][$joinCnt]['as'] = ($join['as']) ? $this->quoteName($join['as']) : ''; 01745 foreach ($from_table[$k]['JOIN'][$joinCnt]['ON'] as &$condition) { 01746 $condition['left']['table'] = ($condition['left']['table']) ? $this->quoteName($condition['left']['table']) : ''; 01747 $condition['left']['field'] = $this->quoteName($condition['left']['field']); 01748 $condition['right']['table'] = ($condition['right']['table']) ? $this->quoteName($condition['right']['table']) : ''; 01749 $condition['right']['field'] = $this->quoteName($condition['right']['field']); 01750 } 01751 } 01752 } 01753 } 01754 01755 return $from_table; 01756 } 01757 01758 /** 01759 * Quotes the field (and table) names within a where clause with the quote character suitable for the DB being used 01760 * 01761 * @param string A where clause that can be parsed by parseWhereClause 01762 * @return string Usable where clause with quoted field/table names 01763 */ 01764 public function quoteWhereClause($where_clause) { 01765 if ($where_clause === '' || $this->runningNative()) { 01766 return $where_clause; 01767 } 01768 01769 $where_clause = $this->SQLparser->parseWhereClause($where_clause); 01770 if (is_array($where_clause)) { 01771 $where_clause = $this->_quoteWhereClause($where_clause); 01772 $where_clause = $this->SQLparser->compileWhereClause($where_clause); 01773 } else { 01774 die('Could not parse where clause in ' . __FILE__ . ' : ' . __LINE__); 01775 } 01776 01777 return $where_clause; 01778 } 01779 01780 /** 01781 * Quotes field names in a SQL WHERE clause acccording to DB rules 01782 * 01783 * @param array $where_clause The parsed WHERE clause to quote 01784 * @return array 01785 * @see quoteWhereClause() 01786 */ 01787 protected function _quoteWhereClause(array $where_clause) { 01788 foreach ($where_clause as $k => $v) { 01789 // Look for sublevel: 01790 if (is_array($where_clause[$k]['sub'])) { 01791 $where_clause[$k]['sub'] = $this->_quoteWhereClause($where_clause[$k]['sub']); 01792 } elseif (isset($v['func'])) { 01793 switch ($where_clause[$k]['func']['type']) { 01794 case 'EXISTS': 01795 $where_clause[$k]['func']['subquery'] = $this->quoteSELECTsubquery($v['func']['subquery']); 01796 break; 01797 case 'FIND_IN_SET': 01798 // quoteStr that will be used for Oracle 01799 $pattern = str_replace($where_clause[$k]['func']['str'][1], '\\' . $where_clause[$k]['func']['str'][1], $where_clause[$k]['func']['str'][0]); 01800 // table is not really needed and may in fact be empty in real statements 01801 // but it's not overriden from t3lib_db at the moment... 01802 $patternForLike = $this->escapeStrForLike($pattern, $where_clause[$k]['func']['table']); 01803 $where_clause[$k]['func']['str_like'] = $patternForLike; 01804 01805 // BEWARE: no break here to have next statements too 01806 case 'IFNULL': 01807 case 'LOCATE': 01808 if ($where_clause[$k]['func']['table'] != '') { 01809 $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']); 01810 } 01811 if ($where_clause[$k]['func']['field'] != '') { 01812 $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']); 01813 } 01814 break; 01815 } 01816 } else { 01817 if ($where_clause[$k]['table'] != '') { 01818 $where_clause[$k]['table'] = $this->quoteName($where_clause[$k]['table']); 01819 } 01820 if (!is_numeric($where_clause[$k]['field'])) { 01821 $where_clause[$k]['field'] = $this->quoteName($where_clause[$k]['field']); 01822 } 01823 if (isset($where_clause[$k]['calc_table'])) { 01824 if ($where_clause[$k]['calc_table'] != '') { 01825 $where_clause[$k]['calc_table'] = $this->quoteName($where_clause[$k]['calc_table']); 01826 } 01827 if ($where_clause[$k]['calc_field'] != '') { 01828 $where_clause[$k]['calc_field'] = $this->quoteName($where_clause[$k]['calc_field']); 01829 } 01830 } 01831 } 01832 if ($where_clause[$k]['comparator']) { 01833 if (isset($v['value']['operator'])) { 01834 foreach ($where_clause[$k]['value']['args'] as $argK => $fieldDef) { 01835 $where_clause[$k]['value']['args'][$argK]['table'] = $this->quoteName($fieldDef['table']); 01836 $where_clause[$k]['value']['args'][$argK]['field'] = $this->quoteName($fieldDef['field']); 01837 } 01838 } else { 01839 // Detecting value type; list or plain: 01840 if (t3lib_div::inList('NOTIN,IN', strtoupper(str_replace(array(' ', "\n", "\r", "\t"), '', $where_clause[$k]['comparator'])))) { 01841 if (isset($v['subquery'])) { 01842 $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']); 01843 } 01844 } else { 01845 if ((!isset($where_clause[$k]['value'][1]) || $where_clause[$k]['value'][1] == '') && is_string($where_clause[$k]['value'][0]) && strstr($where_clause[$k]['value'][0], '.')) { 01846 $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]); 01847 } 01848 } 01849 } 01850 } 01851 } 01852 01853 return $where_clause; 01854 } 01855 01856 /** 01857 * Quotes the field (and table) names within a group by clause with the quote 01858 * character suitable for the DB being used 01859 * 01860 * @param string A group by clause that can by parsed by parseFieldList 01861 * @return string Usable group by clause with quoted field/table names 01862 */ 01863 protected function quoteGroupBy($groupBy) { 01864 if ($groupBy === '') { 01865 return ''; 01866 } 01867 if ($this->runningNative()) { 01868 return $groupBy; 01869 } 01870 01871 $groupBy = $this->SQLparser->parseFieldList($groupBy); 01872 $groupBy = $this->_quoteGroupBy($groupBy); 01873 01874 return $this->SQLparser->compileFieldList($groupBy); 01875 } 01876 01877 /** 01878 * Quotes field names in a SQL GROUP BY clause acccording to DB rules 01879 * 01880 * @param array $groupBy The parsed GROUP BY clause to quote 01881 * @return array 01882 * @see quoteGroupBy() 01883 */ 01884 protected function _quoteGroupBy(array $groupBy) { 01885 foreach ($groupBy as $k => $v) { 01886 $groupBy[$k]['field'] = $this->quoteName($groupBy[$k]['field']); 01887 if ($groupBy[$k]['table'] != '') { 01888 $groupBy[$k]['table'] = $this->quoteName($groupBy[$k]['table']); 01889 } 01890 } 01891 return $groupBy; 01892 } 01893 01894 /** 01895 * Quotes the field (and table) names within an order by clause with the quote 01896 * character suitable for the DB being used 01897 * 01898 * @param string An order by clause that can by parsed by parseFieldList 01899 * @return string Usable order by clause with quoted field/table names 01900 */ 01901 protected function quoteOrderBy($orderBy) { 01902 if ($orderBy === '') { 01903 return ''; 01904 } 01905 if ($this->runningNative()) { 01906 return $orderBy; 01907 } 01908 01909 $orderBy = $this->SQLparser->parseFieldList($orderBy); 01910 $orderBy = $this->_quoteOrderBy($orderBy); 01911 01912 return $this->SQLparser->compileFieldList($orderBy); 01913 } 01914 01915 /** 01916 * Quotes field names in a SQL ORDER BY clause acccording to DB rules 01917 * 01918 * @param array $orderBy The parsed ORDER BY clause to quote 01919 * @return array 01920 * @see quoteOrderBy() 01921 */ 01922 protected function _quoteOrderBy(array $orderBy) { 01923 foreach ($orderBy as $k => $v) { 01924 $orderBy[$k]['field'] = $this->quoteName($orderBy[$k]['field']); 01925 if ($orderBy[$k]['table'] != '') { 01926 $orderBy[$k]['table'] = $this->quoteName($orderBy[$k]['table']); 01927 } 01928 } 01929 return $orderBy; 01930 } 01931 01932 01933 /************************************** 01934 * 01935 * Various helper functions 01936 * 01937 **************************************/ 01938 01939 /** 01940 * Escaping and quoting values for SQL statements. 01941 * 01942 * @param string Input string 01943 * @param string Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!). 01944 * @return string Output string; Wrapped in single quotes and quotes in the string (" / ') and \ will be backslashed (or otherwise based on DBAL handler) 01945 * @see quoteStr() 01946 */ 01947 public function fullQuoteStr($str, $table) { 01948 return '\'' . $this->quoteStr($str, $table) . '\''; 01949 } 01950 01951 /** 01952 * Substitution for PHP function "addslashes()" 01953 * NOTICE: You must wrap the output of this function in SINGLE QUOTES to be DBAL compatible. Unless you have to apply the single quotes yourself you should rather use ->fullQuoteStr()! 01954 * 01955 * @param string Input string 01956 * @param string Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!). 01957 * @return string Output string; Quotes (" / ') and \ will be backslashed (or otherwise based on DBAL handler) 01958 * @see quoteStr() 01959 */ 01960 public function quoteStr($str, $table) { 01961 $this->lastHandlerKey = $this->handler_getFromTableList($table); 01962 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 01963 case 'native': 01964 if ($this->handlerInstance[$this->lastHandlerKey]['link']) { 01965 $str = mysql_real_escape_string($str, $this->handlerInstance[$this->lastHandlerKey]['link']); 01966 } else { 01967 // link may be null when unit testing DBAL 01968 $str = str_replace('\'', '\\\'', $str); 01969 } 01970 break; 01971 case 'adodb': 01972 $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1); 01973 break; 01974 case 'userdefined': 01975 $str = $this->handlerInstance[$this->lastHandlerKey]->quoteStr($str); 01976 break; 01977 default: 01978 die('No handler found!!!'); 01979 break; 01980 } 01981 01982 return $str; 01983 } 01984 01985 /** 01986 * Quotes an object name (table name, field, ...) 01987 * 01988 * @param string Object's name 01989 * @param string Handler key 01990 * @param boolean If method NameQuote() is not used, whether to use backticks instead of driver-specific quotes 01991 * @return string Properly-quoted object's name 01992 */ 01993 public function quoteName($name, $handlerKey = NULL, $useBackticks = FALSE) { 01994 $handlerKey = $handlerKey ? $handlerKey : $this->lastHandlerKey; 01995 $useNameQuote = isset($this->handlerCfg[$handlerKey]['config']['useNameQuote']) ? $this->handlerCfg[$handlerKey]['config']['useNameQuote'] : FALSE; 01996 if ($useNameQuote) { 01997 return $this->handlerInstance[$handlerKey]->DataDictionary->NameQuote($name); 01998 } else { 01999 $quote = $useBackticks ? '`' : $this->handlerInstance[$handlerKey]->nameQuote; 02000 return $quote . $name . $quote; 02001 } 02002 } 02003 02004 /** 02005 * Return MetaType for native field type (ADOdb only!) 02006 * 02007 * @param string native type as reported by admin_get_fields() 02008 * @param string Table name for which query type string. Important for detection of DBMS handler of the query! 02009 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype) 02010 */ 02011 public function MetaType($type, $table, $max_length = -1) { 02012 $this->lastHandlerKey = $this->handler_getFromTableList($table); 02013 $str = ''; 02014 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 02015 case 'native': 02016 $str = $type; 02017 break; 02018 case 'adodb': 02019 if (in_array($table, $this->cache_fieldType)) { 02020 $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1); 02021 $str = $rs->MetaType($type, $max_length); 02022 } 02023 break; 02024 case 'userdefined': 02025 $str = $this->handlerInstance[$this->lastHandlerKey]->MetaType($str, $table, $max_length); 02026 break; 02027 default: 02028 die('No handler found!!!'); 02029 break; 02030 } 02031 02032 return $str; 02033 } 02034 02035 02036 /** 02037 * Return MetaType for native MySQL field type 02038 * 02039 * @param string native type as reported as in mysqldump files 02040 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype) 02041 */ 02042 public function MySQLMetaType($t) { 02043 02044 switch (strtoupper($t)) { 02045 case 'STRING': 02046 case 'CHAR': 02047 case 'VARCHAR': 02048 case 'TINYBLOB': 02049 case 'TINYTEXT': 02050 case 'ENUM': 02051 case 'SET': 02052 return 'C'; 02053 02054 case 'TEXT': 02055 case 'LONGTEXT': 02056 case 'MEDIUMTEXT': 02057 return 'XL'; 02058 02059 case 'IMAGE': 02060 case 'LONGBLOB': 02061 case 'BLOB': 02062 case 'MEDIUMBLOB': 02063 return 'B'; 02064 02065 case 'YEAR': 02066 case 'DATE': 02067 return 'D'; 02068 02069 case 'TIME': 02070 case 'DATETIME': 02071 case 'TIMESTAMP': 02072 return 'T'; 02073 02074 case 'FLOAT': 02075 case 'DOUBLE': 02076 return 'F'; 02077 02078 case 'INT': 02079 case 'INTEGER': 02080 case 'TINYINT': 02081 case 'SMALLINT': 02082 case 'MEDIUMINT': 02083 case 'BIGINT': 02084 return 'I8'; // we always return I8 to be on the safe side. Under some circumstances the fields are to small otherwise... 02085 02086 default: 02087 return 'N'; 02088 } 02089 } 02090 02091 /** 02092 * Return actual MySQL type for meta field type 02093 * 02094 * @param string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype) 02095 * @return string native type as reported as in mysqldump files, uppercase 02096 */ 02097 public function MySQLActualType($meta) { 02098 switch (strtoupper($meta)) { 02099 case 'C': 02100 return 'VARCHAR'; 02101 case 'XL': 02102 case 'X': 02103 return 'LONGTEXT'; 02104 02105 case 'C2': 02106 return 'VARCHAR'; 02107 case 'X2': 02108 return 'LONGTEXT'; 02109 02110 case 'B': 02111 return 'LONGBLOB'; 02112 02113 case 'D': 02114 return 'DATE'; 02115 case 'T': 02116 return 'DATETIME'; 02117 case 'L': 02118 return 'TINYINT'; 02119 02120 case 'I': 02121 case 'I1': 02122 case 'I2': 02123 case 'I4': 02124 case 'I8': 02125 return 'BIGINT'; // we only have I8 in DBAL, see MySQLMetaType() 02126 02127 case 'F': 02128 return 'DOUBLE'; 02129 case 'N': 02130 return 'NUMERIC'; 02131 02132 default: 02133 return $meta; 02134 } 02135 } 02136 02137 02138 /************************************** 02139 * 02140 * SQL wrapper functions (Overriding parent methods) 02141 * (For use in your applications) 02142 * 02143 **************************************/ 02144 02145 /** 02146 * Returns the error status on the most recent sql() execution (based on $this->lastHandlerKey) 02147 * 02148 * @return string Handler error strings 02149 */ 02150 public function sql_error() { 02151 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) { 02152 case 'native': 02153 $output = mysql_error($this->handlerInstance[$this->lastHandlerKey]['link']); 02154 break; 02155 case 'adodb': 02156 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorMsg(); 02157 break; 02158 case 'userdefined': 02159 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_error(); 02160 break; 02161 } 02162 return $output; 02163 } 02164 02165 /** 02166 * Returns the error number on the most recent sql() execution (based on $this->lastHandlerKey) 02167 * 02168 * @return int Handler error number 02169 */ 02170 public function sql_errno() { 02171 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) { 02172 case 'native': 02173 $output = mysql_errno($this->handlerInstance[$this->lastHandlerKey]['link']); 02174 break; 02175 case 'adodb': 02176 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorNo(); 02177 break; 02178 case 'userdefined': 02179 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_errno(); 02180 break; 02181 } 02182 return $output; 02183 } 02184 02185 /** 02186 * Returns the number of selected rows. 02187 * 02188 * @param pointer Result pointer / DBAL object 02189 * @return integer Number of resulting rows. 02190 */ 02191 public function sql_num_rows(&$res) { 02192 if ($res === FALSE) { 02193 return 0; 02194 } 02195 02196 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native'; 02197 switch ($handlerType) { 02198 case 'native': 02199 $output = mysql_num_rows($res); 02200 break; 02201 case 'adodb': 02202 $output = method_exists($res, 'RecordCount') ? $res->RecordCount() : 0; 02203 break; 02204 case 'userdefined': 02205 $output = $res->sql_num_rows(); 02206 break; 02207 } 02208 return $output; 02209 } 02210 02211 /** 02212 * Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows. 02213 * 02214 * @param pointer MySQL result pointer (of SELECT query) / DBAL object 02215 * @return array Associative array of result row. 02216 */ 02217 public function sql_fetch_assoc(&$res) { 02218 $output = array(); 02219 02220 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : (is_resource($res) ? 'native' : FALSE); 02221 switch ($handlerType) { 02222 case 'native': 02223 $output = mysql_fetch_assoc($res); 02224 $tableList = $this->resourceIdToTableNameMap[(string) $res]; // Reading list of tables from SELECT query: 02225 break; 02226 case 'adodb': 02227 // Check if method exists for the current $res object. 02228 // If a table exists in TCA but not in the db, a error 02229 // occured because $res is not a valid object. 02230 if (method_exists($res, 'FetchRow')) { 02231 $output = $res->FetchRow(); 02232 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query: 02233 02234 // Removing all numeric/integer keys. 02235 // A workaround because in ADOdb we would need to know what we want before executing the query... 02236 // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So 02237 // we don't need to remove anything. 02238 if (is_array($output)) { 02239 if ($this->runningADOdbDriver('mssql')) { 02240 // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix. 02241 foreach ($output as $key => $value) { 02242 if ($value === ' ') { 02243 $output[$key] = ''; 02244 } 02245 } 02246 } else { 02247 foreach ($output as $key => $value) { 02248 if (is_integer($key)) { 02249 unset($output[$key]); 02250 } 02251 } 02252 } 02253 } 02254 } 02255 break; 02256 case 'userdefined': 02257 $output = $res->sql_fetch_assoc(); 02258 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query: 02259 break; 02260 } 02261 02262 // Table/Fieldname mapping: 02263 if (is_array($output)) { 02264 if ($tables = $this->map_needMapping($tableList, TRUE)) { 02265 $output = $this->map_assocArray($output, $tables, 1); 02266 } 02267 } 02268 02269 // Return result: 02270 return $output; 02271 } 02272 02273 /** 02274 * Returns an array that corresponds to the fetched row, or FALSE if there are no more rows. 02275 * The array contains the values in numerical indices. 02276 * 02277 * @param pointer MySQL result pointer (of SELECT query) / DBAL object 02278 * @return array Array with result rows. 02279 */ 02280 public function sql_fetch_row(&$res) { 02281 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native'; 02282 switch ($handlerType) { 02283 case 'native': 02284 $output = mysql_fetch_row($res); 02285 break; 02286 case 'adodb': 02287 // Check if method exists for the current $res object. 02288 // If a table exists in TCA but not in the db, a error 02289 // occured because $res is not a valid object. 02290 if (method_exists($res, 'FetchRow')) { 02291 $output = $res->FetchRow(); 02292 02293 // Removing all assoc. keys. 02294 // A workaround because in ADOdb we would need to know what we want before executing the query... 02295 // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So 02296 // we need to convert resultset. 02297 if (is_array($output)) { 02298 $keyIndex = 0; 02299 foreach ($output as $key => $value) { 02300 unset($output[$key]); 02301 if (is_integer($key) || $this->runningADOdbDriver('mssql')) { 02302 $output[$keyIndex] = $value; 02303 if ($value === ' ') { 02304 // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix. 02305 $output[$keyIndex] = ''; 02306 } 02307 $keyIndex++; 02308 } 02309 } 02310 } 02311 } 02312 break; 02313 case 'userdefined': 02314 $output = $res->sql_fetch_row(); 02315 break; 02316 } 02317 return $output; 02318 } 02319 02320 /** 02321 * Free result memory / unset result object 02322 * 02323 * @param pointer MySQL result pointer to free / DBAL object 02324 * @return boolean Returns TRUE on success or FALSE on failure. 02325 */ 02326 public function sql_free_result(&$res) { 02327 if ($res === FALSE) { 02328 return FALSE; 02329 } 02330 02331 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native'; 02332 switch ($handlerType) { 02333 case 'native': 02334 $output = mysql_free_result($res); 02335 break; 02336 case 'adodb': 02337 if (method_exists($res, 'Close')) { 02338 $res->Close(); 02339 unset($res); 02340 $output = TRUE; 02341 } else { 02342 $output = FALSE; 02343 } 02344 break; 02345 case 'userdefined': 02346 unset($res); 02347 break; 02348 } 02349 return $output; 02350 } 02351 02352 /** 02353 * Get the ID generated from the previous INSERT operation 02354 * 02355 * @return integer The uid of the last inserted record. 02356 */ 02357 public function sql_insert_id() { 02358 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) { 02359 case 'native': 02360 $output = mysql_insert_id($this->handlerInstance[$this->lastHandlerKey]['link']); 02361 break; 02362 case 'adodb': 02363 $output = $this->handlerInstance[$this->lastHandlerKey]->last_insert_id; 02364 break; 02365 case 'userdefined': 02366 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_insert_id(); 02367 break; 02368 } 02369 return $output; 02370 } 02371 02372 /** 02373 * Returns the number of rows affected by the last INSERT, UPDATE or DELETE query 02374 * 02375 * @return integer Number of rows affected by last query 02376 */ 02377 public function sql_affected_rows() { 02378 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) { 02379 case 'native': 02380 $output = mysql_affected_rows(); 02381 break; 02382 case 'adodb': 02383 $output = $this->handlerInstance[$this->lastHandlerKey]->Affected_Rows(); 02384 break; 02385 case 'userdefined': 02386 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_affected_rows(); 02387 break; 02388 } 02389 return $output; 02390 } 02391 02392 /** 02393 * Move internal result pointer 02394 * 02395 * @param pointer MySQL result pointer (of SELECT query) / DBAL object 02396 * @param integer Seek result number. 02397 * @return boolean Returns TRUE on success or FALSE on failure. 02398 */ 02399 public function sql_data_seek(&$res, $seek) { 02400 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native'; 02401 switch ($handlerType) { 02402 case 'native': 02403 $output = mysql_data_seek($res, $seek); 02404 break; 02405 case 'adodb': 02406 $output = $res->Move($seek); 02407 break; 02408 case 'userdefined': 02409 $output = $res->sql_data_seek($seek); 02410 break; 02411 } 02412 return $output; 02413 } 02414 02415 /** 02416 * Get the type of the specified field in a result 02417 * 02418 * If the first parameter is a string, it is used as table name for the lookup. 02419 * 02420 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name 02421 * @param integer Field index. In case of ADOdb a string (field name!) FIXME 02422 * @return string Returns the type of the specified field index 02423 */ 02424 public function sql_field_metatype($table, $field) { 02425 // If $table and/or $field are mapped, use the original names instead 02426 foreach ($this->mapping as $tableName => $tableMapInfo) { 02427 if (isset($tableMapInfo['mapTableName']) && $tableMapInfo['mapTableName'] === $table) { 02428 // Table name is mapped => use original name 02429 $table = $tableName; 02430 } 02431 02432 if (isset($tableMapInfo['mapFieldNames'])) { 02433 foreach ($tableMapInfo['mapFieldNames'] as $fieldName => $fieldMapInfo) { 02434 if ($fieldMapInfo === $field) { 02435 // Field name is mapped => use original name 02436 $field = $fieldName; 02437 } 02438 } 02439 } 02440 } 02441 02442 return $this->cache_fieldType[$table][$field]['metaType']; 02443 } 02444 02445 /** 02446 * Get the type of the specified field in a result 02447 * 02448 * If the first parameter is a string, it is used as table name for the lookup. 02449 * 02450 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name 02451 * @param integer Field index. In case of ADOdb a string (field name!) FIXME 02452 * @return string Returns the type of the specified field index 02453 */ 02454 public function sql_field_type(&$res, $pointer) { 02455 if ($res === null) { 02456 debug(array('no res in sql_field_type!')); 02457 return 'text'; 02458 } 02459 elseif (is_string($res)) { 02460 if ($res === 'tx_dbal_debuglog') return 'text'; 02461 $handlerType = 'adodb'; 02462 } 02463 else { 02464 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native'; 02465 } 02466 02467 switch ($handlerType) { 02468 case 'native': 02469 $output = mysql_field_type($res, $pointer); 02470 break; 02471 case 'adodb': 02472 if (is_string($pointer)) { 02473 $output = $this->cache_fieldType[$res][$pointer]['type']; 02474 } 02475 02476 break; 02477 case 'userdefined': 02478 $output = $res->sql_field_type($pointer); 02479 break; 02480 } 02481 02482 return $output; 02483 } 02484 02485 02486 /********** 02487 * 02488 * Legacy functions, bound to _DEFAULT handler. (Overriding parent methods) 02489 * Deprecated or still experimental. 02490 * 02491 **********/ 02492 02493 /** 02494 * Executes query (on DEFAULT handler!) 02495 * DEPRECATED - use exec_* functions from this class instead! 02496 * 02497 * @param string Database name 02498 * @param string Query to execute 02499 * @return pointer Result pointer 02500 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 02501 */ 02502 public function sql($db, $query) { 02503 t3lib_div::logDeprecatedFunction(); 02504 02505 return $this->sql_query($query); 02506 } 02507 02508 /** 02509 * Executes a query 02510 * EXPERIMENTAL - This method will make its best to handle the query correctly 02511 * but if it cannot, it will simply pass the query to DEFAULT handler. 02512 * 02513 * You should use exec_* function from this class instead! 02514 * If you don't, anything that does not use the _DEFAULT handler will probably break! 02515 * 02516 * This method was deprecated in TYPO3 4.1 but is considered experimental since TYPO3 4.4 02517 * as it tries to handle the query correctly anyway. 02518 * 02519 * @param string Query to execute 02520 * @return pointer Result pointer / DBAL object 02521 */ 02522 public function sql_query($query) { 02523 $globalConfig = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['dbal']); 02524 if ($globalConfig['sql_query.passthrough']) { 02525 return parent::sql_query($query); 02526 } 02527 02528 // This method is heavily used by Extbase, try to handle it with DBAL-native methods 02529 $queryParts = $this->SQLparser->parseSQL($query); 02530 if (is_array($queryParts) && t3lib_div::inList('SELECT,UPDATE,INSERT,DELETE', $queryParts['type'])) { 02531 return $this->exec_query($queryParts); 02532 } 02533 02534 switch ($this->handlerCfg['_DEFAULT']['type']) { 02535 case 'native': 02536 $sqlResult = mysql_query($query, $this->handlerInstance['_DEFAULT']['link']); 02537 break; 02538 case 'adodb': 02539 $sqlResult = $this->handlerInstance['_DEFAULT']->Execute($query); 02540 $sqlResult->TYPO3_DBAL_handlerType = 'adodb'; 02541 break; 02542 case 'userdefined': 02543 $sqlResult = $this->handlerInstance['_DEFAULT']->sql_query($query); 02544 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined'; 02545 break; 02546 } 02547 02548 if ($this->printErrors && $this->sql_error()) { 02549 debug(array($this->lastQuery, $this->sql_error())); 02550 } 02551 02552 return $sqlResult; 02553 } 02554 02555 /** 02556 * Opening the _DEFAULT connection handler to the database. 02557 * This is typically done by the scripts "init.php" in the backend or "index_ts.php" in the frontend (tslib_fe->connectToMySQL()) 02558 * You wouldn't need to use this at any time - let TYPO3 core handle this. 02559 * 02560 * @param string Database host IP/domain 02561 * @param string Username to connect with. 02562 * @param string Password to connect with. 02563 * @return mixed Returns handler connection value 02564 * @see handler_init() 02565 */ 02566 public function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password) { 02567 // Overriding the _DEFAULT handler configuration of username, password, localhost and database name: 02568 $this->handlerCfg['_DEFAULT']['config']['username'] = $TYPO3_db_username; 02569 $this->handlerCfg['_DEFAULT']['config']['password'] = $TYPO3_db_password; 02570 $this->handlerCfg['_DEFAULT']['config']['host'] = $TYPO3_db_host; 02571 $this->handlerCfg['_DEFAULT']['config']['database'] = TYPO3_db; 02572 02573 // Initializing and output value: 02574 $sqlResult = $this->handler_init('_DEFAULT'); 02575 return $sqlResult; 02576 } 02577 02578 /** 02579 * Select database for _DEFAULT handler. 02580 * 02581 * @param string Database to connect to. 02582 * @return boolean Always returns TRUE; function is obsolete, database selection is made in handler_init() function! 02583 */ 02584 public function sql_select_db($TYPO3_db) { 02585 return TRUE; 02586 } 02587 02588 02589 /************************************** 02590 * 02591 * SQL admin functions 02592 * (For use in the Install Tool and Extension Manager) 02593 * 02594 **************************************/ 02595 02596 /** 02597 * Listing databases from current MySQL connection. NOTICE: It WILL try to select those databases and thus break selection of current database. 02598 * Use in Install Tool only! 02599 * Usage count/core: 1 02600 * 02601 * @return array Each entry represents a database name 02602 */ 02603 public function admin_get_dbs() { 02604 $dbArr = array(); 02605 switch ($this->handlerCfg['_DEFAULT']['type']) { 02606 case 'native': 02607 $db_list = mysql_list_dbs($this->link); 02608 while ($row = mysql_fetch_object($db_list)) { 02609 if ($this->sql_select_db($row->Database)) { 02610 $dbArr[] = $row->Database; 02611 } 02612 } 02613 break; 02614 case 'adodb': 02615 // check needed for install tool - otherwise it will just die because the call to 02616 // MetaDatabases is done on a stdClass instance 02617 if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaDatabases')) { 02618 $sqlDBs = $this->handlerInstance['_DEFAULT']->MetaDatabases(); 02619 if (is_array($sqlDBs)) { 02620 foreach ($sqlDBs as $k => $theDB) { 02621 $dbArr[] = $theDB; 02622 } 02623 } 02624 } 02625 break; 02626 case 'userdefined': 02627 $dbArr = $this->handlerInstance['_DEFAULT']->admin_get_tables(); 02628 break; 02629 } 02630 02631 return $dbArr; 02632 } 02633 02634 /** 02635 * Returns the list of tables from the system (quering the DBMSs) 02636 * It looks up all tables from the DBMS of the _DEFAULT handler and then add all tables *configured* to be managed by other handlers 02637 * 02638 * When fetching the tables, it skips tables whose names begin with BIN$, as this is taken as a table coming from the "Recycle Bin" on Oracle. 02639 * 02640 * @return array Tables in an array (tablename is in both key and value) 02641 * @todo Should the check for Oracle Recycle Bin stuff be moved elsewhere? 02642 * @todo Should return table details in value! see t3lib_db::admin_get_tables() 02643 */ 02644 public function admin_get_tables() { 02645 $whichTables = array(); 02646 02647 // Getting real list of tables: 02648 switch ($this->handlerCfg['_DEFAULT']['type']) { 02649 case 'native': 02650 $tables_result = mysql_query('SHOW TABLE STATUS FROM `' . TYPO3_db . '`', $this->handlerInstance['_DEFAULT']['link']); 02651 if (!$this->sql_error()) { 02652 while ($theTable = $this->sql_fetch_assoc($tables_result)) { 02653 $whichTables[$theTable['Name']] = $theTable; 02654 } 02655 } 02656 break; 02657 case 'adodb': 02658 // check needed for install tool - otherwise it will just die because the call to 02659 // MetaTables is done on a stdClass instance 02660 if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaTables')) { 02661 $sqlTables = $this->handlerInstance['_DEFAULT']->MetaTables('TABLES'); 02662 while (list($k, $theTable) = each($sqlTables)) { 02663 if (preg_match('/BIN\$/', $theTable)) continue; // skip tables from the Oracle 10 Recycle Bin 02664 $whichTables[$theTable] = $theTable; 02665 } 02666 } 02667 break; 02668 case 'userdefined': 02669 $whichTables = $this->handlerInstance['_DEFAULT']->admin_get_tables(); 02670 break; 02671 } 02672 02673 // Check mapping: 02674 if (is_array($this->mapping) && count($this->mapping)) { 02675 02676 // Mapping table names in reverse, first getting list of real table names: 02677 $tMap = array(); 02678 foreach ($this->mapping as $tN => $tMapInfo) { 02679 if (isset($tMapInfo['mapTableName'])) { 02680 $tMap[$tMapInfo['mapTableName']] = $tN; 02681 } 02682 } 02683 02684 // Do mapping: 02685 $newList = array(); 02686 foreach ($whichTables as $tN => $tDefinition) { 02687 if (isset($tMap[$tN])) { 02688 $tN = $tMap[$tN]; 02689 } 02690 $newList[$tN] = $tDefinition; 02691 } 02692 02693 $whichTables = $newList; 02694 } 02695 02696 // Adding tables configured to reside in other DBMS (handler by other handlers than the default): 02697 if (is_array($this->table2handlerKeys)) { 02698 foreach ($this->table2handlerKeys as $key => $handlerKey) { 02699 $whichTables[$key] = $key; 02700 } 02701 } 02702 02703 return $whichTables; 02704 } 02705 02706 /** 02707 * Returns information about each field in the $table (quering the DBMS) 02708 * In a DBAL this should look up the right handler for the table and return compatible information 02709 * This function is important not only for the Install Tool but probably for DBALs as well since they might need to look up table specific information in order to construct correct queries. In such cases this information should probably be cached for quick delivery 02710 * 02711 * @param string Table name 02712 * @return array Field information in an associative array with fieldname => field row 02713 */ 02714 public function admin_get_fields($tableName) { 02715 $output = array(); 02716 02717 // Do field mapping if needed: 02718 $ORIG_tableName = $tableName; 02719 if ($tableArray = $this->map_needMapping($tableName)) { 02720 02721 // Table name: 02722 if ($this->mapping[$tableName]['mapTableName']) { 02723 $tableName = $this->mapping[$tableName]['mapTableName']; 02724 } 02725 } 02726 02727 // Find columns 02728 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 02729 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 02730 case 'native': 02731 $columns_res = mysql_query('SHOW columns FROM ' . $tableName, $this->handlerInstance[$this->lastHandlerKey]['link']); 02732 while ($fieldRow = mysql_fetch_assoc($columns_res)) { 02733 $output[$fieldRow['Field']] = $fieldRow; 02734 } 02735 break; 02736 case 'adodb': 02737 $fieldRows = $this->handlerInstance[$this->lastHandlerKey]->MetaColumns($tableName, FALSE); 02738 if (is_array($fieldRows)) { 02739 foreach ($fieldRows as $k => $fieldRow) { 02740 settype($fieldRow, 'array'); 02741 $fieldRow['Field'] = $fieldRow['name']; 02742 $ntype = $this->MySQLActualType($this->MetaType($fieldRow['type'], $tableName)); 02743 $ntype .= (($fieldRow['max_length'] != -1) ? (($ntype == 'INT') ? '(11)' : '(' . $fieldRow['max_length'] . ')') : ''); 02744 $fieldRow['Type'] = strtolower($ntype); 02745 $fieldRow['Null'] = ''; 02746 $fieldRow['Key'] = ''; 02747 $fieldRow['Default'] = $fieldRow['default_value']; 02748 $fieldRow['Extra'] = ''; 02749 $output[$fieldRow['name']] = $fieldRow; 02750 } 02751 } 02752 break; 02753 case 'userdefined': 02754 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_fields($tableName); 02755 break; 02756 } 02757 02758 // mapping should be done: 02759 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) { 02760 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']); 02761 02762 $newOutput = array(); 02763 foreach ($output as $fN => $fInfo) { 02764 if (isset($revFields[$fN])) { 02765 $fN = $revFields[$fN]; 02766 $fInfo['Field'] = $fN; 02767 } 02768 $newOutput[$fN] = $fInfo; 02769 } 02770 $output = $newOutput; 02771 } 02772 02773 return $output; 02774 } 02775 02776 /** 02777 * Returns information about each index key in the $table (quering the DBMS) 02778 * In a DBAL this should look up the right handler for the table and return compatible information 02779 * 02780 * @param string Table name 02781 * @return array Key information in a numeric array 02782 */ 02783 public function admin_get_keys($tableName) { 02784 $output = array(); 02785 02786 // Do field mapping if needed: 02787 $ORIG_tableName = $tableName; 02788 if ($tableArray = $this->map_needMapping($tableName)) { 02789 02790 // Table name: 02791 if ($this->mapping[$tableName]['mapTableName']) { 02792 $tableName = $this->mapping[$tableName]['mapTableName']; 02793 } 02794 } 02795 02796 // Find columns 02797 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName); 02798 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 02799 case 'native': 02800 $keyRes = mysql_query('SHOW keys FROM ' . $tableName, $this->handlerInstance[$this->lastHandlerKey]['link']); 02801 while ($keyRow = mysql_fetch_assoc($keyRes)) { 02802 $output[] = $keyRow; 02803 } 02804 break; 02805 case 'adodb': 02806 $keyRows = $this->handlerInstance[$this->lastHandlerKey]->MetaIndexes($tableName); 02807 if ($keyRows !== FALSE) { 02808 while (list($k, $theKey) = each($keyRows)) { 02809 $theKey['Table'] = $tableName; 02810 $theKey['Non_unique'] = (int) !$theKey['unique']; 02811 $theKey['Key_name'] = str_replace($tableName . '_', '', $k); 02812 02813 // the following are probably not needed anyway... 02814 $theKey['Collation'] = ''; 02815 $theKey['Cardinality'] = ''; 02816 $theKey['Sub_part'] = ''; 02817 $theKey['Packed'] = ''; 02818 $theKey['Null'] = ''; 02819 $theKey['Index_type'] = ''; 02820 $theKey['Comment'] = ''; 02821 02822 // now map multiple fields into multiple rows (we mimic MySQL, remember...) 02823 $keycols = $theKey['columns']; 02824 while (list($c, $theCol) = each($keycols)) { 02825 $theKey['Seq_in_index'] = $c + 1; 02826 $theKey['Column_name'] = $theCol; 02827 $output[] = $theKey; 02828 } 02829 } 02830 } 02831 $priKeyRow = $this->handlerInstance[$this->lastHandlerKey]->MetaPrimaryKeys($tableName); 02832 $theKey = array(); 02833 $theKey['Table'] = $tableName; 02834 $theKey['Non_unique'] = 0; 02835 $theKey['Key_name'] = 'PRIMARY'; 02836 02837 // the following are probably not needed anyway... 02838 $theKey['Collation'] = ''; 02839 $theKey['Cardinality'] = ''; 02840 $theKey['Sub_part'] = ''; 02841 $theKey['Packed'] = ''; 02842 $theKey['Null'] = ''; 02843 $theKey['Index_type'] = ''; 02844 $theKey['Comment'] = ''; 02845 02846 // now map multiple fields into multiple rows (we mimic MySQL, remember...) 02847 if ($priKeyRow !== FALSE) { 02848 while (list($c, $theCol) = each($priKeyRow)) { 02849 $theKey['Seq_in_index'] = $c + 1; 02850 $theKey['Column_name'] = $theCol; 02851 $output[] = $theKey; 02852 } 02853 } 02854 break; 02855 case 'userdefined': 02856 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_keys($tableName); 02857 break; 02858 } 02859 02860 // mapping should be done: 02861 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) { 02862 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']); 02863 02864 $newOutput = array(); 02865 foreach ($output as $kN => $kInfo) { 02866 // Table: 02867 $kInfo['Table'] = $ORIG_tableName; 02868 02869 // Column 02870 if (isset($revFields[$kInfo['Column_name']])) { 02871 $kInfo['Column_name'] = $revFields[$kInfo['Column_name']]; 02872 } 02873 02874 // Write it back: 02875 $newOutput[$kN] = $kInfo; 02876 } 02877 $output = $newOutput; 02878 } 02879 02880 return $output; 02881 } 02882 02883 /** 02884 * mysql() wrapper function, used by the Install Tool. 02885 * 02886 * @return array 02887 */ 02888 public function admin_get_charsets() { 02889 return array(); 02890 } 02891 02892 /** 02893 * mysql() wrapper function, used by the Install Tool and EM for all queries regarding management of the database! 02894 * 02895 * @param string Query to execute 02896 * @return pointer Result pointer 02897 */ 02898 public function admin_query($query) { 02899 $parsedQuery = $this->SQLparser->parseSQL($query); 02900 $ORIG_table = $parsedQuery['TABLE']; 02901 02902 if (is_array($parsedQuery)) { 02903 02904 // Process query based on type: 02905 switch ($parsedQuery['type']) { 02906 case 'CREATETABLE': 02907 case 'ALTERTABLE': 02908 case 'DROPTABLE': 02909 if (file_exists(PATH_typo3conf . 'temp_fieldInfo.php')) unlink(PATH_typo3conf . 'temp_fieldInfo.php'); 02910 $this->map_genericQueryParsed($parsedQuery); 02911 break; 02912 case 'INSERT': 02913 case 'TRUNCATETABLE': 02914 $this->map_genericQueryParsed($parsedQuery); 02915 break; 02916 case 'CREATEDATABASE': 02917 die('Creating a database with DBAL is not supported. Did you really read the manual?'); 02918 break; 02919 default: 02920 die('ERROR: Invalid Query type (' . $parsedQuery['type'] . ') for ->admin_query() function!: "' . htmlspecialchars($query) . '"'); 02921 break; 02922 } 02923 02924 // Setting query array (for other applications to access if needed) 02925 $this->lastParsedAndMappedQueryArray = $parsedQuery; 02926 02927 // Execute query (based on handler derived from the TABLE name which we actually know for once!) 02928 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_table); 02929 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 02930 case 'native': 02931 // Compiling query: 02932 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray); 02933 02934 //if ($this->lastParsedAndMappedQueryArray['type'] == 'INSERT') { 02935 // return mysql_query($compiledQuery, $this->link); 02936 //} 02937 return mysql_query($compiledQuery, $this->link); 02938 break; 02939 case 'adodb': 02940 // Compiling query: 02941 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray); 02942 switch ($this->lastParsedAndMappedQueryArray['type']) { 02943 case 'INSERT': 02944 return $this->exec_INSERTquery($this->lastParsedAndMappedQueryArray['TABLE'], $compiledQuery); 02945 case 'TRUNCATETABLE': 02946 return $this->exec_TRUNCATEquery($this->lastParsedAndMappedQueryArray['TABLE']); 02947 } 02948 return $this->handlerInstance[$this->lastHandlerKey]->DataDictionary->ExecuteSQLArray($compiledQuery); 02949 break; 02950 case 'userdefined': 02951 // Compiling query: 02952 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray); 02953 02954 return $this->handlerInstance[$this->lastHandlerKey]->admin_query($compiledQuery); 02955 break; 02956 } 02957 } else die('ERROR: Query could not be parsed: "' . htmlspecialchars($parsedQuery) . '". Query: "' . htmlspecialchars($query) . '"'); 02958 } 02959 02960 02961 /************************************ 02962 * 02963 * Handler management 02964 * 02965 **************************************/ 02966 02967 /** 02968 * Return the handler key pointing to an appropriate database handler as found in $this->handlerCfg array 02969 * Notice: TWO or more tables in the table list MUST use the SAME handler key - otherwise a fatal error is thrown! (Logically, no database can possibly join two tables from separate sources!) 02970 * 02971 * @param string Table list, eg. "pages" or "pages, tt_content" or "pages AS A, tt_content AS B" 02972 * @return string Handler key (see $this->handlerCfg array) for table 02973 */ 02974 public function handler_getFromTableList($tableList) { 02975 02976 $key = $tableList; 02977 02978 if (!isset($this->cache_handlerKeyFromTableList[$key])) { 02979 02980 // Get tables separated: 02981 $_tableList = $tableList; 02982 $tableArray = $this->SQLparser->parseFromTables($_tableList); 02983 02984 // If success, traverse the tables: 02985 if (is_array($tableArray) && count($tableArray)) { 02986 $outputHandlerKey = ''; 02987 02988 foreach ($tableArray as $vArray) { 02989 // Find handler key, select "_DEFAULT" if none is specifically configured: 02990 $handlerKey = $this->table2handlerKeys[$vArray['table']] ? $this->table2handlerKeys[$vArray['table']] : '_DEFAULT'; 02991 02992 // In case of separate handler keys for joined tables: 02993 if ($outputHandlerKey && $handlerKey != $outputHandlerKey) { 02994 die('DBAL fatal error: Tables in this list "' . $tableList . '" didn\'t use the same DB handler!'); 02995 } 02996 02997 $outputHandlerKey = $handlerKey; 02998 } 02999 03000 // Check initialized state; if handler is NOT initialized (connected) then we will connect it! 03001 if (!isset($this->handlerInstance[$outputHandlerKey])) { 03002 $this->handler_init($outputHandlerKey); 03003 } 03004 03005 // Return handler key: 03006 $this->cache_handlerKeyFromTableList[$key] = $outputHandlerKey; 03007 } else { 03008 die('DBAL fatal error: No handler found in handler_getFromTableList() for: "' . $tableList . '" (' . $tableArray . ')'); 03009 } 03010 } 03011 03012 return $this->cache_handlerKeyFromTableList[$key]; 03013 } 03014 03015 /** 03016 * Initialize handler (connecting to database) 03017 * 03018 * @param string Handler key 03019 * @return boolean If connection went well, return TRUE 03020 * @see handler_getFromTableList() 03021 */ 03022 public function handler_init($handlerKey) { 03023 03024 // Find handler configuration: 03025 $cfgArray = $this->handlerCfg[$handlerKey]; 03026 $handlerType = (string) $cfgArray['type']; 03027 $output = FALSE; 03028 03029 if (is_array($cfgArray)) { 03030 if (!$cfgArray['config']['database']) { 03031 // Configuration is incomplete 03032 return; 03033 } 03034 switch ($handlerType) { 03035 case 'native': 03036 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) { 03037 $link = mysql_connect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], TRUE); 03038 } else { 03039 $link = mysql_pconnect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password']); 03040 } 03041 03042 // Set handler instance: 03043 $this->handlerInstance[$handlerKey] = array('handlerType' => 'native', 'link' => $link); 03044 03045 // If link succeeded: 03046 if ($link) { 03047 // For default, set ->link (see t3lib_DB) 03048 if ($handlerKey == '_DEFAULT') { 03049 $this->link = $link; 03050 } 03051 03052 // Select database as well: 03053 if (mysql_select_db($cfgArray['config']['database'], $link)) { 03054 $output = TRUE; 03055 } 03056 $setDBinit = t3lib_div::trimExplode(LF, str_replace("' . LF . '", LF, $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit']), TRUE); 03057 foreach ($setDBinit as $v) { 03058 if (mysql_query($v, $link) === FALSE) { 03059 t3lib_div::sysLog('Could not initialize DB connection with query "' . $v . '".', 'Core', 3); 03060 } 03061 } 03062 } else { 03063 t3lib_div::sysLog('Could not connect to MySQL server ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4); 03064 } 03065 break; 03066 case 'adodb': 03067 $output = TRUE; 03068 require_once(t3lib_extMgm::extPath('adodb') . 'adodb/adodb.inc.php'); 03069 if (!defined('ADODB_FORCE_NULLS')) define('ADODB_FORCE_NULLS', 1); 03070 $GLOBALS['ADODB_FORCE_TYPE'] = ADODB_FORCE_VALUE; 03071 $GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_BOTH; 03072 03073 $this->handlerInstance[$handlerKey] = &ADONewConnection($cfgArray['config']['driver']); 03074 03075 // Set driver-specific options 03076 if (isset($cfgArray['config']['driverOptions'])) { 03077 foreach ($cfgArray['config']['driverOptions'] as $optionName => $optionValue) { 03078 $optionSetterName = 'set' . ucfirst($optionName); 03079 if (method_exists($this->handlerInstance[$handlerKey], $optionSetterName)) { 03080 $this->handlerInstance[$handlerKey]->$optionSetterName($optionValue); 03081 } else { 03082 $this->handlerInstance[$handlerKey]->$optionName = $optionValue; 03083 } 03084 } 03085 } 03086 03087 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) { 03088 $this->handlerInstance[$handlerKey]->Connect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']); 03089 } else { 03090 $this->handlerInstance[$handlerKey]->PConnect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']); 03091 } 03092 if (!$this->handlerInstance[$handlerKey]->isConnected()) { 03093 $dsn = $cfgArray['config']['driver'] . '://' . $cfgArray['config']['username'] . 03094 (strlen($cfgArray['config']['password']) ? ':XXXX@' : '') . 03095 $cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : '') . '/' . $cfgArray['config']['database'] . 03096 ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'] ? '' : '?persistent=1'); 03097 t3lib_div::sysLog('Could not connect to DB server using ADOdb on ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4); 03098 error_log('DBAL error: Connection to ' . $dsn . ' failed. Maybe PHP doesn\'t support the database?'); 03099 $output = FALSE; 03100 } else { 03101 $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]); 03102 $this->handlerInstance[$handlerKey]->last_insert_id = 0; 03103 if (isset($cfgArray['config']['sequenceStart'])) { 03104 $this->handlerInstance[$handlerKey]->sequenceStart = $cfgArray['config']['sequenceStart']; 03105 } else { 03106 $this->handlerInstance[$handlerKey]->sequenceStart = 1; 03107 } 03108 } 03109 break; 03110 case 'userdefined': 03111 // Find class file: 03112 $fileName = t3lib_div::getFileAbsFileName($cfgArray['config']['classFile']); 03113 if (@is_file($fileName)) { 03114 require_once($fileName); 03115 } else die('DBAL error: "' . $fileName . '" was not a file to include.'); 03116 03117 // Initialize: 03118 $this->handlerInstance[$handlerKey] = t3lib_div::makeInstance($cfgArray['config']['class']); 03119 $this->handlerInstance[$handlerKey]->init($cfgArray, $this); 03120 03121 if (is_object($this->handlerInstance[$handlerKey])) { 03122 $output = TRUE; 03123 } 03124 break; 03125 default: 03126 die('ERROR: Invalid handler type: "' . $cfgArray['type'] . '"'); 03127 break; 03128 } 03129 03130 return $output; 03131 } else die('ERROR: No handler for key "' . $handlerKey . '"'); 03132 } 03133 03134 03135 /** 03136 * Checks if database is connected. 03137 * 03138 * @return boolean 03139 */ 03140 public function isConnected() { 03141 $result = FALSE; 03142 switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) { 03143 case 'native': 03144 $result = is_resource($this->link); 03145 break; 03146 case 'adodb': 03147 case 'userdefined': 03148 $result = is_object($this->handlerInstance[$this->lastHandlerKey]) && $this->handlerInstance[$this->lastHandlerKey]->isConnected(); 03149 break; 03150 } 03151 return $result; 03152 } 03153 03154 03155 /** 03156 * Checks whether the DBAL is currently inside an operation running on the "native" DB handler (i.e. MySQL) 03157 * 03158 * @return boolean True if running on "native" DB handler (i.e. MySQL) 03159 */ 03160 public function runningNative() { 03161 return ((string) $this->handlerCfg[$this->lastHandlerKey]['type']==='native'); 03162 } 03163 03164 03165 /** 03166 * Checks whether the ADOdb handler is running with a driver that contains the argument 03167 * 03168 * @param string $driver Driver name, matched with strstr(). 03169 * @return boolean True if running with the given driver 03170 */ 03171 public function runningADOdbDriver($driver) { 03172 return strstr($this->handlerCfg[$this->lastHandlerKey]['config']['driver'], $driver); 03173 } 03174 03175 03176 /************************************ 03177 * 03178 * Table/Field mapping 03179 * 03180 **************************************/ 03181 03182 /** 03183 * Checks if mapping is needed for a table(list) 03184 * 03185 * @param string List of tables in query 03186 * @param boolean If TRUE, it will check only if FIELDs are configured and ignore the mapped table name if any. 03187 * @param array Parsed list of tables, should be passed as reference to be reused and prevent double parsing 03188 * @return mixed Returns an array of table names (parsed version of input table) if mapping is needed, otherwise just FALSE. 03189 */ 03190 protected function map_needMapping($tableList, $fieldMappingOnly = FALSE, array &$parsedTableList = array()) { 03191 $key = $tableList . '|' . $fieldMappingOnly; 03192 if (!isset($this->cache_mappingFromTableList[$key])) { 03193 $this->cache_mappingFromTableList[$key] = FALSE; // Default: 03194 03195 $tables = $this->SQLparser->parseFromTables($tableList); 03196 if (is_array($tables)) { 03197 $parsedTableList = $tables; 03198 foreach ($tables as $tableCfg) { 03199 if ($fieldMappingOnly) { 03200 if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) { 03201 $this->cache_mappingFromTableList[$key] = $tables; 03202 } elseif (is_array($tableCfg['JOIN'])) { 03203 foreach ($tableCfg['JOIN'] as $join) { 03204 if (is_array($this->mapping[$join['withTable']]['mapFieldNames'])) { 03205 $this->cache_mappingFromTableList[$key] = $tables; 03206 break; 03207 } 03208 } 03209 } 03210 } else { 03211 if (is_array($this->mapping[$tableCfg['table']])) { 03212 $this->cache_mappingFromTableList[$key] = $tables; 03213 } elseif (is_array($tableCfg['JOIN'])) { 03214 foreach ($tableCfg['JOIN'] as $join) { 03215 if (is_array($this->mapping[$join['withTable']])) { 03216 $this->cache_mappingFromTableList[$key] = $tables; 03217 break; 03218 } 03219 } 03220 } 03221 } 03222 } 03223 } 03224 } 03225 03226 return $this->cache_mappingFromTableList[$key]; 03227 } 03228 03229 /** 03230 * Takes an associated array with field => value pairs and remaps the field names if configured for this table in $this->mapping array. 03231 * Be careful not to map a field name to another existing fields name (although you can use this to swap fieldnames of course...:-) 03232 * Observe mapping problems with join-results (more than one table): Joined queries should always prefix the table name to avoid problems with this. 03233 * Observe that alias fields are not mapped of course (should not be a problem though) 03234 * 03235 * @param array Input array, associative keys 03236 * @param array Array of tables from the query. Normally just one table; many tables in case of a join. NOTICE: for multiple tables (with joins) there MIGHT occur trouble with fields of the same name in the two tables: This function traverses the mapping information for BOTH tables and applies mapping without checking from which table the field really came! 03237 * @param boolean If TRUE, reverse direction. Default direction is to map an array going INTO the database (thus mapping TYPO3 fieldnames to PHYSICAL field names!) 03238 * @return array Output array, with mapped associative keys. 03239 */ 03240 protected function map_assocArray($input, $tables, $rev = FALSE) { 03241 // Traverse tables from query (hopefully only one table): 03242 foreach ($tables as $tableCfg) { 03243 $tableKey = $this->getMappingKey($tableCfg['table']); 03244 if (is_array($this->mapping[$tableKey]['mapFieldNames'])) { 03245 03246 // Get the map (reversed if needed): 03247 if ($rev) { 03248 $theMap = array_flip($this->mapping[$tableKey]['mapFieldNames']); 03249 } else { 03250 $theMap = $this->mapping[$tableKey]['mapFieldNames']; 03251 } 03252 03253 // Traverse selected record, map fieldnames: 03254 $output = array(); 03255 foreach ($input as $fN => $value) { 03256 03257 // Set the field name, change it if found in mapping array: 03258 if ($theMap[$fN]) { 03259 $newKey = $theMap[$fN]; 03260 } else { 03261 $newKey = $fN; 03262 } 03263 03264 // Set value to fieldname: 03265 $output[$newKey] = $value; 03266 } 03267 03268 // When done, override the $input array with the result: 03269 $input = $output; 03270 } 03271 } 03272 03273 // Return input array (which might have been altered in the mean time) 03274 return $input; 03275 } 03276 03277 /** 03278 * Remaps table/field names in a SELECT query's parts 03279 * 03280 * @param mixed Either parsed list of tables (SQLparser->parseFromTables()) or list of fields to select from the table. This is what comes right after "SELECT ...". Required value. 03281 * @param string Table(s) from which to select. This is what comes right after "FROM ...". Require value. 03282 * @param string Where clause. This is what comes right after "WHERE ...". Can be blank. 03283 * @param string Group by field(s) 03284 * @param string Order by field(s) 03285 * @return void 03286 * @see exec_SELECTquery() 03287 */ 03288 protected function map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy) { 03289 // Backup current mapping as it may be altered if aliases on mapped tables are found 03290 $backupMapping = $this->mapping; 03291 03292 // Tables: 03293 $tables = is_array($from_table) ? $from_table : $this->SQLparser->parseFromTables($from_table); 03294 $defaultTable = $tables[0]['table']; 03295 // Prepare mapping for aliased tables. This will copy the definition of the original table name. 03296 // The alias is prefixed with a database-incompatible character to prevent naming clash with real table name 03297 // Further access to $this->mapping should be made through $this->getMappingKey() method 03298 foreach ($tables as $k => $v) { 03299 if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) { 03300 $mappingKey = $this->getFreeMappingKey($v['as']); 03301 $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames']; 03302 } 03303 if (is_array($v['JOIN'])) { 03304 foreach ($v['JOIN'] as $joinCnt => $join) { 03305 if ($join['as'] && is_array($this->mapping[$join['withTable']]['mapFieldNames'])) { 03306 $mappingKey = $this->getFreeMappingKey($join['as']); 03307 $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$join['withTable']]['mapFieldNames']; 03308 } 03309 } 03310 } 03311 } 03312 foreach ($tables as $k => $v) { 03313 $tableKey = $this->getMappingKey($v['table']); 03314 if ($this->mapping[$tableKey]['mapTableName']) { 03315 $tables[$k]['table'] = $this->mapping[$tableKey]['mapTableName']; 03316 } 03317 // Mapping JOINS 03318 if (is_array($v['JOIN'])) { 03319 foreach ($v['JOIN'] as $joinCnt => $join) { 03320 // Mapping withTable of the JOIN 03321 $withTableKey = $this->getMappingKey($join['withTable']); 03322 if ($this->mapping[$withTableKey]['mapTableName']) { 03323 $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$withTableKey]['mapTableName']; 03324 } 03325 $onPartsArray = array(); 03326 // Mapping ON parts of the JOIN 03327 if (is_array($tables[$k]['JOIN'][$joinCnt]['ON'])) { 03328 foreach ($tables[$k]['JOIN'][$joinCnt]['ON'] as &$condition) { 03329 // Left side of the comparator 03330 $leftTableKey = $this->getMappingKey($condition['left']['table']); 03331 if (isset($this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']])) { 03332 $condition['left']['field'] = $this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']]; 03333 } 03334 if (isset($this->mapping[$leftTableKey]['mapTableName'])) { 03335 $condition['left']['table'] = $this->mapping[$leftTableKey]['mapTableName']; 03336 } 03337 // Right side of the comparator 03338 $rightTableKey = $this->getMappingKey($condition['right']['table']); 03339 if (isset($this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']])) { 03340 $condition['right']['field'] = $this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']]; 03341 } 03342 if (isset($this->mapping[$rightTableKey]['mapTableName'])) { 03343 $condition['right']['table'] = $this->mapping[$rightTableKey]['mapTableName']; 03344 } 03345 } 03346 } 03347 } 03348 } 03349 } 03350 $fromParts = $tables; 03351 03352 // Where clause: 03353 $parameterReferences = array(); 03354 $whereParts = $this->SQLparser->parseWhereClause($where_clause, '', $parameterReferences); 03355 $this->map_sqlParts($whereParts, $defaultTable); 03356 03357 // Select fields: 03358 $selectParts = $this->SQLparser->parseFieldList($select_fields); 03359 $this->map_sqlParts($selectParts, $defaultTable); 03360 03361 // Group By fields 03362 $groupByParts = $this->SQLparser->parseFieldList($groupBy); 03363 $this->map_sqlParts($groupByParts, $defaultTable); 03364 03365 // Order By fields 03366 $orderByParts = $this->SQLparser->parseFieldList($orderBy); 03367 $this->map_sqlParts($orderByParts, $defaultTable); 03368 03369 // Restore the original mapping 03370 $this->mapping = $backupMapping; 03371 03372 return array($selectParts, $fromParts, $whereParts, $groupByParts, $orderByParts, $parameterReferences); 03373 } 03374 03375 /** 03376 * Returns the key to be used when retrieving information from $this->mapping. This ensures 03377 * that mapping from aliased tables is properly retrieved. 03378 * 03379 * @param string $tableName 03380 * @return string 03381 */ 03382 protected function getMappingKey($tableName) { 03383 // Search deepest alias mapping 03384 while (isset($this->mapping['*' . $tableName])) { 03385 $tableName = '*' . $tableName; 03386 } 03387 return $tableName; 03388 } 03389 03390 /** 03391 * Returns a free key to be used to store mapping information in $this->mapping. 03392 * 03393 * @param string $tableName 03394 * @return string 03395 */ 03396 protected function getFreeMappingKey($tableName) { 03397 while (isset($this->mapping[$tableName])) { 03398 $tableName = '*' . $tableName; 03399 } 03400 return $tableName; 03401 } 03402 03403 /** 03404 * Generic mapping of table/field names arrays (as parsed by tx_dbal_sqlengine) 03405 * 03406 * @param array Array with parsed SQL parts; Takes both fields, tables, where-parts, group and order-by. Passed by reference. 03407 * @param string Default table name to assume if no table is found in $sqlPartArray 03408 * @return void 03409 * @access private 03410 * @see map_remapSELECTQueryParts() 03411 */ 03412 protected function map_sqlParts(&$sqlPartArray, $defaultTable) { 03413 $defaultTableKey = $this->getMappingKey($defaultTable); 03414 // Traverse sql Part array: 03415 if (is_array($sqlPartArray)) { 03416 foreach ($sqlPartArray as $k => $v) { 03417 03418 if (isset($sqlPartArray[$k]['type'])) { 03419 switch ($sqlPartArray[$k]['type']) { 03420 case 'flow-control': 03421 $temp = array($sqlPartArray[$k]['flow-control']); 03422 $this->map_sqlParts($temp, $defaultTable); // Call recursively! 03423 $sqlPartArray[$k]['flow-control'] = $temp[0]; 03424 break; 03425 case 'CASE': 03426 if (isset($sqlPartArray[$k]['case_field'])) { 03427 $fieldArray = explode('.', $sqlPartArray[$k]['case_field']); 03428 if (count($fieldArray) == 1 && is_array($this->mapping[$defaultTableKey]['mapFieldNames']) && isset($this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]])) { 03429 $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]]; 03430 } 03431 elseif (count($fieldArray) == 2) { 03432 // Map the external table 03433 $table = $fieldArray[0]; 03434 $tableKey = $this->getMappingKey($table); 03435 if (isset($this->mapping[$tableKey]['mapTableName'])) { 03436 $table = $this->mapping[$tableKey]['mapTableName']; 03437 } 03438 // Map the field itself 03439 $field = $fieldArray[1]; 03440 if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) { 03441 $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]]; 03442 } 03443 $sqlPartArray[$k]['case_field'] = $table . '.' . $field; 03444 } 03445 } 03446 foreach ($sqlPartArray[$k]['when'] as $key => $when) { 03447 $this->map_sqlParts($sqlPartArray[$k]['when'][$key]['when_value'], $defaultTable); 03448 } 03449 break; 03450 } 03451 } 03452 03453 // Look for sublevel (WHERE parts only) 03454 if (is_array($sqlPartArray[$k]['sub'])) { 03455 $this->map_sqlParts($sqlPartArray[$k]['sub'], $defaultTable); // Call recursively! 03456 } elseif (isset($sqlPartArray[$k]['func'])) { 03457 switch ($sqlPartArray[$k]['func']['type']) { 03458 case 'EXISTS': 03459 $this->map_subquery($sqlPartArray[$k]['func']['subquery']); 03460 break; 03461 case 'FIND_IN_SET': 03462 case 'IFNULL': 03463 case 'LOCATE': 03464 // For the field, look for table mapping (generic): 03465 $t = $sqlPartArray[$k]['func']['table'] ? $sqlPartArray[$k]['func']['table'] : $defaultTable; 03466 $t = $this->getMappingKey($t); 03467 if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]) { 03468 $sqlPartArray[$k]['func']['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]; 03469 } 03470 if ($this->mapping[$t]['mapTableName']) { 03471 $sqlPartArray[$k]['func']['table'] = $this->mapping[$t]['mapTableName']; 03472 } 03473 break; 03474 } 03475 } else { 03476 // For the field, look for table mapping (generic): 03477 $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable; 03478 $t = $this->getMappingKey($t); 03479 03480 // Mapping field name, if set: 03481 if (is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']])) { 03482 $sqlPartArray[$k]['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']]; 03483 } 03484 03485 // Mapping field name in SQL-functions like MIN(), MAX() or SUM() 03486 if ($this->mapping[$t]['mapFieldNames']) { 03487 $fieldArray = explode('.', $sqlPartArray[$k]['func_content']); 03488 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) { 03489 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]]; 03490 $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]]; 03491 } 03492 elseif (count($fieldArray) == 2) { 03493 // Map the external table 03494 $table = $fieldArray[0]; 03495 $tableKey = $this->getMappingKey($table); 03496 if (isset($this->mapping[$tableKey]['mapTableName'])) { 03497 $table = $this->mapping[$tableKey]['mapTableName']; 03498 } 03499 // Map the field itself 03500 $field = $fieldArray[1]; 03501 if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) { 03502 $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]]; 03503 } 03504 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field; 03505 $sqlPartArray[$k]['func_content'] = $table . '.' . $field; 03506 } 03507 03508 // Mapping flow-control statements 03509 if (isset($sqlPartArray[$k]['flow-control'])) { 03510 if (isset($sqlPartArray[$k]['flow-control']['type'])) { 03511 $temp = array($sqlPartArray[$k]['flow-control']); 03512 $this->map_sqlParts($temp, $t); // Call recursively! 03513 $sqlPartArray[$k]['flow-control'] = $temp[0]; 03514 } 03515 } 03516 } 03517 03518 // Do we have a function (e.g., CONCAT) 03519 if (isset($v['value']['operator'])) { 03520 foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) { 03521 $tableKey = $this->getMappingKey($fieldDef['table']); 03522 if (isset($this->mapping[$tableKey]['mapTableName'])) { 03523 $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$tableKey]['mapTableName']; 03524 } 03525 if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']])) { 03526 $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']]; 03527 } 03528 } 03529 } 03530 03531 // Do we have a subquery (WHERE parts only)? 03532 if (isset($sqlPartArray[$k]['subquery'])) { 03533 $this->map_subquery($sqlPartArray[$k]['subquery']); 03534 } 03535 03536 // do we have a field name in the value? 03537 // this is a very simplistic check, beware 03538 if (!is_numeric($sqlPartArray[$k]['value'][0]) && !isset($sqlPartArray[$k]['value'][1])) { 03539 $fieldArray = explode('.', $sqlPartArray[$k]['value'][0]); 03540 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) { 03541 $sqlPartArray[$k]['value'][0] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]]; 03542 } elseif (count($fieldArray) == 2) { 03543 // Map the external table 03544 $table = $fieldArray[0]; 03545 $tableKey = $this->getMappingKey($table); 03546 if (isset($this->mapping[$tableKey]['mapTableName'])) { 03547 $table = $this->mapping[$tableKey]['mapTableName']; 03548 } 03549 // Map the field itself 03550 $field = $fieldArray[1]; 03551 if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) { 03552 $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]]; 03553 } 03554 $sqlPartArray[$k]['value'][0] = $table . '.' . $field; 03555 } 03556 } 03557 03558 // Map table? 03559 $tableKey = $this->getMappingKey($sqlPartArray[$k]['table']); 03560 if ($sqlPartArray[$k]['table'] && $this->mapping[$tableKey]['mapTableName']) { 03561 $sqlPartArray[$k]['table'] = $this->mapping[$tableKey]['mapTableName']; 03562 } 03563 } 03564 } 03565 } 03566 } 03567 03568 /** 03569 * Maps table and field names in a subquery. 03570 * 03571 * @param array $parsedQuery 03572 * @return void 03573 */ 03574 protected function map_subquery(&$parsedQuery) { 03575 // Backup current mapping as it may be altered 03576 $backupMapping = $this->mapping; 03577 03578 foreach ($parsedQuery['FROM'] as $k => $v) { 03579 $mappingKey = $v['table']; 03580 if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) { 03581 $mappingKey = $this->getFreeMappingKey($v['as']); 03582 } else { 03583 // Should ensure that no alias is defined in the external query 03584 // which would correspond to a real table name in the subquery 03585 if ($this->getMappingKey($v['table']) !== $v['table']) { 03586 $mappingKey = $this->getFreeMappingKey($v['table']); 03587 // This is the only case when 'mapTableName' should be copied 03588 $this->mapping[$mappingKey]['mapTableName'] =& $this->mapping[$v['table']]['mapTableName']; 03589 } 03590 } 03591 if ($mappingKey !== $v['table']) { 03592 $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames']; 03593 } 03594 } 03595 03596 // Perform subquery's remapping 03597 $defaultTable = $parsedQuery['FROM'][0]['table']; 03598 $this->map_sqlParts($parsedQuery['SELECT'], $defaultTable); 03599 $this->map_sqlParts($parsedQuery['FROM'], $defaultTable); 03600 $this->map_sqlParts($parsedQuery['WHERE'], $defaultTable); 03601 03602 // Restore the mapping 03603 $this->mapping = $backupMapping; 03604 } 03605 03606 /** 03607 * Will do table/field mapping on a general tx_dbal_sqlengine-compliant SQL query 03608 * (May still not support all query types...) 03609 * 03610 * @param array Parsed QUERY as from tx_dbal_sqlengine::parseSQL(). NOTICE: Passed by reference! 03611 * @return void 03612 * @see tx_dbal_sqlengine::parseSQL() 03613 */ 03614 protected function map_genericQueryParsed(&$parsedQuery) { 03615 03616 // Getting table - same for all: 03617 $table = $parsedQuery['TABLE']; 03618 if ($table) { 03619 // Do field mapping if needed: 03620 if ($tableArray = $this->map_needMapping($table)) { 03621 03622 // Table name: 03623 if ($this->mapping[$table]['mapTableName']) { 03624 $parsedQuery['TABLE'] = $this->mapping[$table]['mapTableName']; 03625 } 03626 03627 // Based on type, do additional changes: 03628 switch ($parsedQuery['type']) { 03629 case 'ALTERTABLE': 03630 03631 // Changing field name: 03632 $newFieldName = $this->mapping[$table]['mapFieldNames'][$parsedQuery['FIELD']]; 03633 if ($newFieldName) { 03634 if ($parsedQuery['FIELD'] == $parsedQuery['newField']) { 03635 $parsedQuery['FIELD'] = $parsedQuery['newField'] = $newFieldName; 03636 } else $parsedQuery['FIELD'] = $newFieldName; 03637 } 03638 03639 // Changing key field names: 03640 if (is_array($parsedQuery['fields'])) { 03641 $this->map_fieldNamesInArray($table, $parsedQuery['fields']); 03642 } 03643 break; 03644 case 'CREATETABLE': 03645 // Remapping fields: 03646 if (is_array($parsedQuery['FIELDS'])) { 03647 $newFieldsArray = array(); 03648 foreach ($parsedQuery['FIELDS'] as $fN => $fInfo) { 03649 if ($this->mapping[$table]['mapFieldNames'][$fN]) { 03650 $fN = $this->mapping[$table]['mapFieldNames'][$fN]; 03651 } 03652 $newFieldsArray[$fN] = $fInfo; 03653 } 03654 $parsedQuery['FIELDS'] = $newFieldsArray; 03655 } 03656 03657 // Remapping keys: 03658 if (is_array($parsedQuery['KEYS'])) { 03659 foreach ($parsedQuery['KEYS'] as $kN => $kInfo) { 03660 $this->map_fieldNamesInArray($table, $parsedQuery['KEYS'][$kN]); 03661 } 03662 } 03663 break; 03664 03665 /// ... and here support for all other query types should be! 03666 03667 } 03668 } 03669 } else die('ERROR, mapping: No table found in parsed Query array...'); 03670 } 03671 03672 /** 03673 * Re-mapping field names in array 03674 * 03675 * @param string (TYPO3) Table name for fields. 03676 * @param array Array of fieldnames to remap. Notice: Passed by reference! 03677 * @return void 03678 */ 03679 protected function map_fieldNamesInArray($table, &$fieldArray) { 03680 if (is_array($this->mapping[$table]['mapFieldNames'])) { 03681 foreach ($fieldArray as $k => $v) { 03682 if ($this->mapping[$table]['mapFieldNames'][$v]) { 03683 $fieldArray[$k] = $this->mapping[$table]['mapFieldNames'][$v]; 03684 } 03685 } 03686 } 03687 } 03688 03689 03690 /************************************** 03691 * 03692 * Debugging 03693 * 03694 **************************************/ 03695 03696 /** 03697 * Debug handler for query execution 03698 * 03699 * @param string Function name from which this function is called. 03700 * @param string Execution time in ms of the query 03701 * @param array In-data of various kinds. 03702 * @return void 03703 * @access private 03704 */ 03705 public function debugHandler($function, $execTime, $inData) { 03706 // we don't want to log our own log/debug SQL 03707 $script = substr(PATH_thisScript, strlen(PATH_site)); 03708 03709 if (substr($script, -strlen('dbal/mod1/index.php')) != 'dbal/mod1/index.php' && !strstr($inData['args'][0], 'tx_dbal_debuglog')) { 03710 $data = array(); 03711 $errorFlag = 0; 03712 $joinTable = ''; 03713 03714 if ($this->sql_error()) { 03715 $data['sqlError'] = $this->sql_error(); 03716 $errorFlag |= 1; 03717 } 03718 03719 // if lastQuery is empty (for whatever reason) at least log inData.args 03720 if (empty($this->lastQuery)) { 03721 $query = implode(' ', $inData['args']); 03722 } else { 03723 $query = $this->lastQuery; 03724 } 03725 03726 if ($this->conf['debugOptions']['numberRows']) { 03727 switch ($function) { 03728 case 'exec_INSERTquery': 03729 case 'exec_UPDATEquery': 03730 case 'exec_DELETEquery': 03731 $data['numberRows'] = $this->sql_affected_rows(); 03732 break; 03733 case 'exec_SELECTquery': 03734 $data['numberRows'] = $inData['numberRows']; 03735 break; 03736 } 03737 } 03738 03739 if ($this->conf['debugOptions']['backtrace']) { 03740 $backtrace = debug_backtrace(); 03741 unset($backtrace[0]); // skip this very method :) 03742 $data['backtrace'] = array_slice($backtrace, 0, $this->conf['debugOptions']['backtrace']); 03743 } 03744 03745 switch ($function) { 03746 case 'exec_INSERTquery': 03747 case 'exec_UPDATEquery': 03748 case 'exec_DELETEquery': 03749 $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script); 03750 break; 03751 03752 case 'exec_SELECTquery': 03753 // Get explain data: 03754 if ($this->conf['debugOptions']['EXPLAIN'] && t3lib_div::inList('adodb,native', $inData['handlerType'])) { 03755 $data['EXPLAIN'] = $this->debug_explain($this->lastQuery); 03756 } 03757 03758 // Check parsing of Query: 03759 if ($this->conf['debugOptions']['parseQuery']) { 03760 $parseResults = array(); 03761 $parseResults['SELECT'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][1]); 03762 $parseResults['FROM'] = $this->SQLparser->debug_parseSQLpart('FROM', $inData['args'][0]); 03763 $parseResults['WHERE'] = $this->SQLparser->debug_parseSQLpart('WHERE', $inData['args'][2]); 03764 $parseResults['GROUPBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][3]); // Using select field list syntax 03765 $parseResults['ORDERBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][4]); // Using select field list syntax 03766 03767 foreach ($parseResults as $k => $v) { 03768 if (!strlen($parseResults[$k])) unset($parseResults[$k]); 03769 } 03770 if (count($parseResults)) { 03771 $data['parseError'] = $parseResults; 03772 $errorFlag |= 2; 03773 } 03774 } 03775 03776 // Checking joinTables: 03777 if ($this->conf['debugOptions']['joinTables']) { 03778 if (count(explode(',', $inData['ORIG_from_table'])) > 1) { 03779 $joinTable = $inData['args'][0]; 03780 } 03781 } 03782 03783 // Logging it: 03784 $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script); 03785 if (!empty($inData['args'][2])) 03786 $this->debug_WHERE($inData['args'][0], $inData['args'][2], $script); 03787 break; 03788 } 03789 } 03790 } 03791 03792 /** 03793 * Logs the where clause for debugging purposes. 03794 * 03795 * @param string $table Table name(s) the query was targeted at 03796 * @param string $where The WHERE clause to be logged 03797 * @param string $script The script calling the logging 03798 * @return void 03799 */ 03800 public function debug_WHERE($table, $where, $script = '') { 03801 $insertArray = array( 03802 'tstamp' => $GLOBALS['EXEC_TIME'], 03803 'beuser_id' => intval($GLOBALS['BE_USER']->user['uid']), 03804 'script' => $script, 03805 'tablename' => $table, 03806 'whereclause' => $where 03807 ); 03808 03809 $this->exec_INSERTquery('tx_dbal_debuglog_where', $insertArray); 03810 } 03811 03812 /** 03813 * Inserts row in the log table 03814 * 03815 * @param string The current query 03816 * @param integer Execution time of query in milliseconds 03817 * @param array Data to be stored serialized. 03818 * @param string Join string if there IS a join. 03819 * @param integer Error status. 03820 * @param string $script The script calling the logging 03821 * @return void 03822 */ 03823 public function debug_log($query, $ms, $data, $join, $errorFlag, $script = '') { 03824 if (is_array($query)) { 03825 $queryToLog = $query[0] . ' -- '; 03826 if (count($query[1])) { 03827 $queryToLog .= count($query[1]) . ' BLOB FIELDS: ' . implode(', ', array_keys($query[1])); 03828 } 03829 if (count($query[2])) { 03830 $queryToLog .= count($query[2]) . ' CLOB FIELDS: ' . implode(', ', array_keys($query[2])); 03831 } 03832 } else { 03833 $queryToLog = $query; 03834 } 03835 $insertArray = array( 03836 'tstamp' => $GLOBALS['EXEC_TIME'], 03837 'beuser_id' => intval($GLOBALS['BE_USER']->user['uid']), 03838 'script' => $script, 03839 'exec_time' => $ms, 03840 'table_join' => $join, 03841 'serdata' => serialize($data), 03842 'query' => $queryToLog, 03843 'errorFlag' => $errorFlag 03844 ); 03845 03846 $this->exec_INSERTquery('tx_dbal_debuglog', $insertArray); 03847 } 03848 03849 /** 03850 * Perform EXPLAIN query on DEFAULT handler! 03851 * 03852 * @param string SELECT Query 03853 * @return array The Explain result rows in an array 03854 * @todo Not supporting other than the default handler? And what about DBMS of other kinds than MySQL - support for EXPLAIN? 03855 */ 03856 public function debug_explain($query) { 03857 $output = array(); 03858 $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type']; 03859 switch ($hType) { 03860 case 'native': 03861 $res = $this->sql_query('EXPLAIN ' . $query); 03862 while ($row = $this->sql_fetch_assoc($res)) { 03863 $output[] = $row; 03864 } 03865 break; 03866 case 'adodb': 03867 switch ($this->handlerCfg['_DEFAULT']['config']['driver']) { 03868 case 'oci8': 03869 $res = $this->sql_query('EXPLAIN PLAN ' . $query); 03870 $output[] = 'EXPLAIN PLAN data logged to default PLAN_TABLE'; 03871 break; 03872 default: 03873 $res = $this->sql_query('EXPLAIN ' . $query); 03874 while ($row = $this->sql_fetch_assoc($res)) { 03875 $output[] = $row; 03876 } 03877 break; 03878 } 03879 break; 03880 } 03881 03882 return $output; 03883 } 03884 } 03885 03886 03887 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.ux_t3lib_db.php'])) { 03888 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/dbal/class.ux_t3lib_db.php']); 03889 } 03890 03891 ?>
1.8.0