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