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