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