Fixed bug #6196: IFNULL operator cannot be parsed
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / class.ux_t3lib_db.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2004-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * (c) 2004-2009 Karsten Dambekalns <karsten@typo3.org>
7 * (c) 2009-2010 Xavier Perseguers <typo3@perseguers.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Contains a database abstraction layer class for TYPO3
31 *
32 * $Id$
33 *
34 * @author Kasper Skaarhoj <kasper@typo3.com>
35 * @author Karsten Dambekalns <k.dambekalns@fishfarm.de>
36 * @author Xavier Perseguers <typo3@perseguers.ch>
37 */
38 /**
39 * [CLASS/FUNCTION INDEX of SCRIPT]
40 *
41 *
42 *
43 * 123: class ux_t3lib_DB extends t3lib_DB
44 * 169: function ux_t3lib_DB()
45 * 184: function initInternalVariables()
46 *
47 * SECTION: Query Building (Overriding parent methods)
48 * 217: function exec_INSERTquery($table,$fields_values)
49 * 275: function exec_UPDATEquery($table,$where,$fields_values)
50 * 334: function exec_DELETEquery($table,$where)
51 * 387: function exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy = '',$orderBy = '',$limit = '')
52 *
53 * SECTION: Creates an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
54 * 533: function SELECTquery($select_fields,$from_table,$where_clause,$groupBy = '',$orderBy = '',$limit = '')
55 * 556: function quoteSelectFields(&$select_fields)
56 * 573: function quoteFromTables(&$from_table)
57 * 595: function quoteWhereClause(&$where_clause)
58 * 620: function quoteGroupBy(&$groupBy)
59 * 637: function quoteOrderBy(&$orderBy)
60 *
61 * SECTION: Various helper functions
62 * 663: function quoteStr($str, $table)
63 *
64 * SECTION: SQL wrapper functions (Overriding parent methods)
65 * 707: function sql_error()
66 * 734: function sql_num_rows(&$res)
67 * 760: function sql_fetch_assoc(&$res)
68 * 808: function sql_fetch_row(&$res)
69 * 842: function sql_free_result(&$res)
70 * 868: function sql_insert_id()
71 * 893: function sql_affected_rows()
72 * 919: function sql_data_seek(&$res,$seek)
73 * 946: function sql_field_type(&$res,$pointer)
74 *
75 * SECTION: Legacy functions, bound to _DEFAULT handler. (Overriding parent methods)
76 * 987: function sql($db,$query)
77 * 999: function sql_query($query)
78 * 1035: function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password)
79 * 1055: function sql_select_db($TYPO3_db)
80 *
81 * SECTION: SQL admin functions
82 * 1086: function admin_get_tables()
83 * 1149: function admin_get_fields($tableName)
84 * 1210: function admin_get_keys($tableName)
85 * 1270: function admin_query($query)
86 *
87 * SECTION: Handler management
88 * 1333: function handler_getFromTableList($tableList)
89 * 1379: function handler_init($handlerKey)
90 *
91 * SECTION: Table/Field mapping
92 * 1488: function map_needMapping($tableList,$fieldMappingOnly = FALSE)
93 * 1524: function map_assocArray($input,$tables,$rev = FALSE)
94 * 1573: function map_remapSELECTQueryParts(&$select_fields,&$from_table,&$where_clause,&$groupBy,&$orderBy)
95 * 1615: function map_sqlParts(&$sqlPartArray, $defaultTable)
96 * 1650: function map_genericQueryParsed(&$parsedQuery)
97 * 1717: function map_fieldNamesInArray($table,&$fieldArray)
98 *
99 * SECTION: Debugging
100 * 1758: function debugHandler($function,$execTime,$inData)
101 * 1823: function debug_log($query,$ms,$data,$join,$errorFlag)
102 * 1849: function debug_explain($query)
103 *
104 * TOTAL FUNCTIONS: 41
105 * (This index is automatically created/updated by the extension "extdeveval")
106 *
107 */
108 /**
109 * TYPO3 database abstraction layer
110 *
111 * @author Kasper Skaarhoj <kasper@typo3.com>
112 * @author Karsten Dambekalns <k.dambekalns@fishfarm.de>
113 * @package TYPO3
114 * @subpackage tx_dbal
115 */
116 class ux_t3lib_DB extends t3lib_DB {
117
118 // Internal, static:
119 var $printErrors = FALSE; // Enable output of SQL errors after query executions. Set through TYPO3_CONF_VARS, see init()
120 var $debug = FALSE; // Enable debug mode. Set through TYPO3_CONF_VARS, see init()
121 var $conf = array(); // Configuration array, copied from TYPO3_CONF_VARS in constructor.
122
123 var $mapping = array(); // See manual.
124 var $table2handlerKeys = array(); // See manual.
125 var $handlerCfg = array( // See manual.
126 '_DEFAULT' => array(
127 'type' => 'native',
128 'config' => array(
129 'username' => '', // Set by default (overridden)
130 'password' => '', // Set by default (overridden)
131 'host' => '', // Set by default (overridden)
132 'database' => '', // Set by default (overridden)
133 'driver' => '', // ONLY "adodb" type; eg. "mysql"
134 'sequenceStart' => 1, // ONLY "adodb", first number in sequences/serials/...
135 'useNameQuote' => 0 // ONLY "adodb", whether to use NameQuote() method from ADOdb to quote names
136 )
137 ),
138 );
139
140
141 // Internal, dynamic:
142 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.
143 var $lastHandlerKey = ''; // Storage of the handler key of last ( SELECT) query - used for subsequent fetch-row calls etc.
144 var $lastQuery = ''; // Storage of last SELECT query
145 var $lastParsedAndMappedQueryArray = array(); // Query array, the last one parsed
146
147 var $resourceIdToTableNameMap = array(); // Mapping of resource ids to table names.
148
149 // Internal, caching:
150 var $cache_handlerKeyFromTableList = array(); // Caching handlerKeys for table lists
151 var $cache_mappingFromTableList = array(); // Caching mapping information for table lists
152 var $cache_autoIncFields = array(); // parsed SQL from standard DB dump file
153 var $cache_fieldType = array(); // field types for tables/fields
154 var $cache_primaryKeys = array(); // primary keys
155
156 /**
157 * SQL parser
158 *
159 * @var tx_dbal_sqlengine
160 */
161 var $SQLparser;
162
163 /**
164 * Installer
165 *
166 * @var t3lib_install
167 */
168 var $Installer;
169
170
171 /**
172 * Constructor.
173 * Creates SQL parser object and imports configuration from $TYPO3_CONF_VARS['EXTCONF']['dbal']
174 */
175 public function __construct() {
176 // Set SQL parser object for internal use:
177 $this->SQLparser = t3lib_div::makeInstance('tx_dbal_sqlengine');
178 $this->Installer = t3lib_div::makeInstance('t3lib_install');
179
180 // Set internal variables with configuration:
181 $this->conf = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal'];
182 $this->initInternalVariables();
183 }
184
185 /**
186 * Setting internal variables from $this->conf.
187 *
188 * @return void
189 */
190 protected function initInternalVariables() {
191 // Set outside configuration:
192 if (isset($this->conf['mapping'])) {
193 $this->mapping = $this->conf['mapping'];
194 }
195 if (isset($this->conf['table2handlerKeys'])) {
196 $this->table2handlerKeys = $this->conf['table2handlerKeys'];
197 }
198 if (isset($this->conf['handlerCfg'])) {
199 $this->handlerCfg = $this->conf['handlerCfg'];
200 }
201
202 $this->cacheFieldInfo();
203 // Debugging settings:
204 $this->printErrors = $this->conf['debugOptions']['printErrors'] ? TRUE : FALSE;
205 $this->debug = $this->conf['debugOptions']['enabled'] ? TRUE : FALSE;
206 }
207
208 /**
209 * Clears the cached field information file.
210 *
211 * @return void
212 */
213 public function clearCachedFieldInfo() {
214 if (file_exists(PATH_typo3conf . 'temp_fieldInfo.php')) {
215 unlink(PATH_typo3conf . 'temp_fieldInfo.php');
216 }
217 }
218
219 /**
220 * Caches the field information.
221 *
222 * @return void
223 */
224 public function cacheFieldInfo() {
225 $extSQL = '';
226 $parsedExtSQL = array();
227
228 // try to fetch cached file first
229 // file is removed when admin_query() is called
230 if (file_exists(PATH_typo3conf . 'temp_fieldInfo.php')) {
231 $fdata = unserialize(t3lib_div::getUrl(PATH_typo3conf . 'temp_fieldInfo.php'));
232 $this->cache_autoIncFields = $fdata['incFields'];
233 $this->cache_fieldType = $fdata['fieldTypes'];
234 $this->cache_primaryKeys = $fdata['primaryKeys'];
235 } else {
236 // handle stddb.sql, parse and analyze
237 $extSQL = t3lib_div::getUrl(PATH_site . 't3lib/stddb/tables.sql');
238 $parsedExtSQL = $this->Installer->getFieldDefinitions_fileContent($extSQL);
239 $this->analyzeFields($parsedExtSQL);
240
241 // loop over all installed extensions
242 foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $ext => $v) {
243 if (!is_array($v) || !isset($v['ext_tables.sql'])) {
244 continue;
245 }
246
247 // fetch db dump (if any) and parse it, then analyze
248 $extSQL = t3lib_div::getUrl($v['ext_tables.sql']);
249 $parsedExtSQL = $this->Installer->getFieldDefinitions_fileContent($extSQL);
250 $this->analyzeFields($parsedExtSQL);
251 }
252
253 $cachedFieldInfo = array('incFields' => $this->cache_autoIncFields, 'fieldTypes' => $this->cache_fieldType, 'primaryKeys' => $this->cache_primaryKeys);
254 $cachedFieldInfo = serialize($this->mapCachedFieldInfo($cachedFieldInfo));
255
256 // write serialized content to file
257 t3lib_div::writeFile(PATH_typo3conf . 'temp_fieldInfo.php', $cachedFieldInfo);
258
259 if (strcmp(t3lib_div::getUrl(PATH_typo3conf . 'temp_fieldInfo.php'), $cachedFieldInfo)) {
260 die('typo3temp/temp_incfields.php was NOT updated properly (written content didn\'t match file content) - maybe write access problem?');
261 }
262 }
263 }
264
265 /**
266 * Analyzes fields and adds the extracted information to the field type, auto increment and primary key info caches.
267 *
268 * @param array $parsedExtSQL The output produced by t3lib_install::getFieldDefinitions_fileContent()
269 * @return void
270 * @see t3lib_install::getFieldDefinitions_fileContent()
271 */
272 protected function analyzeFields($parsedExtSQL) {
273 foreach ($parsedExtSQL as $table => $tdef) {
274 if (is_array($tdef['fields'])) {
275 foreach ($tdef['fields'] as $field => $fdef) {
276 $fdef = $this->SQLparser->parseFieldDef($fdef);
277 $this->cache_fieldType[$table][$field]['type'] = $fdef['fieldType'];
278 $this->cache_fieldType[$table][$field]['metaType'] = $this->MySQLMetaType($fdef['fieldType']);
279 $this->cache_fieldType[$table][$field]['notnull'] = (isset($fdef['featureIndex']['NOTNULL']) && !$this->SQLparser->checkEmptyDefaultValue($fdef['featureIndex'])) ? 1 : 0;
280 if (isset($fdef['featureIndex']['DEFAULT'])) {
281 $default = $fdef['featureIndex']['DEFAULT']['value'][0];
282 if (isset($fdef['featureIndex']['DEFAULT']['value'][1])) {
283 $default = $fdef['featureIndex']['DEFAULT']['value'][1] . $default . $fdef['featureIndex']['DEFAULT']['value'][1];
284 }
285 $this->cache_fieldType[$table][$field]['default'] = $default;
286 }
287 if (isset($fdef['featureIndex']['AUTO_INCREMENT'])) {
288 $this->cache_autoIncFields[$table] = $field;
289 }
290 if (isset($tdef['keys']['PRIMARY'])) {
291 $this->cache_primaryKeys[$table] = substr($tdef['keys']['PRIMARY'], 13, -1);
292 }
293 }
294 }
295 }
296 }
297
298 /**
299 * This function builds all definitions for mapped tables and fields
300 * @see cacheFieldInfo()
301 */
302 protected function mapCachedFieldInfo($fieldInfo) {
303 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'])) {
304 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'] as $mappedTable => $mappedConf) {
305 if (array_key_exists($mappedTable, $fieldInfo['incFields'])) {
306 $mappedTableAlias = $mappedConf['mapTableName'];
307 if (isset($mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]])) {
308 $fieldInfo['incFields'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]];
309 } else {
310 $fieldInfo['incFields'][$mappedTableAlias] = $fieldInfo['incFields'][$mappedTable];
311 }
312 }
313
314 if (array_key_exists($mappedTable, $fieldInfo['fieldTypes'])) {
315 foreach ($fieldInfo['fieldTypes'][$mappedTable] as $field => $fieldConf) {
316 $tempMappedFieldConf[$mappedConf['mapFieldNames'][$field]] = $fieldConf;
317 }
318
319 $fieldInfo['fieldTypes'][$mappedConf['mapTableName']] = $tempMappedFieldConf;
320 }
321
322 if (array_key_exists($mappedTable, $fieldInfo['primaryKeys'])) {
323 $mappedTableAlias = $mappedConf['mapTableName'];
324 if (isset($mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]])) {
325 $fieldInfo['primaryKeys'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]];
326 } else {
327 $fieldInfo['primaryKeys'][$mappedTableAlias] = $fieldInfo['primaryKeys'][$mappedTable];
328 }
329 }
330 }
331 }
332
333 return $fieldInfo;
334 }
335
336
337 /************************************
338 *
339 * Query Building (Overriding parent methods)
340 * These functions are extending counterparts in the parent class.
341 *
342 **************************************/
343
344 /* From the ADOdb documentation, this is what we do (_Execute for SELECT, _query for the other actions)
345
346 Execute() is the default way to run queries. You can use the low-level functions _Execute() and _query() to reduce query overhead.
347 Both these functions share the same parameters as Execute().
348
349 If you do not have any bind parameters or your database supports binding (without emulation), then you can call _Execute() directly.
350 Calling this function bypasses bind emulation. Debugging is still supported in _Execute().
351
352 If you do not require debugging facilities nor emulated binding, and do not require a recordset to be returned, then you can call _query.
353 This is great for inserts, updates and deletes. Calling this function bypasses emulated binding, debugging, and recordset handling. Either
354 the resultid, TRUE or FALSE are returned by _query().
355 */
356
357 /**
358 * Inserts a record for $table from the array with field/value pairs $fields_values.
359 *
360 * @param string Table name
361 * @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.
362 * @param mixed List/array of keys NOT to quote (eg. SQL functions)
363 * @return mixed Result from handler, usually TRUE when success and FALSE on failure
364 */
365 public function exec_INSERTquery($table, $fields_values, $no_quote_fields = '') {
366
367 if ($this->debug) {
368 $pt = t3lib_div::milliseconds();
369 }
370
371 // Do field mapping if needed:
372 $ORIG_tableName = $table;
373 if ($tableArray = $this->map_needMapping($table)) {
374
375 // Field mapping of array:
376 $fields_values = $this->map_assocArray($fields_values, $tableArray);
377
378 // Table name:
379 if ($this->mapping[$table]['mapTableName']) {
380 $table = $this->mapping[$table]['mapTableName'];
381 }
382 }
383 // Select API:
384 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
385 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
386 case 'native':
387 $this->lastQuery = $this->INSERTquery($table,$fields_values,$no_quote_fields);
388 if (is_string($this->lastQuery)) {
389 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
390 } else {
391 $sqlResult = mysql_query($this->lastQuery[0], $this->handlerInstance[$this->lastHandlerKey]['link']);
392 foreach ($this->lastQuery[1] as $field => $content) {
393 mysql_query('UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table) . ' WHERE ' . $this->quoteWhereClause($where), $this->handlerInstance[$this->lastHandlerKey]['link']);
394 }
395 }
396 break;
397 case 'adodb':
398 // auto generate ID for auto_increment fields if not present (static import needs this!)
399 // should we check the table name here (static_*)?
400 if (isset($this->cache_autoIncFields[$table])) {
401 if (isset($fields_values[$this->cache_autoIncFields[$table]])) {
402 $new_id = $fields_values[$this->cache_autoIncFields[$table]];
403 if ($table != 'tx_dbal_debuglog') {
404 $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
405 }
406 } else {
407 $new_id = $this->handlerInstance[$this->lastHandlerKey]->GenID($table.'_'.$this->cache_autoIncFields[$table], $this->handlerInstance[$this->lastHandlerKey]->sequenceStart);
408 $fields_values[$this->cache_autoIncFields[$table]] = $new_id;
409 if ($table != 'tx_dbal_debuglog') {
410 $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
411 }
412 }
413 }
414
415 $this->lastQuery = $this->INSERTquery($table,$fields_values,$no_quote_fields);
416 if (is_string($this->lastQuery)) {
417 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery,FALSE);
418 } else {
419 $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
420 if (strlen($this->lastQuery[0])) {
421 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0],FALSE);
422 }
423 if (is_array($this->lastQuery[1])) {
424 foreach ($this->lastQuery[1] as $field => $content) {
425 if (empty($content)) continue;
426
427 if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
428 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table),$field,$content,$this->quoteWhereClause($this->cache_autoIncFields[$table].'='.$new_id));
429 } elseif (isset($this->cache_primaryKeys[$table])) {
430 $where = '';
431 $pks = explode(',', $this->cache_primaryKeys[$table]);
432 foreach ($pks as $pk) {
433 if (isset($fields_values[$pk]))
434 $where .= $pk.'='.$this->fullQuoteStr($fields_values[$pk], $table).' AND ';
435 }
436 $where = $this->quoteWhereClause($where.'1=1');
437 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table),$field,$content,$where);
438 } else {
439 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE);
440 die('Could not update BLOB >>>> no WHERE clause found!'); // should never ever happen
441 }
442 }
443 }
444 if (is_array($this->lastQuery[2])) {
445 foreach ($this->lastQuery[2] as $field => $content) {
446 if (empty($content)) continue;
447
448 if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
449 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table),$field,$content,$this->quoteWhereClause($this->cache_autoIncFields[$table].'='.$new_id));
450 } elseif (isset($this->cache_primaryKeys[$table])) {
451 $where = '';
452 $pks = explode(',', $this->cache_primaryKeys[$table]);
453 foreach ($pks as $pk) {
454 if (isset($fields_values[$pk]))
455 $where .= $pk.'='.$this->fullQuoteStr($fields_values[$pk], $table).' AND ';
456 }
457 $where = $this->quoteWhereClause($where.'1=1');
458 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table),$field,$content,$where);
459 } else {
460 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE);
461 die('Could not update CLOB >>>> no WHERE clause found!'); // should never ever happen
462 }
463 }
464 }
465 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
466 }
467 break;
468 case 'userdefined':
469 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_INSERTquery($table,$fields_values,$no_quote_fields);
470 break;
471 }
472
473 if ($this->printErrors && $this->sql_error()) {
474 debug(array($this->lastQuery, $this->sql_error()));
475 }
476
477 if ($this->debug) {
478 $this->debugHandler(
479 'exec_INSERTquery',
480 t3lib_div::milliseconds()-$pt,
481 array(
482 'handlerType' => $hType,
483 'args' => array($table,$fields_values),
484 'ORIG_tablename' => $ORIG_tableName
485 )
486 );
487 }
488 // Return output:
489 return $sqlResult;
490 }
491
492 /**
493 * Creates and executes an INSERT SQL-statement for $table with multiple rows.
494 * This method uses exec_INSERTquery() and is just a syntax wrapper to it.
495 *
496 * @param string Table name
497 * @param array Field names
498 * @param array Table rows. Each row should be an array with field values mapping to $fields
499 * @param string/array See fullQuoteArray()
500 * @return mixed Result from last handler, usually TRUE when success and FALSE on failure
501 */
502 public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
503 if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
504 return parent::exec_INSERTmultipleRows($table, $fields, $rows, $no_quote_fields);
505 }
506
507 foreach ($rows as $row) {
508 $fields_values = array();
509 foreach ($fields as $key => $value) {
510 $fields_values[$value] = $row[$key];
511 }
512 $res = $this->exec_INSERTquery($table, $fields_values, $no_quote_fields);
513 }
514
515 return $res;
516 }
517
518 /**
519 * Updates a record from $table
520 *
521 * @param string Database tablename
522 * @param string WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
523 * @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.
524 * @param mixed List/array of keys NOT to quote (eg. SQL functions)
525 * @return mixed Result from handler, usually TRUE when success and FALSE on failure
526 */
527 public function exec_UPDATEquery($table,$where,$fields_values,$no_quote_fields = '') {
528 if ($this->debug) {
529 $pt = t3lib_div::milliseconds();
530 }
531
532 // Do table/field mapping:
533 $ORIG_tableName = $table;
534 if ($tableArray = $this->map_needMapping($table)) {
535
536 // Field mapping of array:
537 $fields_values = $this->map_assocArray($fields_values,$tableArray);
538
539 // Where clause table and field mapping:
540 $whereParts = $this->SQLparser->parseWhereClause($where);
541 $this->map_sqlParts($whereParts,$tableArray[0]['table']);
542 $where = $this->SQLparser->compileWhereClause($whereParts, FALSE);
543
544 // Table name:
545 if ($this->mapping[$table]['mapTableName']) {
546 $table = $this->mapping[$table]['mapTableName'];
547 }
548 }
549
550 // Select API
551 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
552 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
553 case 'native':
554 $this->lastQuery = $this->UPDATEquery($table,$where,$fields_values,$no_quote_fields);
555 if (is_string($this->lastQuery)) {
556 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
557 }
558 else {
559 $sqlResult = mysql_query($this->lastQuery[0], $this->handlerInstance[$this->lastHandlerKey]['link']);
560 foreach ($this->lastQuery[1] as $field => $content) {
561 mysql_query('UPDATE '.$this->quoteFromTables($table).' SET '.$this->quoteFromTables($field).'='.$this->fullQuoteStr($content,$table).' WHERE '.$this->quoteWhereClause($where), $this->handlerInstance[$this->lastHandlerKey]['link']);
562 }
563 }
564 break;
565 case 'adodb':
566 $this->lastQuery = $this->UPDATEquery($table,$where,$fields_values,$no_quote_fields);
567 if (is_string($this->lastQuery)) {
568 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery,FALSE);
569 } else {
570 $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
571 if (strlen($this->lastQuery[0])) {
572 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0],FALSE);
573 }
574 if (is_array($this->lastQuery[1])) {
575 foreach ($this->lastQuery[1] as $field => $content) {
576 $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table),$field,$content,$this->quoteWhereClause($where));
577 }
578 }
579 if (is_array($this->lastQuery[2])) {
580 foreach ($this->lastQuery[2] as $field => $content) {
581 $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table),$field,$content,$this->quoteWhereClause($where));
582 }
583 }
584 $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
585 }
586 break;
587 case 'userdefined':
588 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_UPDATEquery($table,$where,$fields_values,$no_quote_fields);
589 break;
590 }
591
592 if ($this->printErrors && $this->sql_error()) {
593 debug(array($this->lastQuery, $this->sql_error()));
594 }
595
596 if ($this->debug) {
597 $this->debugHandler(
598 'exec_UPDATEquery',
599 t3lib_div::milliseconds()-$pt,
600 array(
601 'handlerType' => $hType,
602 'args' => array($table,$where, $fields_values),
603 'ORIG_from_table' => $ORIG_tableName
604 )
605 );
606 }
607
608 // Return result:
609 return $sqlResult;
610 }
611
612 /**
613 * Deletes records from table
614 *
615 * @param string Database tablename
616 * @param string WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
617 * @return mixed Result from handler
618 */
619 public function exec_DELETEquery($table, $where) {
620 if ($this->debug) {
621 $pt = t3lib_div::milliseconds();
622 }
623
624 // Do table/field mapping:
625 $ORIG_tableName = $table;
626 if ($tableArray = $this->map_needMapping($table)) {
627
628 // Where clause:
629 $whereParts = $this->SQLparser->parseWhereClause($where);
630 $this->map_sqlParts($whereParts,$tableArray[0]['table']);
631 $where = $this->SQLparser->compileWhereClause($whereParts, FALSE);
632
633 // Table name:
634 if ($this->mapping[$table]['mapTableName']) {
635 $table = $this->mapping[$table]['mapTableName'];
636 }
637 }
638
639 // Select API
640 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
641 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
642 case 'native':
643 $this->lastQuery = $this->DELETEquery($table,$where);
644 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
645 break;
646 case 'adodb':
647 $this->lastQuery = $this->DELETEquery($table,$where);
648 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery,FALSE);
649 break;
650 case 'userdefined':
651 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_DELETEquery($table,$where);
652 break;
653 }
654
655 if ($this->printErrors && $this->sql_error()) {
656 debug(array($this->lastQuery, $this->sql_error()));
657 }
658
659 if ($this->debug) {
660 $this->debugHandler(
661 'exec_DELETEquery',
662 t3lib_div::milliseconds()-$pt,
663 array(
664 'handlerType' => $hType,
665 'args' => array($table,$where),
666 'ORIG_from_table' => $ORIG_tableName
667 )
668 );
669 }
670
671 // Return result:
672 return $sqlResult;
673 }
674
675 /**
676 * Selects records from Data Source
677 *
678 * @param string $select_fields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
679 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
680 * @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!
681 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
682 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
683 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
684 * @return mixed Result from handler. Typically object from DBAL layers.
685 */
686 public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
687 if ($this->debug) {
688 $pt = t3lib_div::milliseconds();
689 }
690
691 // Map table / field names if needed:
692 $ORIG_tableName = $from_table; // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
693 if ($tableArray = $this->map_needMapping($ORIG_tableName)) {
694 $this->map_remapSELECTQueryParts($select_fields,$from_table,$where_clause,$groupBy,$orderBy); // Variables passed by reference!
695 }
696
697 // Get handler key and select API:
698 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
699 $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
700 switch ($hType) {
701 case 'native':
702 $this->lastQuery = $this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit);
703 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
704 $this->resourceIdToTableNameMap[(string)$sqlResult] = $ORIG_tableName;
705 break;
706 case 'adodb':
707 if ($limit != '') {
708 $splitLimit = t3lib_div::intExplode(',', $limit); // Splitting the limit values:
709 if ($splitLimit[1]) { // If there are two parameters, do mapping differently than otherwise:
710 $numrows = $splitLimit[1];
711 $offset = $splitLimit[0];
712 } else {
713 $numrows = $splitLimit[0];
714 $offset = 0;
715 }
716
717 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy), $numrows, $offset);
718 $this->lastQuery = $sqlResult->sql;
719 } else {
720 $this->lastQuery = $this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy);
721 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery);
722 }
723
724 $sqlResult->TYPO3_DBAL_handlerType = 'adodb'; // Setting handler type in result object (for later recognition!)
725 $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
726 break;
727 case 'userdefined':
728 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit);
729 if (is_object($sqlResult)) {
730 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined'; // Setting handler type in result object (for later recognition!)
731 $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
732 }
733 break;
734 }
735
736 if ($this->printErrors && $this->sql_error()) {
737 debug(array($this->lastQuery, $this->sql_error()));
738 }
739
740 if ($this->debug) {
741 $this->debugHandler(
742 'exec_SELECTquery',
743 t3lib_div::milliseconds()-$pt,
744 array(
745 'handlerType' => $hType,
746 'args' => array($from_table,$select_fields,$where_clause,$groupBy,$orderBy,$limit),
747 'ORIG_from_table' => $ORIG_tableName
748 )
749 );
750 }
751
752 // Return result handler.
753 return $sqlResult;
754 }
755
756 /**
757 * Truncates a table.
758 *
759 * @param string Database tablename
760 * @return mixed Result from handler
761 */
762 public function exec_TRUNCATEquery($table) {
763 if ($this->debug) {
764 $pt = t3lib_div::milliseconds();
765 }
766
767 // Do table/field mapping:
768 $ORIG_tableName = $table;
769 if ($tableArray = $this->map_needMapping($table)) {
770 // Table name:
771 if ($this->mapping[$table]['mapTableName']) {
772 $table = $this->mapping[$table]['mapTableName'];
773 }
774 }
775
776 // Select API
777 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
778 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
779 case 'native':
780 $this->lastQuery = $this->TRUNCATEquery($table);
781 $sqlResult = mysql_query($this->lastQuery, $this->handlerInstance[$this->lastHandlerKey]['link']);
782 break;
783 case 'adodb':
784 $this->lastQuery = $this->TRUNCATEquery($table);
785 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
786 break;
787 case 'userdefined':
788 $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_TRUNCATEquery($table,$where);
789 break;
790 }
791
792 if ($this->printErrors && $this->sql_error()) {
793 debug(array($this->lastQuery, $this->sql_error()));
794 }
795
796 if ($this->debug) {
797 $this->debugHandler(
798 'exec_TRUNCATEquery',
799 t3lib_div::milliseconds() - $pt,
800 array(
801 'handlerType' => $hType,
802 'args' => array($table),
803 'ORIG_from_table' => $ORIG_tableName
804 )
805 );
806 }
807
808 // Return result:
809 return $sqlResult;
810 }
811
812 /**
813 * Executes a query.
814 * EXPERIMENTAL since TYPO3 4.4.
815 *
816 * @param array $queryParts SQL parsed by method parseSQL() of t3lib_sqlparser
817 * @return pointer Result pointer / DBAL object
818 * @see ux_t3lib_db::sql_query()
819 */
820 protected function exec_query(array $queryParts) {
821 switch ($queryParts['type']) {
822 case 'SELECT':
823 $selectFields = $this->SQLparser->compileFieldList($queryParts['SELECT']);
824 $fromTables = $this->SQLparser->compileFromTables($queryParts['FROM']);
825 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
826 $groupBy = isset($queryParts['GROUPBY']) ? $this->SQLparser->compileWhereClause($queryParts['GROUPBY']) : '';
827 $orderBy = isset($queryParts['GROUPBY']) ? $this->SQLparser->compileWhereClause($queryParts['ORDERBY']) : '';
828 $limit = isset($queryParts['LIMIT']) ? $this->SQLparser->compileWhereClause($queryParts['LIMIT']) : '';
829 return $this->exec_SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy, $limit);
830
831 case 'UPDATE':
832 $table = $queryParts['TABLE'];
833 $fields = array();
834 foreach ($components['FIELDS'] as $fN => $fV) {
835 $fields[$fN] = $fV[0];
836 }
837 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
838 return $this->exec_UPDATEquery($table, $whereClause, $fields);
839
840 case 'INSERT':
841 $table = $queryParts['TABLE'];
842 $values = array();
843 if (isset($queryParts['VALUES_ONLY']) && is_array($queryParts['VALUES_ONLY'])) {
844 $fields = $GLOBALS['TYPO3_DB']->cache_fieldType[$table];
845 $fc = 0;
846 foreach ($fields as $fn => $fd) {
847 $values[$fn] = $queryParts['VALUES_ONLY'][$fc++][0];
848 }
849 } else {
850 foreach ($queryParts['FIELDS'] as $fN => $fV) {
851 $values[$fN] = $fV[0];
852 }
853 }
854 return $this->exec_INSERTquery($table, $values);
855
856 case 'DELETE':
857 $table = $queryParts['TABLE'];
858 $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
859 return $this->exec_DELETEquery($table, $whereClause);
860
861 case 'TRUNCATETABLE':
862 $table = $queryParts['TABLE'];
863 return $this->exec_TRUNCATEquery($table);
864 }
865 }
866
867
868
869 /**************************************
870 *
871 * Query building
872 *
873 **************************************/
874
875 /**
876 * Creates an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
877 * Usage count/core: 4
878 *
879 * @param string See exec_INSERTquery()
880 * @param array See exec_INSERTquery()
881 * @param mixed See exec_INSERTquery()
882 * @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
883 */
884 public function INSERTquery($table, $fields_values, $no_quote_fields = '') {
885 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
886 if (is_array($fields_values) && count($fields_values)) {
887
888 if (is_string($no_quote_fields)) {
889 $no_quote_fields = explode(',', $no_quote_fields);
890 } elseif (!is_array($no_quote_fields)) {
891 $no_quote_fields = array();
892 }
893
894 $blobfields = array();
895 $nArr = array();
896 foreach ($fields_values as $k => $v) {
897 if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
898 // we skip the field in the regular INSERT statement, it is only in blobfields
899 $blobfields[$this->quoteFieldNames($k)] = $v;
900 } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
901 // we skip the field in the regular INSERT statement, it is only in clobfields
902 $clobfields[$this->quoteFieldNames($k)] = $v;
903 } else {
904 // Add slashes old-school:
905 // cast numerical values
906 $mt = $this->sql_field_metatype($table, $k);
907 if ($mt{0} == 'I') {
908 $v = (int)$v;
909 } else if ($mt{0} == 'F') {
910 $v = (double)$v;
911 }
912
913 $nArr[$this->quoteFieldNames($k)] = (!in_array($k,$no_quote_fields)) ? $this->fullQuoteStr($v, $table) : $v;
914 }
915 }
916
917 if (count($blobfields) || count($clobfields)) {
918 if (count($nArr)) {
919 $query[0] = 'INSERT INTO ' . $this->quoteFromTables($table) . '
920 (
921 ' . implode(',
922 ', array_keys($nArr)) . '
923 ) VALUES (
924 ' . implode(',
925 ', $nArr) . '
926 )';
927 }
928 if (count($blobfields)) $query[1] = $blobfields;
929 if (count($clobfields)) $query[2] = $clobfields;
930 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query[0];
931 } else {
932 $query = 'INSERT INTO '.$this->quoteFromTables($table).'
933 (
934 ' . implode(',
935 ', array_keys($nArr)) . '
936 ) VALUES (
937 ' . implode(',
938 ', $nArr) . '
939 )';
940
941 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query;
942 }
943
944 return $query;
945 }
946 }
947
948 /**
949 * Creates an INSERT SQL-statement for $table with multiple rows.
950 * This method will create multiple INSERT queries concatenated with ';'
951 *
952 * @param string Table name
953 * @param array Field names
954 * @param array Table rows. Each row should be an array with field values mapping to $fields
955 * @param string/array See fullQuoteArray()
956 * @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.
957 */
958 public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
959 if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
960 return parent::INSERTmultipleRows($table, $fields, $rows, $no_quote_fields);
961 }
962
963 $result = array();
964
965 foreach ($rows as $row) {
966 $fields_values = array();
967 foreach ($fields as $key => $value) {
968 $fields_values[$value] = $row[$key];
969 }
970 $rowQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
971 if (is_array($rowQuery)) {
972 $result[] = $rowQuery;
973 } else {
974 $result[][0] = $rowQuery;
975 }
976 }
977
978 return $result;
979 }
980
981 /**
982 * Creates an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values.
983 * Usage count/core: 6
984 *
985 * @param string See exec_UPDATEquery()
986 * @param string See exec_UPDATEquery()
987 * @param array See exec_UPDATEquery()
988 * @param mixed See exec_UPDATEquery()
989 * @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
990 */
991 public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = '') {
992 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
993 if (is_string($where)) {
994 if (is_array($fields_values) && count($fields_values)) {
995
996 if (is_string($no_quote_fields)) {
997 $no_quote_fields = explode(',', $no_quote_fields);
998 } elseif (!is_array($no_quote_fields)) {
999 $no_quote_fields = array();
1000 }
1001
1002 $blobfields = array();
1003 $nArr = array();
1004 foreach ($fields_values as $k => $v) {
1005 if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
1006 // we skip the field in the regular UPDATE statement, it is only in blobfields
1007 $blobfields[$this->quoteFieldNames($k)] = $v;
1008 } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
1009 // we skip the field in the regular UPDATE statement, it is only in clobfields
1010 $clobfields[$this->quoteFieldNames($k)] = $v;
1011 } else {
1012 // Add slashes old-school:
1013 // cast numeric values
1014 $mt = $this->sql_field_metatype($table, $k);
1015 if ($mt{0} == 'I') {
1016 $v = (int)$v;
1017 } else if ($mt{0} == 'F') {
1018 $v = (double)$v;
1019 }
1020 $nArr[] = $this->quoteFieldNames($k) . '=' . ((!in_array($k, $no_quote_fields)) ? $this->fullQuoteStr($v, $table) : $v);
1021 }
1022 }
1023
1024 if (count($blobfields) || count($clobfields)) {
1025 if (count($nArr)) {
1026 $query[0] = 'UPDATE '.$this->quoteFromTables($table).'
1027 SET
1028 '.implode(',
1029 ',$nArr).
1030 (strlen($where)>0 ? '
1031 WHERE
1032 '.$this->quoteWhereClause($where) : '');
1033 }
1034 if (count($blobfields)) $query[1] = $blobfields;
1035 if (count($clobfields)) $query[2] = $clobfields;
1036 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query[0];
1037 } else {
1038 $query = 'UPDATE '.$this->quoteFromTables($table).'
1039 SET
1040 '.implode(',
1041 ',$nArr).
1042 (strlen($where)>0 ? '
1043 WHERE
1044 '.$this->quoteWhereClause($where) : '');
1045
1046 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query;
1047 }
1048
1049 return $query;
1050 }
1051 } else {
1052 die('<strong>TYPO3 Fatal Error:</strong> "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !');
1053 }
1054 }
1055
1056 /**
1057 * Creates a DELETE SQL-statement for $table where $where-clause
1058 * Usage count/core: 3
1059 *
1060 * @param string See exec_DELETEquery()
1061 * @param string See exec_DELETEquery()
1062 * @return string Full SQL query for DELETE
1063 */
1064 public function DELETEquery($table, $where) {
1065 if (is_string($where)) {
1066 $table = $this->quoteFromTables($table);
1067 $where = $this->quoteWhereClause($where);
1068
1069 $query = parent::DELETEquery($table, $where);
1070
1071 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query;
1072 return $query;
1073 } else {
1074 die('<strong>TYPO3 Fatal Error:</strong> "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !');
1075 }
1076 }
1077
1078 /**
1079 * Creates a SELECT SQL-statement
1080 * Usage count/core: 11
1081 *
1082 * @param string See exec_SELECTquery()
1083 * @param string See exec_SELECTquery()
1084 * @param string See exec_SELECTquery()
1085 * @param string See exec_SELECTquery()
1086 * @param string See exec_SELECTquery()
1087 * @param string See exec_SELECTquery()
1088 * @return string Full SQL query for SELECT
1089 */
1090 public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
1091 $select_fields = $this->quoteFieldNames($select_fields);
1092 $from_table = $this->quoteFromTables($from_table);
1093 $where_clause = $this->quoteWhereClause($where_clause);
1094 $groupBy = $this->quoteGroupBy($groupBy);
1095 $orderBy = $this->quoteOrderBy($orderBy);
1096
1097 // Call parent method to build actual query
1098 $query = parent::SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit);
1099
1100 if ($this->debugOutput || $this->store_lastBuiltQuery) $this->debug_lastBuiltQuery = $query;
1101
1102 return $query;
1103 }
1104
1105 /**
1106 * Creates a TRUNCATE TABLE SQL-statement
1107 *
1108 * @param string See exec_TRUNCATEquery()
1109 * @return string Full SQL query for TRUNCATE TABLE
1110 */
1111 public function TRUNCATEquery($table) {
1112 $table = $this->quoteFromTables($table);
1113
1114 // Call parent method to build actual query
1115 $query = parent::TRUNCATEquery($table);
1116
1117 if ($this->debugOutput || $this->store_lastBuiltQuery) {
1118 $this->debug_lastBuiltQuery = $query;
1119 }
1120
1121 return $query;
1122 }
1123
1124
1125 /**************************************
1126 *
1127 * Functions for quoting table/field names
1128 *
1129 **************************************/
1130
1131 /**
1132 * Quotes components of a SELECT subquery.
1133 *
1134 * @param array $components Array of SQL query components
1135 * @return array
1136 */
1137 protected function quoteSELECTsubquery(array $components) {
1138 $components['SELECT'] = $this->_quoteFieldNames($components['SELECT']);
1139 $components['FROM'] = $this->_quoteFromTables($components['FROM']);
1140 $components['WHERE'] = $this->_quoteWhereClause($components['WHERE']);
1141 return $components;
1142 }
1143
1144 /**
1145 * Quotes field (and table) names with the quote character suitable for the DB being used
1146 * Use quoteFieldNames instead!
1147 *
1148 * @param string List of fields to be selected from DB
1149 * @return string Quoted list of fields to be selected from DB
1150 * @deprecated since TYPO3 4.0
1151 */
1152 public function quoteSelectFields($select_fields) {
1153 $this->quoteFieldNames($select_fields);
1154 }
1155
1156 /**
1157 * Quotes field (and table) names with the quote character suitable for the DB being used
1158 *
1159 * @param string List of fields to be used in query to DB
1160 * @return string Quoted list of fields to be in query to DB
1161 */
1162 public function quoteFieldNames($select_fields) {
1163 if ($select_fields == '') return '';
1164 if ($this->runningNative()) return $select_fields;
1165
1166 $select_fields = $this->SQLparser->parseFieldList($select_fields);
1167 if ($this->SQLparser->parse_error) {
1168 die($this->SQLparser->parse_error . ' in ' . __FILE__ . ' : ' . __LINE__);
1169 }
1170 $select_fields = $this->_quoteFieldNames($select_fields);
1171
1172 return $this->SQLparser->compileFieldList($select_fields);
1173 }
1174
1175 /**
1176 * Quotes field (and table) names in a SQL SELECT clause acccording to DB rules
1177 *
1178 * @param array $select_fields The parsed fields to quote
1179 * @return array
1180 * @see quoteFieldNames()
1181 */
1182 protected function _quoteFieldNames(array $select_fields) {
1183 foreach ($select_fields as $k => $v) {
1184 if ($select_fields[$k]['field'] != '' && $select_fields[$k]['field'] != '*' && !is_numeric($select_fields[$k]['field'])) {
1185 $select_fields[$k]['field'] = $this->quoteName($select_fields[$k]['field']);
1186 }
1187 if ($select_fields[$k]['table'] != '' && !is_numeric($select_fields[$k]['table'])) {
1188 $select_fields[$k]['table'] = $this->quoteName($select_fields[$k]['table']);
1189 }
1190 if ($select_fields[$k]['as'] != '') {
1191 $select_fields[$k]['as'] = $this->quoteName($select_fields[$k]['as']);
1192 }
1193 if (isset($select_fields[$k]['func_content.']) && $select_fields[$k]['func_content.'][0]['func_content'] != '*'){
1194 $select_fields[$k]['func_content.'][0]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content.'][0]['func_content']);
1195 $select_fields[$k]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content']);
1196 }
1197 if (isset($select_fields[$k]['flow-control'])) {
1198 // Quoting flow-control statements
1199 if ($select_fields[$k]['flow-control']['type'] === 'CASE') {
1200 if (isset($select_fields[$k]['flow-control']['case_field'])) {
1201 $select_fields[$k]['flow-control']['case_field'] = $this->quoteFieldNames($select_fields[$k]['flow-control']['case_field']);
1202 }
1203 foreach ($select_fields[$k]['flow-control']['when'] as $key => $when) {
1204 $select_fields[$k]['flow-control']['when'][$key]['when_value'] = $this->_quoteWhereClause($when['when_value']);
1205 }
1206 }
1207 }
1208 }
1209
1210 return $select_fields;
1211 }
1212
1213 /**
1214 * Quotes table names with the quote character suitable for the DB being used
1215 *
1216 * @param string List of tables to be selected from DB
1217 * @return string Quoted list of tables to be selected from DB
1218 */
1219 public function quoteFromTables($from_table) {
1220 if ($from_table == '') return '';
1221 if ($this->runningNative()) return $from_table;
1222
1223 $from_table = $this->SQLparser->parseFromTables($from_table);
1224 $from_table = $this->_quoteFromTables($from_table);
1225 return $this->SQLparser->compileFromTables($from_table);
1226 }
1227
1228 /**
1229 * Quotes table names in a SQL FROM clause acccording to DB rules
1230 *
1231 * @param array $from_table The parsed FROM clause to quote
1232 * @return array
1233 * @see quoteFromTables()
1234 */
1235 protected function _quoteFromTables(array $from_table) {
1236 foreach ($from_table as $k => $v) {
1237 $from_table[$k]['table'] = $this->quoteName($from_table[$k]['table']);
1238 if ($from_table[$k]['as'] != '') {
1239 $from_table[$k]['as'] = $this->quoteName($from_table[$k]['as']);
1240 }
1241 if (is_array($v['JOIN'])) {
1242 foreach ($v['JOIN'] as $joinCnt => $join) {
1243 $from_table[$k]['JOIN'][$joinCnt]['withTable'] = $this->quoteName($join['withTable']);
1244 $from_table[$k]['JOIN'][$joinCnt]['as'] = ($join['as']) ? $this->quoteName($join['as']) : '';
1245 $from_table[$k]['JOIN'][$joinCnt]['ON'][0]['table'] = ($join['ON'][0]['table']) ? $this->quoteName($join['ON'][0]['table']) : '';
1246 $from_table[$k]['JOIN'][$joinCnt]['ON'][0]['field'] = $this->quoteName($join['ON'][0]['field']);
1247 $from_table[$k]['JOIN'][$joinCnt]['ON'][1]['table'] = ($join['ON'][1]['table']) ? $this->quoteName($join['ON'][1]['table']) : '';
1248 $from_table[$k]['JOIN'][$joinCnt]['ON'][1]['field'] = $this->quoteName($join['ON'][1]['field']);
1249 }
1250 }
1251 }
1252
1253 return $from_table;
1254 }
1255
1256 /**
1257 * Quotes the field (and table) names within a where clause with the quote character suitable for the DB being used
1258 *
1259 * @param string A where clause that can e parsed by parseWhereClause
1260 * @return string Usable where clause with quoted field/table names
1261 */
1262 public function quoteWhereClause($where_clause) {
1263 if ($where_clause === '' || $this->runningNative()) return $where_clause;
1264
1265 $where_clause = $this->SQLparser->parseWhereClause($where_clause);
1266 if (is_array($where_clause)) {
1267 $where_clause = $this->_quoteWhereClause($where_clause);
1268 $where_clause = $this->SQLparser->compileWhereClause($where_clause);
1269 } else {
1270 die('Could not parse where clause in ' . __FILE__ . ' : ' . __LINE__);
1271 }
1272
1273 return $where_clause;
1274 }
1275
1276 /**
1277 * Quotes field names in a SQL WHERE clause acccording to DB rules
1278 *
1279 * @param array $where_clause The parsed WHERE clause to quote
1280 * @return array
1281 * @see quoteWhereClause()
1282 */
1283 protected function _quoteWhereClause(array $where_clause) {
1284 foreach ($where_clause as $k => $v) {
1285 // Look for sublevel:
1286 if (is_array($where_clause[$k]['sub'])) {
1287 $where_clause[$k]['sub'] = $this->_quoteWhereClause($where_clause[$k]['sub']);
1288 } elseif (isset($v['func'])) {
1289 switch ($where_clause[$k]['func']['type']) {
1290 case 'EXISTS':
1291 $where_clause[$k]['func']['subquery'] = $this->quoteSELECTsubquery($v['func']['subquery']);
1292 break;
1293 case 'IFNULL':
1294 case 'LOCATE':
1295 if ($where_clause[$k]['func']['table'] != '') {
1296 $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
1297 }
1298 if ($where_clause[$k]['func']['field'] != '') {
1299 $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
1300 }
1301 break;
1302 }
1303 } else {
1304 if ($where_clause[$k]['table'] != '') {
1305 $where_clause[$k]['table'] = $this->quoteName($where_clause[$k]['table']);
1306 }
1307 if (!is_numeric($where_clause[$k]['field'])) {
1308 $where_clause[$k]['field'] = $this->quoteName($where_clause[$k]['field']);
1309 }
1310 if (isset($where_clause[$k]['calc_table'])) {
1311 if ($where_clause[$k]['calc_table'] != '') {
1312 $where_clause[$k]['calc_table'] = $this->quoteName($where_clause[$k]['calc_table']);
1313 }
1314 if ($where_clause[$k]['calc_field'] != '') {
1315 $where_clause[$k]['calc_field'] = $this->quoteName($where_clause[$k]['calc_field']);
1316 }
1317 }
1318 }
1319 if ($where_clause[$k]['comparator']) {
1320 if (isset($v['value']['operator'])) {
1321 foreach ($where_clause[$k]['value']['args'] as $argK => $fieldDef) {
1322 $where_clause[$k]['value']['args'][$argK]['table'] = $this->quoteName($fieldDef['table']);
1323 $where_clause[$k]['value']['args'][$argK]['field'] = $this->quoteName($fieldDef['field']);
1324 }
1325 } else {
1326 // Detecting value type; list or plain:
1327 if (t3lib_div::inList('NOTIN,IN', strtoupper(str_replace(array(' ',"\n", "\r", "\t"), '', $where_clause[$k]['comparator'])))) {
1328 if (isset($v['subquery'])) {
1329 $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']);
1330 }
1331 } else {
1332 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], '.')) {
1333 $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]);
1334 }
1335 }
1336 }
1337 }
1338 }
1339
1340 return $where_clause;
1341 }
1342
1343 /**
1344 * [Describe function...]
1345 *
1346 * @param [type] $$groupBy: ...
1347 * @return [type] ...
1348 */
1349 protected function quoteGroupBy($groupBy) {
1350 if ($groupBy === '') return '';
1351 if ($this->runningNative()) return $groupBy;
1352
1353 $groupBy = $this->SQLparser->parseFieldList($groupBy);
1354 foreach ($groupBy as $k => $v) {
1355 $groupBy[$k]['field'] = $this->quoteName($groupBy[$k]['field']);
1356 if ($groupBy[$k]['table'] != '') {
1357 $groupBy[$k]['table'] = $this->quoteName($groupBy[$k]['table']);
1358 }
1359 }
1360 return $this->SQLparser->compileFieldList($groupBy);
1361 }
1362
1363 /**
1364 * [Describe function...]
1365 *
1366 * @param [type] $$orderBy: ...
1367 * @return [type] ...
1368 */
1369 protected function quoteOrderBy($orderBy) {
1370 if ($orderBy === '') return '';
1371 if ($this->runningNative()) return $orderBy;
1372
1373 $orderBy = $this->SQLparser->parseFieldList($orderBy);
1374 foreach ($orderBy as $k => $v) {
1375 $orderBy[$k]['field'] = $this->quoteName($orderBy[$k]['field']);
1376 if ($orderBy[$k]['table'] != '') {
1377 $orderBy[$k]['table'] = $this->quoteName($orderBy[$k]['table']);
1378 }
1379 }
1380 return $this->SQLparser->compileFieldList($orderBy);
1381 }
1382
1383
1384
1385 /**************************************
1386 *
1387 * Various helper functions
1388 *
1389 **************************************/
1390
1391 /**
1392 * Escaping and quoting values for SQL statements.
1393 *
1394 * @param string Input string
1395 * @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!).
1396 * @return string Output string; Wrapped in single quotes and quotes in the string (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
1397 * @see quoteStr()
1398 */
1399 public function fullQuoteStr($str, $table) {
1400 return '\'' . $this->quoteStr($str, $table) . '\'';
1401 }
1402
1403 /**
1404 * Substitution for PHP function "addslashes()"
1405 * 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()!
1406 *
1407 * @param string Input string
1408 * @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!).
1409 * @return string Output string; Quotes (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
1410 * @see quoteStr()
1411 */
1412 public function quoteStr($str, $table) {
1413 $this->lastHandlerKey = $this->handler_getFromTableList($table);
1414 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1415 case 'native':
1416 $str = mysql_real_escape_string($str, $this->handlerInstance[$this->lastHandlerKey]['link']);
1417 break;
1418 case 'adodb':
1419 $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1);
1420 break;
1421 case 'userdefined':
1422 $str = $this->handlerInstance[$this->lastHandlerKey]->quoteStr($str);
1423 break;
1424 default:
1425 die('No handler found!!!');
1426 break;
1427 }
1428
1429 return $str;
1430 }
1431
1432 /**
1433 * Quotes an object name (table name, field, ...)
1434 *
1435 * @param string Object's name
1436 * @param string Handler key
1437 * @param boolean If method NameQuote() is not used, whether to use backticks instead of driver-specific quotes
1438 * @return string Properly-quoted object's name
1439 */
1440 public function quoteName($name, $handlerKey = NULL, $useBackticks = FALSE) {
1441 $handlerKey = $handlerKey ? $handlerKey : $this->lastHandlerKey;
1442 $useNameQuote = isset($this->handlerCfg[$handlerKey]['config']['useNameQuote']) ? $this->handlerCfg[$handlerKey]['config']['useNameQuote'] : FALSE;
1443 if ($useNameQuote) {
1444 return $this->handlerInstance[$handlerKey]->DataDictionary->NameQuote($name);
1445 } else {
1446 $quote = $useBackticks ? '`' : $this->handlerInstance[$handlerKey]->nameQuote;
1447 return $quote . $name . $quote;
1448 }
1449 }
1450
1451 /**
1452 * Return MetaType for native field type (ADOdb only!)
1453 *
1454 * @param string native type as reported by admin_get_fields()
1455 * @param string Table name for which query type string. Important for detection of DBMS handler of the query!
1456 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1457 */
1458 public function MetaType($type, $table, $max_length = -1) {
1459 $this->lastHandlerKey = $this->handler_getFromTableList($table);
1460 $str = '';
1461 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1462 case 'native':
1463 $str = $type;
1464 break;
1465 case 'adodb':
1466 if (in_array($table, $this->cache_fieldType)) {
1467 $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1);
1468 $str = $rs->MetaType($type, $max_length);
1469 }
1470 break;
1471 case 'userdefined':
1472 $str = $this->handlerInstance[$this->lastHandlerKey]->MetaType($str,$table,$max_length);
1473 break;
1474 default:
1475 die('No handler found!!!');
1476 break;
1477 }
1478
1479 return $str;
1480 }
1481
1482
1483 /**
1484 * Return MetaType for native MySQL field type
1485 *
1486 * @param string native type as reported as in mysqldump files
1487 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1488 */
1489 public function MySQLMetaType($t) {
1490
1491 switch (strtoupper($t)) {
1492 case 'STRING':
1493 case 'CHAR':
1494 case 'VARCHAR':
1495 case 'TINYBLOB':
1496 case 'TINYTEXT':
1497 case 'ENUM':
1498 case 'SET': return 'C';
1499
1500 case 'TEXT':
1501 case 'LONGTEXT':
1502 case 'MEDIUMTEXT': return 'XL';
1503
1504 case 'IMAGE':
1505 case 'LONGBLOB':
1506 case 'BLOB':
1507 case 'MEDIUMBLOB': return 'B';
1508
1509 case 'YEAR':
1510 case 'DATE': return 'D';
1511
1512 case 'TIME':
1513 case 'DATETIME':
1514 case 'TIMESTAMP': return 'T';
1515
1516 case 'FLOAT':
1517 case 'DOUBLE': return 'F';
1518
1519 case 'INT':
1520 case 'INTEGER':
1521 case 'TINYINT':
1522 case 'SMALLINT':
1523 case 'MEDIUMINT':
1524 case 'BIGINT': return 'I8'; // we always return I8 to be on the safe side. Under some circumstances the fields are to small otherwise...
1525
1526 default: return 'N';
1527 }
1528 }
1529
1530 /**
1531 * Return actual MySQL type for meta field type
1532 *
1533 * @param string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1534 * @return string native type as reported as in mysqldump files, uppercase
1535 */
1536 public function MySQLActualType($meta) {
1537 switch (strtoupper($meta)) {
1538 case 'C': return 'VARCHAR';
1539 case 'XL':
1540 case 'X': return 'LONGTEXT';
1541
1542 case 'C2': return 'VARCHAR';
1543 case 'X2': return 'LONGTEXT';
1544
1545 case 'B': return 'LONGBLOB';
1546
1547 case 'D': return 'DATE';
1548 case 'T': return 'DATETIME';
1549 case 'L': return 'TINYINT';
1550
1551 case 'I':
1552 case 'I1':
1553 case 'I2':
1554 case 'I4':
1555 case 'I8': return 'BIGINT'; // we only have I8 in DBAL, see MySQLMetaType()
1556
1557 case 'F': return 'DOUBLE';
1558 case 'N': return 'NUMERIC';
1559
1560 default: return $meta;
1561 }
1562 }
1563
1564
1565
1566
1567 /**************************************
1568 *
1569 * SQL wrapper functions (Overriding parent methods)
1570 * (For use in your applications)
1571 *
1572 **************************************/
1573
1574 /**
1575 * Returns the error status on the most recent sql() execution (based on $this->lastHandlerKey)
1576 *
1577 * @return string Handler error strings
1578 */
1579 public function sql_error() {
1580 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1581 case 'native':
1582 $output = mysql_error($this->handlerInstance[$this->lastHandlerKey]['link']);
1583 break;
1584 case 'adodb':
1585 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorMsg();
1586 break;
1587 case 'userdefined':
1588 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_error();
1589 break;
1590 }
1591 return $output;
1592 }
1593
1594 /**
1595 * Returns the error number on the most recent sql() execution (based on $this->lastHandlerKey)
1596 *
1597 * @return int Handler error number
1598 */
1599 public function sql_errno() {
1600 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1601 case 'native':
1602 $output = mysql_errno($this->handlerInstance[$this->lastHandlerKey]['link']);
1603 break;
1604 case 'adodb':
1605 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorNo();
1606 break;
1607 case 'userdefined':
1608 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_errno();
1609 break;
1610 }
1611 return $output;
1612 }
1613
1614 /**
1615 * Returns the number of selected rows.
1616 *
1617 * @param pointer Result pointer / DBAL object
1618 * @return integer Number of resulting rows.
1619 */
1620 public function sql_num_rows(&$res) {
1621 if ($res === FALSE) return 0;
1622
1623 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1624 switch ($handlerType) {
1625 case 'native':
1626 $output = mysql_num_rows($res);
1627 break;
1628 case 'adodb':
1629 $output = method_exists($res, 'RecordCount') ? $res->RecordCount() : 0;
1630 break;
1631 case 'userdefined':
1632 $output = $res->sql_num_rows();
1633 break;
1634 }
1635 return $output;
1636 }
1637
1638 /**
1639 * Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows.
1640 *
1641 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1642 * @return array Associative array of result row.
1643 */
1644 public function sql_fetch_assoc(&$res) {
1645 $output = array();
1646
1647 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : (is_resource($res) ? 'native' : FALSE);
1648 switch ($handlerType) {
1649 case 'native':
1650 $output = mysql_fetch_assoc($res);
1651 $tableList = $this->resourceIdToTableNameMap[(string)$res]; // Reading list of tables from SELECT query:
1652 break;
1653 case 'adodb':
1654 // Check if method exists for the current $res object.
1655 // If a table exists in TCA but not in the db, a error
1656 // occured because $res is not a valid object.
1657 if (method_exists($res, 'FetchRow')) {
1658 $output = $res->FetchRow();
1659 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query:
1660
1661 // Removing all numeric/integer keys.
1662 // A workaround because in ADOdb we would need to know what we want before executing the query...
1663 if (is_array($output)) {
1664 foreach ($output as $key => $value) {
1665 if (is_integer($key)) {
1666 unset($output[$key]);
1667 }
1668 elseif ($value === ' ' && $this->runningADOdbDriver('mssql')) $output[$key] = ''; // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
1669 }
1670 }
1671 }
1672 break;
1673 case 'userdefined':
1674 $output = $res->sql_fetch_assoc();
1675 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query:
1676 break;
1677 }
1678
1679 // Table/Fieldname mapping:
1680 if (is_array($output)) {
1681 if ($tables = $this->map_needMapping($tableList,TRUE)) {
1682 $output = $this->map_assocArray($output,$tables,1);
1683 }
1684 }
1685
1686 // Return result:
1687 return $output;
1688 }
1689
1690 /**
1691 * Returns an array that corresponds to the fetched row, or FALSE if there are no more rows.
1692 * The array contains the values in numerical indices.
1693 *
1694 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1695 * @return array Array with result rows.
1696 */
1697 public function sql_fetch_row(&$res) {
1698 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1699 switch ($handlerType) {
1700 case 'native':
1701 $output = mysql_fetch_row($res);
1702 break;
1703 case 'adodb':
1704 // Check if method exists for the current $res object.
1705 // If a table exists in TCA but not in the db, a error
1706 // occured because $res is not a valid object.
1707 if (method_exists($res, 'FetchRow')) {
1708 $output = $res->FetchRow();
1709
1710 // Removing all assoc. keys.
1711 // A workaround because in ADOdb we would need to know what we want before executing the query...
1712 if (is_array($output)) {
1713 foreach ($output as $key => $value) {
1714 if (!is_integer($key)) unset($output[$key]);
1715 elseif ($value === ' ' && $this->runningADOdbDriver('mssql')) $output[$key] = ''; // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
1716 }
1717 }
1718 }
1719 break;
1720 case 'userdefined':
1721 $output = $res->sql_fetch_row();
1722 break;
1723 }
1724 return $output;
1725 }
1726
1727 /**
1728 * Free result memory / unset result object
1729 *
1730 * @param pointer MySQL result pointer to free / DBAL object
1731 * @return boolean Returns TRUE on success or FALSE on failure.
1732 */
1733 public function sql_free_result(&$res) {
1734 if ($res === FALSE) return FALSE;
1735
1736 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1737 switch ($handlerType) {
1738 case 'native':
1739 $output = mysql_free_result($res);
1740 break;
1741 case 'adodb':
1742 if (method_exists($res, 'Close')) {
1743 $res->Close();
1744 unset($res);
1745 $output = TRUE;
1746 } else {
1747 $output = FALSE;
1748 }
1749 break;
1750 case 'userdefined':
1751 unset($res);
1752 break;
1753 }
1754 return $output;
1755 }
1756
1757 /**
1758 * Get the ID generated from the previous INSERT operation
1759 *
1760 * @return integer The uid of the last inserted record.
1761 */
1762 public function sql_insert_id() {
1763 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1764 case 'native':
1765 $output = mysql_insert_id($this->handlerInstance[$this->lastHandlerKey]['link']);
1766 break;
1767 case 'adodb':
1768 $output = $this->handlerInstance[$this->lastHandlerKey]->last_insert_id;
1769 break;
1770 case 'userdefined':
1771 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_insert_id();
1772 break;
1773 }
1774 return $output;
1775 }
1776
1777 /**
1778 * Returns the number of rows affected by the last INSERT, UPDATE or DELETE query
1779 *
1780 * @return integer Number of rows affected by last query
1781 */
1782 public function sql_affected_rows() {
1783 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1784 case 'native':
1785 $output = mysql_affected_rows();
1786 break;
1787 case 'adodb':
1788 $output = $this->handlerInstance[$this->lastHandlerKey]->Affected_Rows();
1789 break;
1790 case 'userdefined':
1791 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_affected_rows();
1792 break;
1793 }
1794 return $output;
1795 }
1796
1797 /**
1798 * Move internal result pointer
1799 *
1800 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1801 * @param integer Seek result number.
1802 * @return boolean Returns TRUE on success or FALSE on failure.
1803 */
1804 public function sql_data_seek(&$res, $seek) {
1805 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1806 switch ($handlerType) {
1807 case 'native':
1808 $output = mysql_data_seek($res,$seek);
1809 break;
1810 case 'adodb':
1811 $output = $res->Move($seek);
1812 break;
1813 case 'userdefined':
1814 $output = $res->sql_data_seek($seek);
1815 break;
1816 }
1817 return $output;
1818 }
1819
1820 /**
1821 * Get the type of the specified field in a result
1822 *
1823 * If the first parameter is a string, it is used as table name for the lookup.
1824 *
1825 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name
1826 * @param integer Field index. In case of ADOdb a string (field name!) FIXME
1827 * @return string Returns the type of the specified field index
1828 */
1829 public function sql_field_metatype($table, $field) {
1830 // If $table and/or $field are mapped, use the original names instead
1831 foreach ($this->mapping as $tableName => $tableMapInfo) {
1832 if (isset($tableMapInfo['mapTableName']) && $tableMapInfo['mapTableName'] === $table) {
1833 // Table name is mapped => use original name
1834 $table = $tableName;
1835 }
1836
1837 if (isset($tableMapInfo['mapFieldNames'])) {
1838 foreach ($tableMapInfo['mapFieldNames'] as $fieldName => $fieldMapInfo) {
1839 if ($fieldMapInfo === $field) {
1840 // Field name is mapped => use original name
1841 $field = $fieldName;
1842 }
1843 }
1844 }
1845 }
1846
1847 return $this->cache_fieldType[$table][$field]['metaType'];
1848 }
1849
1850 /**
1851 * Get the type of the specified field in a result
1852 *
1853 * If the first parameter is a string, it is used as table name for the lookup.
1854 *
1855 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name
1856 * @param integer Field index. In case of ADOdb a string (field name!) FIXME
1857 * @return string Returns the type of the specified field index
1858 */
1859 public function sql_field_type(&$res,$pointer) {
1860 if ($res === null) {
1861 debug(array('no res in sql_field_type!'));
1862 return 'text';
1863 }
1864 elseif (is_string($res)){
1865 if ($res === 'tx_dbal_debuglog') return 'text';
1866 $handlerType = 'adodb';
1867 }
1868 else {
1869 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1870 }
1871
1872 switch ($handlerType) {
1873 case 'native':
1874 $output = mysql_field_type($res,$pointer);
1875 break;
1876 case 'adodb':
1877 if (is_string($pointer)){
1878 $output = $this->cache_fieldType[$res][$pointer]['type'];
1879 }
1880
1881 break;
1882 case 'userdefined':
1883 $output = $res->sql_field_type($pointer);
1884 break;
1885 }
1886
1887 return $output;
1888 }
1889
1890
1891
1892
1893
1894
1895
1896
1897 /**********
1898 *
1899 * Legacy functions, bound to _DEFAULT handler. (Overriding parent methods)
1900 * Deprecated or still experimental.
1901 *
1902 **********/
1903
1904 /**
1905 * Executes query (on DEFAULT handler!)
1906 * DEPRECATED - use exec_* functions from this class instead!
1907 *
1908 * @param string Database name
1909 * @param string Query to execute
1910 * @return pointer Result pointer
1911 * @deprecated since TYPO3 4.1
1912 */
1913 public function sql($db,$query) {
1914 return $this->sql_query($query);
1915 }
1916
1917 /**
1918 * Executes a query
1919 * EXPERIMENTAL - This method will make its best to handle the query correctly
1920 * but if it cannot, it will simply pass the query to DEFAULT handler.
1921 *
1922 * You should use exec_* function from this class instead!
1923 * If you don't, anything that does not use the _DEFAULT handler will probably break!
1924 *
1925 * This method was deprecated in TYPO3 4.1 but is considered experimental since TYPO3 4.4
1926 * as it tries to handle the query correctly anyway.
1927 *
1928 * @param string Query to execute
1929 * @return pointer Result pointer / DBAL object
1930 */
1931 public function sql_query($query) {
1932 // This method is heavily used by Extbase, try to handle it with DBAL-native methods
1933 $queryParts = $this->SQLparser->parseSQL($query);
1934 if (is_array($queryParts) && t3lib_div::inList('SELECT,UPDATE,INSERT,DELETE', $queryParts['type'])) {
1935 return $this->exec_query($queryParts);
1936 }
1937
1938 switch ($this->handlerCfg['_DEFAULT']['type']) {
1939 case 'native':
1940 $sqlResult = mysql_query($query, $this->handlerInstance['_DEFAULT']['link']);
1941 break;
1942 case 'adodb':
1943 $sqlResult = $this->handlerInstance['_DEFAULT']->Execute($query);
1944 $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
1945 break;
1946 case 'userdefined':
1947 $sqlResult = $this->handlerInstance['_DEFAULT']->sql_query($query);
1948 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
1949 break;
1950 }
1951
1952 if ($this->printErrors && $this->sql_error()) {
1953 debug(array($this->lastQuery, $this->sql_error()));
1954 }
1955
1956 return $sqlResult;
1957 }
1958
1959 /**
1960 * Opening the _DEFAULT connection handler to the database.
1961 * This is typically done by the scripts "init.php" in the backend or "index_ts.php" in the frontend (tslib_fe->connectToMySQL())
1962 * You wouldn't need to use this at any time - let TYPO3 core handle this.
1963 *
1964 * @param string Database host IP/domain
1965 * @param string Username to connect with.
1966 * @param string Password to connect with.
1967 * @return mixed Returns handler connection value
1968 * @deprecated since TYPO3 4.1
1969 * @see handler_init()
1970 */
1971 public function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password) {
1972 // Overriding the _DEFAULT handler configuration of username, password, localhost and database name:
1973 $this->handlerCfg['_DEFAULT']['config']['username'] = $TYPO3_db_username;
1974 $this->handlerCfg['_DEFAULT']['config']['password'] = $TYPO3_db_password;
1975 $this->handlerCfg['_DEFAULT']['config']['host'] = $TYPO3_db_host;
1976 $this->handlerCfg['_DEFAULT']['config']['database'] = TYPO3_db;
1977
1978 // Initializing and output value:
1979 $sqlResult = $this->handler_init('_DEFAULT');
1980 return $sqlResult;
1981 }
1982
1983 /**
1984 * Select database for _DEFAULT handler.
1985 *
1986 * @param string Database to connect to.
1987 * @return boolean Always returns TRUE; function is obsolete, database selection is made in handler_init() function!
1988 * @deprecated since TYPO3 4.1
1989 */
1990 public function sql_select_db($TYPO3_db) {
1991 return TRUE;
1992 }
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008 /**************************************
2009 *
2010 * SQL admin functions
2011 * (For use in the Install Tool and Extension Manager)
2012 *
2013 **************************************/
2014
2015 /**
2016 * Listing databases from current MySQL connection. NOTICE: It WILL try to select those databases and thus break selection of current database.
2017 * Use in Install Tool only!
2018 * Usage count/core: 1
2019 *
2020 * @return array Each entry represents a database name
2021 */
2022 public function admin_get_dbs() {
2023 $dbArr = array();
2024 switch ($this->handlerCfg['_DEFAULT']['type']) {
2025 case 'native':
2026 $db_list = mysql_list_dbs($this->link);
2027 while ($row = mysql_fetch_object($db_list)) {
2028 if ($this->sql_select_db($row->Database)) {
2029 $dbArr[] = $row->Database;
2030 }
2031 }
2032 break;
2033 case 'adodb':
2034 // check needed for install tool - otherwise it will just die because the call to
2035 // MetaDatabases is done on a stdClass instance
2036 if (method_exists($this->handlerInstance['_DEFAULT'],'MetaDatabases')) {
2037 $sqlDBs = $this->handlerInstance['_DEFAULT']->MetaDatabases();
2038 if (is_array($sqlDBs)) {
2039 foreach ($sqlDBs as $k => $theDB) {
2040 $dbArr[] = $theDB;
2041 }
2042 }
2043 }
2044 break;
2045 case 'userdefined':
2046 $dbArr = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2047 break;
2048 }
2049
2050 return $dbArr;
2051 }
2052
2053 /**
2054 * Returns the list of tables from the system (quering the DBMSs)
2055 * It looks up all tables from the DBMS of the _DEFAULT handler and then add all tables *configured* to be managed by other handlers
2056 *
2057 * 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.
2058 *
2059 * @return array Tables in an array (tablename is in both key and value)
2060 * @todo Should the check for Oracle Recycle Bin stuff be moved elsewhere?
2061 * @todo Should return table details in value! see t3lib_db::admin_get_tables()
2062 */
2063 public function admin_get_tables() {
2064 $whichTables = array();
2065
2066 // Getting real list of tables:
2067 switch ($this->handlerCfg['_DEFAULT']['type']) {
2068 case 'native':
2069 $tables_result = mysql_query('SHOW TABLE STATUS FROM `' . TYPO3_db . '`', $this->handlerInstance['_DEFAULT']['link']);
2070 if (!$this->sql_error()) {
2071 while ($theTable = $this->sql_fetch_assoc($tables_result)) {
2072 $whichTables[current($theTable)] = current($theTable);
2073 }
2074 }
2075 break;
2076 case 'adodb':
2077 $sqlTables = $this->handlerInstance['_DEFAULT']->MetaTables('TABLES');
2078 while (list($k, $theTable) = each($sqlTables)) {
2079 if (preg_match('/BIN\$/', $theTable)) continue; // skip tables from the Oracle 10 Recycle Bin
2080 $whichTables[$theTable] = $theTable;
2081 }
2082 break;
2083 case 'userdefined':
2084 $whichTables = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2085 break;
2086 }
2087
2088 // Check mapping:
2089 if (is_array($this->mapping) && count($this->mapping)) {
2090
2091 // Mapping table names in reverse, first getting list of real table names:
2092 $tMap = array();
2093 foreach ($this->mapping as $tN => $tMapInfo) {
2094 if (isset($tMapInfo['mapTableName'])) $tMap[$tMapInfo['mapTableName']]=$tN;
2095 }
2096
2097 // Do mapping:
2098 $newList=array();
2099 foreach ($whichTables as $tN) {
2100 if (isset($tMap[$tN])) $tN = $tMap[$tN];
2101 $newList[$tN] = $tN;
2102 }
2103
2104 $whichTables = $newList;
2105 }
2106
2107 // Adding tables configured to reside in other DBMS (handler by other handlers than the default):
2108 if (is_array($this->table2handlerKeys)) {
2109 foreach ($this->table2handlerKeys as $key => $handlerKey) {
2110 $whichTables[$key] = $key;
2111 }
2112 }
2113
2114 return $whichTables;
2115 }
2116
2117 /**
2118 * Returns information about each field in the $table (quering the DBMS)
2119 * In a DBAL this should look up the right handler for the table and return compatible information
2120 * 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
2121 *
2122 * @param string Table name
2123 * @return array Field information in an associative array with fieldname => field row
2124 */
2125 public function admin_get_fields($tableName) {
2126 $output = array();
2127
2128 // Do field mapping if needed:
2129 $ORIG_tableName = $tableName;
2130 if ($tableArray = $this->map_needMapping($tableName)) {
2131
2132 // Table name:
2133 if ($this->mapping[$tableName]['mapTableName']) {
2134 $tableName = $this->mapping[$tableName]['mapTableName'];
2135 }
2136 }
2137
2138 // Find columns
2139 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
2140 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2141 case 'native':
2142 $columns_res = mysql_query('SHOW columns FROM '.$tableName, $this->handlerInstance[$this->lastHandlerKey]['link']);
2143 while($fieldRow = mysql_fetch_assoc($columns_res)) {
2144 $output[$fieldRow['Field']] = $fieldRow;
2145 }
2146 break;
2147 case 'adodb':
2148 $fieldRows = $this->handlerInstance[$this->lastHandlerKey]->MetaColumns($tableName, FALSE);
2149 if (is_array($fieldRows)) {
2150 foreach ($fieldRows as $k => $fieldRow) {
2151 settype($fieldRow, 'array');
2152 $fieldRow['Field'] = $fieldRow['name'];
2153 $ntype = $this->MySQLActualType($this->MetaType($fieldRow['type'],$tableName));
2154 $ntype .= (($fieldRow['max_length'] != -1) ? (($ntype == 'INT') ? '(11)' :'('.$fieldRow['max_length'].')') : '');
2155 $fieldRow['Type'] = strtolower($ntype);
2156 $fieldRow['Null'] = '';
2157 $fieldRow['Key'] = '';
2158 $fieldRow['Default'] = $fieldRow['default_value'];
2159 $fieldRow['Extra'] = '';
2160 $output[$fieldRow['name']] = $fieldRow;
2161 }
2162 }
2163 break;
2164 case 'userdefined':
2165 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_fields($tableName);
2166 break;
2167 }
2168
2169 // mapping should be done:
2170 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2171 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2172
2173 $newOutput = array();
2174 foreach ($output as $fN => $fInfo) {
2175 if (isset($revFields[$fN])) {
2176 $fN = $revFields[$fN];
2177 $fInfo['Field'] = $fN;
2178 }
2179 $newOutput[$fN] = $fInfo;
2180 }
2181 $output = $newOutput;
2182 }
2183
2184 return $output;
2185 }
2186
2187 /**
2188 * Returns information about each index key in the $table (quering the DBMS)
2189 * In a DBAL this should look up the right handler for the table and return compatible information
2190 *
2191 * @param string Table name
2192 * @return array Key information in a numeric array
2193 */
2194 public function admin_get_keys($tableName) {
2195 $output = array();
2196
2197 // Do field mapping if needed:
2198 $ORIG_tableName = $tableName;
2199 if ($tableArray = $this->map_needMapping($tableName)) {
2200
2201 // Table name:
2202 if ($this->mapping[$tableName]['mapTableName']) {
2203 $tableName = $this->mapping[$tableName]['mapTableName'];
2204 }
2205 }
2206
2207 // Find columns
2208 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
2209 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2210 case 'native':
2211 $keyRes = mysql_query('SHOW keys FROM '.$tableName, $this->handlerInstance[$this->lastHandlerKey]['link']);
2212 while($keyRow = mysql_fetch_assoc($keyRes)) {
2213 $output[] = $keyRow;
2214 }
2215 break;
2216 case 'adodb':
2217 $keyRows = $this->handlerInstance[$this->lastHandlerKey]->MetaIndexes($tableName);
2218 if ($keyRows !== FALSE) {
2219 while (list($k, $theKey) = each($keyRows)) {
2220 $theKey['Table'] = $tableName;
2221 $theKey['Non_unique'] = (int) !$theKey['unique'];
2222 $theKey['Key_name'] = str_replace($tableName.'_','',$k);
2223
2224 // the following are probably not needed anyway...
2225 $theKey['Collation'] = '';
2226 $theKey['Cardinality'] = '';
2227 $theKey['Sub_part'] = '';
2228 $theKey['Packed'] = '';
2229 $theKey['Null'] = '';
2230 $theKey['Index_type'] = '';
2231 $theKey['Comment'] = '';
2232
2233 // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2234 $keycols = $theKey['columns'];
2235 while (list($c, $theCol) = each($keycols)) {
2236 $theKey['Seq_in_index'] = $c+1;
2237 $theKey['Column_name'] = $theCol;
2238 $output[] = $theKey;
2239 }
2240 }
2241 }
2242 $priKeyRow = $this->handlerInstance[$this->lastHandlerKey]->MetaPrimaryKeys($tableName);
2243 $theKey = array();
2244 $theKey['Table'] = $tableName;
2245 $theKey['Non_unique'] = 0;
2246 $theKey['Key_name'] = 'PRIMARY';
2247
2248 // the following are probably not needed anyway...
2249 $theKey['Collation'] = '';
2250 $theKey['Cardinality'] = '';
2251 $theKey['Sub_part'] = '';
2252 $theKey['Packed'] = '';
2253 $theKey['Null'] = '';
2254 $theKey['Index_type'] = '';
2255 $theKey['Comment'] = '';
2256
2257 // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2258 if ($priKeyRow !== FALSE) {
2259 while (list($c, $theCol) = each($priKeyRow)) {
2260 $theKey['Seq_in_index'] = $c+1;
2261 $theKey['Column_name'] = $theCol;
2262 $output[] = $theKey;
2263 }
2264 }
2265 break;
2266 case 'userdefined':
2267 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_keys($tableName);
2268 break;
2269 }
2270
2271 // mapping should be done:
2272 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2273 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2274
2275 $newOutput = array();
2276 foreach ($output as $kN => $kInfo) {
2277 // Table:
2278 $kInfo['Table'] = $ORIG_tableName;
2279
2280 // Column
2281 if (isset($revFields[$kInfo['Column_name']])) {
2282 $kInfo['Column_name'] = $revFields[$kInfo['Column_name']];
2283 }
2284
2285 // Write it back:
2286 $newOutput[$kN] = $kInfo;
2287 }
2288 $output = $newOutput;
2289 }
2290
2291 return $output;
2292 }
2293
2294 /**
2295 * mysql() wrapper function, used by the Install Tool.
2296 *
2297 * @return array
2298 */
2299 public function admin_get_charsets() {
2300 return array();
2301 }
2302
2303 /**
2304 * mysql() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
2305 *
2306 * @param string Query to execute
2307 * @return pointer Result pointer
2308 */
2309 public function admin_query($query) {
2310 $parsedQuery = $this->SQLparser->parseSQL($query);
2311 $ORIG_table = $parsedQuery['TABLE'];
2312
2313 if (is_array($parsedQuery)) {
2314
2315 // Process query based on type:
2316 switch ($parsedQuery['type']) {
2317 case 'CREATETABLE':
2318 case 'ALTERTABLE':
2319 case 'DROPTABLE':
2320 if (file_exists(PATH_typo3conf.'temp_fieldInfo.php')) unlink(PATH_typo3conf.'temp_fieldInfo.php');
2321 $this->map_genericQueryParsed($parsedQuery);
2322 break;
2323 case 'INSERT':
2324 case 'TRUNCATETABLE':
2325 $this->map_genericQueryParsed($parsedQuery);
2326 break;
2327 case 'CREATEDATABASE':
2328 die('Creating a database with DBAL is not supported. Did you really read the manual?');
2329 break;
2330 default:
2331 die('ERROR: Invalid Query type ('.$parsedQuery['type'].') for ->admin_query() function!: "'.htmlspecialchars($query).'"');
2332 break;
2333 }
2334
2335 // Setting query array (for other applications to access if needed)
2336 $this->lastParsedAndMappedQueryArray = $parsedQuery;
2337
2338 // Execute query (based on handler derived from the TABLE name which we actually know for once!)
2339 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_table);
2340 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2341 case 'native':
2342 // Compiling query:
2343 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2344
2345 if ($this->lastParsedAndMappedQueryArray['type']=='INSERT') {
2346 return mysql_query($compiledQuery, $this->link);
2347 }
2348 return mysql_query($compiledQuery[0], $this->link);
2349 break;
2350 case 'adodb':
2351 // Compiling query:
2352 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2353 switch ($this->lastParsedAndMappedQueryArray['type']) {
2354 case 'INSERT':
2355 return $this->exec_INSERTquery($this->lastParsedAndMappedQueryArray['TABLE'], $compiledQuery);
2356 case 'TRUNCATETABLE':
2357 return $this->exec_TRUNCATEquery($this->lastParsedAndMappedQueryArray['TABLE']);
2358 }
2359 return $this->handlerInstance[$this->lastHandlerKey]->DataDictionary->ExecuteSQLArray($compiledQuery);
2360 break;
2361 case 'userdefined':
2362 // Compiling query:
2363 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2364
2365 return $this->handlerInstance[$this->lastHandlerKey]->admin_query($compiledQuery);
2366 break;
2367 }
2368 } else die('ERROR: Query could not be parsed: "'.htmlspecialchars($parsedQuery).'". Query: "'.htmlspecialchars($query).'"');
2369 }
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380 /************************************
2381 *
2382 * Handler management
2383 *
2384 **************************************/
2385
2386 /**
2387 * Return the handler key pointing to an appropriate database handler as found in $this->handlerCfg array
2388 * 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!)
2389 *
2390 * @param string Table list, eg. "pages" or "pages, tt_content" or "pages AS A, tt_content AS B"
2391 * @return string Handler key (see $this->handlerCfg array) for table
2392 */
2393 public function handler_getFromTableList($tableList) {
2394
2395 $key = $tableList;
2396
2397 if (!isset($this->cache_handlerKeyFromTableList[$key])) {
2398
2399 // Get tables separated:
2400 $_tableList = $tableList;
2401 $tableArray = $this->SQLparser->parseFromTables($_tableList);
2402
2403 // If success, traverse the tables:
2404 if (is_array($tableArray) && count($tableArray)) {
2405 $outputHandlerKey = '';
2406
2407 foreach ($tableArray as $vArray) {
2408 // Find handler key, select "_DEFAULT" if none is specifically configured:
2409 $handlerKey = $this->table2handlerKeys[$vArray['table']] ? $this->table2handlerKeys[$vArray['table']] : '_DEFAULT';
2410
2411 // In case of separate handler keys for joined tables:
2412 if ($outputHandlerKey && $handlerKey != $outputHandlerKey) {
2413 die('DBAL fatal error: Tables in this list "'.$tableList.'" didn\'t use the same DB handler!');
2414 }
2415
2416 $outputHandlerKey = $handlerKey;
2417 }
2418
2419 // Check initialized state; if handler is NOT initialized (connected) then we will connect it!
2420 if (!isset($this->handlerInstance[$outputHandlerKey])) {
2421 $this->handler_init($outputHandlerKey);
2422 }
2423
2424 // Return handler key:
2425 $this->cache_handlerKeyFromTableList[$key] = $outputHandlerKey;
2426 } else {
2427 die('DBAL fatal error: No handler found in handler_getFromTableList() for: "'.$tableList.'" ('.$tableArray.')');
2428 }
2429 }
2430
2431 return $this->cache_handlerKeyFromTableList[$key];
2432 }
2433
2434 /**
2435 * Initialize handler (connecting to database)
2436 *
2437 * @param string Handler key
2438 * @return boolean If connection went well, return TRUE
2439 * @see handler_getFromTableList()
2440 */
2441 public function handler_init($handlerKey) {
2442
2443 // Find handler configuration:
2444 $cfgArray = $this->handlerCfg[$handlerKey];
2445 $handlerType = (string)$cfgArray['type'];
2446 $output = FALSE;
2447
2448 if (is_array($cfgArray)) {
2449 switch ($handlerType) {
2450 case 'native':
2451 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
2452 $link = mysql_connect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], TRUE);
2453 } else {
2454 $link = mysql_pconnect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password']);
2455 }
2456
2457 // Set handler instance:
2458 $this->handlerInstance[$handlerKey] = array('handlerType' => 'native', 'link' => $link);
2459
2460 // If link succeeded:
2461 if ($link) {
2462 // For default, set ->link (see t3lib_DB)
2463 if ($handlerKey == '_DEFAULT') {
2464 $this->link = $link;
2465 }
2466
2467 // Select database as well:
2468 if (mysql_select_db($cfgArray['config']['database'], $link)) {
2469 $output = TRUE;
2470 }
2471 $setDBinit = t3lib_div::trimExplode(chr(10), $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'], 1);
2472 foreach ($setDBinit as $v) {
2473 if (mysql_query($v, $this->link) === FALSE) {
2474 t3lib_div::sysLog('Could not initialize DB connection with query "'.$v.'".','Core',3);
2475 }
2476 }
2477 } else {
2478 t3lib_div::sysLog('Could not connect to MySQL server '.$cfgArray['config']['host'].' with user '.$cfgArray['config']['username'].'.','Core',4);
2479 }
2480 break;
2481 case 'adodb':
2482 $output = TRUE;
2483 require_once(t3lib_extMgm::extPath('adodb').'adodb/adodb.inc.php');
2484 if (!defined('ADODB_FORCE_NULLS')) define('ADODB_FORCE_NULLS', 1);
2485 $GLOBALS['ADODB_FORCE_TYPE'] = ADODB_FORCE_VALUE;
2486 $GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_BOTH;
2487
2488 $this->handlerInstance[$handlerKey] = &ADONewConnection($cfgArray['config']['driver']);
2489
2490 // Set driver-specific options
2491 if (isset($cfgArray['config']['driverOptions'])) {
2492 foreach ($cfgArray['config']['driverOptions'] as $optionName => $optionValue) {
2493 $optionSetterName = 'set' . ucfirst($optionName);
2494 if (method_exists($this->handlerInstance[$handlerKey], $optionSetterName)) {
2495 $this->handlerInstance[$handlerKey]->$optionSetterName($optionValue);
2496 } else {
2497 $this->handlerInstance[$handlerKey]->$optionName = $optionValue;
2498 }
2499 }
2500 }
2501
2502 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
2503 $this->handlerInstance[$handlerKey]->Connect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''),$cfgArray['config']['username'],$cfgArray['config']['password'],$cfgArray['config']['database']);
2504 } else {
2505 $this->handlerInstance[$handlerKey]->PConnect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''),$cfgArray['config']['username'],$cfgArray['config']['password'],$cfgArray['config']['database']);
2506 }
2507 if (!$this->handlerInstance[$handlerKey]->isConnected()) {
2508 $dsn = $cfgArray['config']['driver'].'://'.$cfgArray['config']['username'].
2509 (strlen($cfgArray['config']['password']) ? ':XXXX@' : '').
2510 $cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : '').'/'.$cfgArray['config']['database'].
2511 ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'] ? '' : '?persistent=1');
2512 t3lib_div::sysLog('Could not connect to DB server using ADOdb on '.$cfgArray['config']['host'].' with user '.$cfgArray['config']['username'].'.','Core',4);
2513 error_log('DBAL error: Connection to '.$dsn.' failed. Maybe PHP doesn\'t support the database?');
2514 $output = FALSE;
2515 } else {
2516 $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
2517 $this->handlerInstance[$handlerKey]->last_insert_id = 0;
2518 if (isset($cfgArray['config']['sequenceStart'])) {
2519 $this->handlerInstance[$handlerKey]->sequenceStart = $cfgArray['config']['sequenceStart'];
2520 } else {
2521 $this->handlerInstance[$handlerKey]->sequenceStart = 1;
2522 }
2523 }
2524 break;
2525 case 'userdefined':
2526 // Find class file:
2527 $fileName = t3lib_div::getFileAbsFileName($cfgArray['config']['classFile']);
2528 if (@is_file($fileName)) {
2529 require_once($fileName);
2530 } else die('DBAL error: "'.$fileName.'" was not a file to include.');
2531
2532 // Initialize:
2533 $this->handlerInstance[$handlerKey] = t3lib_div::makeInstance($cfgArray['config']['class']);
2534 $this->handlerInstance[$handlerKey]->init($cfgArray,$this);
2535
2536 if (is_object($this->handlerInstance[$handlerKey])) {
2537 $output = TRUE;
2538 }
2539 break;
2540 default:
2541 die('ERROR: Invalid handler type: "'.$cfgArray['type'].'"');
2542 break;
2543 }
2544
2545 return $output;
2546 } else die('ERROR: No handler for key "'.$handlerKey.'"');
2547 }
2548
2549
2550 /**
2551 * Checks whether the DBAL is currently inside an operation running on the "native" DB handler (i.e. MySQL)
2552 *
2553 * @return boolean True if running on "native" DB handler (i.e. MySQL)
2554 */
2555 public function runningNative() {
2556 return ((string)$this->handlerCfg[$this->lastHandlerKey]['type']==='native');
2557 }
2558
2559
2560 /**
2561 * Checks whether the ADOdb handler is running with a driver that contains the argument
2562 *
2563 * @param string $driver Driver name, matched with strstr().
2564 * @return boolean True if running with the given driver
2565 */
2566 public function runningADOdbDriver($driver) {
2567 return strstr($this->handlerCfg[$this->lastHandlerKey]['config']['driver'], $driver);
2568 }
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579 /************************************
2580 *
2581 * Table/Field mapping
2582 *
2583 **************************************/
2584
2585 /**
2586 * Checks if mapping is needed for a table(list)
2587 *
2588 * @param string List of tables in query
2589 * @param boolean If TRUE, it will check only if FIELDs are configured and ignore the mapped table name if any.
2590 * @return mixed Returns an array of table names (parsed version of input table) if mapping is needed, otherwise just FALSE.
2591 */
2592 protected function map_needMapping($tableList, $fieldMappingOnly = FALSE) {
2593 $key = $tableList.'|'.$fieldMappingOnly;
2594 if (!isset($this->cache_mappingFromTableList[$key])) {
2595 $this->cache_mappingFromTableList[$key] = FALSE; // Default:
2596
2597 $tables = $this->SQLparser->parseFromTables($tableList);
2598 if (is_array($tables)) {
2599 foreach ($tables as $tableCfg) {
2600 if ($fieldMappingOnly) {
2601 if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
2602 $this->cache_mappingFromTableList[$key] = $tables;
2603 } elseif (is_array($tableCfg['JOIN'])) {
2604 foreach ($tableCfg['JOIN'] as $join) {
2605 if (is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
2606 $this->cache_mappingFromTableList[$key] = $tables;
2607 break;
2608 }
2609 }
2610 }
2611 } else {
2612 if (is_array($this->mapping[$tableCfg['table']])) {
2613 $this->cache_mappingFromTableList[$key] = $tables;
2614 } elseif (is_array($tableCfg['JOIN'])) {
2615 foreach ($tableCfg['JOIN'] as $join) {
2616 if (is_array($this->mapping[$join['withTable']])) {
2617 $this->cache_mappingFromTableList[$key] = $tables;
2618 break;
2619 }
2620 }
2621 }
2622 }
2623 }
2624 }
2625 }
2626
2627 return $this->cache_mappingFromTableList[$key];
2628 }
2629
2630 /**
2631 * Takes an associated array with field => value pairs and remaps the field names if configured for this table in $this->mapping array.
2632 * Be careful not to map a field name to another existing fields name (although you can use this to swap fieldnames of course...:-)
2633 * Observe mapping problems with join-results (more than one table): Joined queries should always prefix the table name to avoid problems with this.
2634 * Observe that alias fields are not mapped of course (should not be a problem though)
2635 *
2636 * @param array Input array, associative keys
2637 * @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!
2638 * @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!)
2639 * @return array Output array, with mapped associative keys.
2640 */
2641 protected function map_assocArray($input, $tables, $rev = FALSE) {
2642 // Traverse tables from query (hopefully only one table):
2643 foreach ($tables as $tableCfg) {
2644 if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
2645
2646 // Get the map (reversed if needed):
2647 if ($rev) {
2648 $theMap = array_flip($this->mapping[$tableCfg['table']]['mapFieldNames']);
2649 } else {
2650 $theMap = $this->mapping[$tableCfg['table']]['mapFieldNames'];
2651 }
2652
2653 // Traverse selected record, map fieldnames:
2654 $output = array();
2655 foreach ($input as $fN => $value) {
2656
2657 // Set the field name, change it if found in mapping array:
2658 if ($theMap[$fN]) {
2659 $newKey = $theMap[$fN];
2660 } else {
2661 $newKey = $fN;
2662 }
2663
2664 // Set value to fieldname:
2665 $output[$newKey] = $value;
2666 }
2667
2668 // When done, override the $input array with the result:
2669 $input = $output;
2670 }
2671 }
2672
2673 // Return input array (which might have been altered in the mean time)
2674 return $input;
2675 }
2676
2677 /**
2678 * Remaps table/field names in a SELECT query's parts
2679 * Notice: All arguments are passed by reference!
2680 *
2681 * @param string List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
2682 * @param string Table(s) from which to select. This is what comes right after "FROM ...". Require value.
2683 * @param string Where clause. This is what comes right after "WHERE ...". Can be blank.
2684 * @param string Group by field(s)
2685 * @param string Order by field(s)
2686 * @return void
2687 * @see exec_SELECTquery()
2688 */
2689 protected function map_remapSELECTQueryParts(&$select_fields, &$from_table, &$where_clause, &$groupBy, &$orderBy) {
2690 // Tables:
2691 $tables = $this->SQLparser->parseFromTables($from_table);
2692 $defaultTable = $tables[0]['table'];
2693 foreach ($tables as $k => $v) {
2694 if ($this->mapping[$v['table']]['mapTableName']) {
2695 $tables[$k]['table'] = $this->mapping[$v['table']]['mapTableName'];
2696 }
2697 // Mapping JOINS
2698 if (is_array($v['JOIN'])) {
2699 foreach($v['JOIN'] as $joinCnt => $join) {
2700 // Mapping withTable of the JOIN
2701 if ($this->mapping[$join['withTable']]['mapTableName']) {
2702 $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$join['withTable']]['mapTableName'];
2703 }
2704 $onPartsArray = array();
2705 // Mapping ON parts of the JOIN
2706 if (is_array($join['ON'])) {
2707 foreach ($join['ON'] as $onParts) {
2708 if (isset($this->mapping[$onParts['table']]['mapFieldNames'][$onParts['field']])) {
2709 $onParts['field'] = $this->mapping[$onParts['table']]['mapFieldNames'][$onParts['field']];
2710 }
2711 if (isset($this->mapping[$onParts['table']]['mapTableName'])) {
2712 $onParts['table'] = $this->mapping[$onParts['table']]['mapTableName'];
2713 }
2714 $onPartsArray[] = $onParts;
2715 }
2716 $tables[$k]['JOIN'][$joinCnt]['ON'] = $onPartsArray;
2717 }
2718 }
2719 }
2720 }
2721 $from_table = $this->SQLparser->compileFromTables($tables);
2722
2723 // Where clause:
2724 $whereParts = $this->SQLparser->parseWhereClause($where_clause);
2725 $this->map_sqlParts($whereParts,$defaultTable);
2726 $where_clause = $this->SQLparser->compileWhereClause($whereParts, FALSE);
2727
2728 // Select fields:
2729 $expFields = $this->SQLparser->parseFieldList($select_fields);
2730 $this->map_sqlParts($expFields,$defaultTable);
2731 $select_fields = $this->SQLparser->compileFieldList($expFields, FALSE, FALSE);
2732
2733 // Group By fields
2734 $expFields = $this->SQLparser->parseFieldList($groupBy);
2735 $this->map_sqlParts($expFields,$defaultTable);
2736 $groupBy = $this->SQLparser->compileFieldList($expFields);
2737
2738 // Order By fields
2739 $expFields = $this->SQLparser->parseFieldList($orderBy);
2740 $this->map_sqlParts($expFields,$defaultTable);
2741 $orderBy = $this->SQLparser->compileFieldList($expFields);
2742 }
2743
2744 /**
2745 * Generic mapping of table/field names arrays (as parsed by tx_dbal_sqlengine)
2746 *
2747 * @param array Array with parsed SQL parts; Takes both fields, tables, where-parts, group and order-by. Passed by reference.
2748 * @param string Default table name to assume if no table is found in $sqlPartArray
2749 * @return void
2750 * @access private
2751 * @see map_remapSELECTQueryParts()
2752 */
2753 protected function map_sqlParts(&$sqlPartArray, $defaultTable) {
2754 // Traverse sql Part array:
2755 if (is_array($sqlPartArray)) {
2756 foreach ($sqlPartArray as $k => $v) {
2757
2758 if (isset($sqlPartArray[$k]['type'])) {
2759 switch ($sqlPartArray[$k]['type']) {
2760 case 'flow-control':
2761 $temp = array($sqlPartArray[$k]['flow-control']);
2762 $this->map_sqlParts($temp, $defaultTable); // Call recursively!
2763 $sqlPartArray[$k]['flow-control'] = $temp[0];
2764 break;
2765 case 'CASE':
2766 if (isset($sqlPartArray[$k]['case_field'])) {
2767 $fieldArray = explode('.', $sqlPartArray[$k]['case_field']);
2768 if (count($fieldArray) == 1 && is_array($this->mapping[$defaultTable]['mapFieldNames']) && isset($this->mapping[$defaultTable]['mapFieldNames'][$fieldArray[0]])) {
2769 $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTable]['mapFieldNames'][$fieldArray[0]];
2770 }
2771 elseif (count($fieldArray) == 2) {
2772 // Map the external table
2773 $table = $fieldArray[0];
2774 if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
2775 $table = $this->mapping[$fieldArray[0]]['mapTableName'];
2776 }
2777 // Map the field itself
2778 $field = $fieldArray[1];
2779 if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
2780 $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
2781 }
2782 $sqlPartArray[$k]['case_field'] = $table . '.' . $field;
2783 }
2784 }
2785 foreach ($sqlPartArray[$k]['when'] as $key => $when) {
2786 $this->map_sqlParts($sqlPartArray[$k]['when'][$key]['when_value'], $defaultTable);
2787 }
2788 break;
2789 }
2790 }
2791
2792 // Look for sublevel (WHERE parts only)
2793 if (is_array($sqlPartArray[$k]['sub'])) {
2794 $this->map_sqlParts($sqlPartArray[$k]['sub'], $defaultTable); // Call recursively!
2795 } elseif (isset($sqlPartArray[$k]['func'])) {
2796 switch ($sqlPartArray[$k]['func']['type']) {
2797 case 'EXISTS':
2798 $subqueryDefaultTable = $sqlPartArray[$k]['func']['subquery']['FROM'][0]['table'];
2799 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['SELECT'], $subqueryDefaultTable);
2800 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['FROM'], $subqueryDefaultTable);
2801 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['WHERE'], $subqueryDefaultTable);
2802 break;
2803 case 'IFNULL':
2804 case 'LOCATE':
2805 // For the field, look for table mapping (generic):
2806 $t = $sqlPartArray[$k]['func']['table'] ? $sqlPartArray[$k]['func']['table'] : $defaultTable;
2807 if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]) {
2808 $sqlPartArray[$k]['func']['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']];
2809 }
2810 if ($this->mapping[$t]['mapTableName']) {
2811 $sqlPartArray[$k]['func']['table'] = $this->mapping[$t]['mapTableName'];
2812 }
2813 break;
2814 }
2815 } else {
2816 // For the field, look for table mapping (generic):
2817 $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable;
2818
2819 // Mapping field name, if set:
2820 if (is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']])) {
2821 $sqlPartArray[$k]['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']];
2822 }
2823
2824 // Mapping field name in SQL-functions like MIN(), MAX() or SUM()
2825 if ($this->mapping[$t]['mapFieldNames']) {
2826 $fieldArray = explode('.', $sqlPartArray[$k]['func_content']);
2827 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
2828 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2829 $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2830 }
2831 elseif (count($fieldArray) == 2) {
2832 // Map the external table
2833 $table = $fieldArray[0];
2834 if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
2835 $table = $this->mapping[$fieldArray[0]]['mapTableName'];
2836 }
2837 // Map the field itself
2838 $field = $fieldArray[1];
2839 if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
2840 $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
2841 }
2842 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field;
2843 $sqlPartArray[$k]['func_content'] = $table . '.' . $field;
2844 }
2845
2846 // Mapping flow-control statements
2847 if (isset($sqlPartArray[$k]['flow-control'])) {
2848 if (isset($sqlPartArray[$k]['flow-control']['type'])) {
2849 $temp = array($sqlPartArray[$k]['flow-control']);
2850 $this->map_sqlParts($temp, $t); // Call recursively!
2851 $sqlPartArray[$k]['flow-control'] = $temp[0];
2852 }
2853 }
2854 }
2855
2856 // Do we have a function (e.g., CONCAT)
2857 if (isset($v['value']['operator'])) {
2858 foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) {
2859 if (isset($this->mapping[$fieldDef['table']]['mapTableName'])) {
2860 $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$fieldDef['table']]['mapTableName'];
2861 }
2862 if (is_array($this->mapping[$fieldDef['table']]['mapFieldNames']) && isset($this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']])) {
2863 $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']];