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