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