Follow-up of changeset #28324: Quoting when part of CASE WHEN flow control
[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 $where_clause[$k]['func']['subquery'] = $this->quoteSELECTsubquery($v['func']['subquery']);
1149 } else {
1150 if ($where_clause[$k]['table'] != '') {
1151 $where_clause[$k]['table'] = $this->quoteName($where_clause[$k]['table']);
1152 }
1153 if (!is_numeric($where_clause[$k]['field'])) {
1154 $where_clause[$k]['field'] = $this->quoteName($where_clause[$k]['field']);
1155 }
1156 if (isset($where_clause[$k]['calc_table'])) {
1157 if ($where_clause[$k]['calc_table'] != '') {
1158 $where_clause[$k]['calc_table'] = $this->quoteName($where_clause[$k]['calc_table']);
1159 }
1160 if ($where_clause[$k]['calc_field'] != '') {
1161 $where_clause[$k]['calc_field'] = $this->quoteName($where_clause[$k]['calc_field']);
1162 }
1163 }
1164 }
1165 if ($where_clause[$k]['comparator']) {
1166 if (isset($v['value']['operator'])) {
1167 foreach ($where_clause[$k]['value']['args'] as $argK => $fieldDef) {
1168 $where_clause[$k]['value']['args'][$argK]['table'] = $this->quoteName($fieldDef['table']);
1169 $where_clause[$k]['value']['args'][$argK]['field'] = $this->quoteName($fieldDef['field']);
1170 }
1171 } else {
1172 // Detecting value type; list or plain:
1173 if (t3lib_div::inList('NOTIN,IN', strtoupper(str_replace(array(' ',"\n", "\r", "\t"), '', $where_clause[$k]['comparator'])))) {
1174 if (isset($v['subquery'])) {
1175 $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']);
1176 }
1177 } else {
1178 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], '.')) {
1179 $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]);
1180 }
1181 }
1182 }
1183 }
1184 }
1185
1186 return $where_clause;
1187 }
1188
1189 /**
1190 * [Describe function...]
1191 *
1192 * @param [type] $$groupBy: ...
1193 * @return [type] ...
1194 */
1195 protected function quoteGroupBy($groupBy) {
1196 if ($groupBy === '') return '';
1197 if ($this->runningNative()) return $groupBy;
1198
1199 $groupBy = $this->SQLparser->parseFieldList($groupBy);
1200 foreach ($groupBy as $k => $v) {
1201 $groupBy[$k]['field'] = $this->quoteName($groupBy[$k]['field']);
1202 if ($groupBy[$k]['table'] != '') {
1203 $groupBy[$k]['table'] = $this->quoteName($groupBy[$k]['table']);
1204 }
1205 }
1206 return $this->SQLparser->compileFieldList($groupBy);
1207 }
1208
1209 /**
1210 * [Describe function...]
1211 *
1212 * @param [type] $$orderBy: ...
1213 * @return [type] ...
1214 */
1215 protected function quoteOrderBy($orderBy) {
1216 if ($orderBy === '') return '';
1217 if ($this->runningNative()) return $orderBy;
1218
1219 $orderBy = $this->SQLparser->parseFieldList($orderBy);
1220 foreach ($orderBy as $k => $v) {
1221 $orderBy[$k]['field'] = $this->quoteName($orderBy[$k]['field']);
1222 if ($orderBy[$k]['table'] != '') {
1223 $orderBy[$k]['table'] = $this->quoteName($orderBy[$k]['table']);
1224 }
1225 }
1226 return $this->SQLparser->compileFieldList($orderBy);
1227 }
1228
1229
1230
1231 /**************************************
1232 *
1233 * Various helper functions
1234 *
1235 **************************************/
1236
1237 /**
1238 * Escaping and quoting values for SQL statements.
1239 *
1240 * @param string Input string
1241 * @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!).
1242 * @return string Output string; Wrapped in single quotes and quotes in the string (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
1243 * @see quoteStr()
1244 */
1245 public function fullQuoteStr($str, $table) {
1246 return '\'' . $this->quoteStr($str, $table) . '\'';
1247 }
1248
1249 /**
1250 * Substitution for PHP function "addslashes()"
1251 * 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()!
1252 *
1253 * @param string Input string
1254 * @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!).
1255 * @return string Output string; Quotes (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
1256 * @see quoteStr()
1257 */
1258 public function quoteStr($str, $table) {
1259 $this->lastHandlerKey = $this->handler_getFromTableList($table);
1260 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1261 case 'native':
1262 $str = mysql_real_escape_string($str, $this->handlerInstance[$this->lastHandlerKey]['link']);
1263 break;
1264 case 'adodb':
1265 $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1);
1266 break;
1267 case 'userdefined':
1268 $str = $this->handlerInstance[$this->lastHandlerKey]->quoteStr($str);
1269 break;
1270 default:
1271 die('No handler found!!!');
1272 break;
1273 }
1274
1275 return $str;
1276 }
1277
1278 /**
1279 * Quotes an object name (table name, field, ...)
1280 *
1281 * @param string Object's name
1282 * @param string Handler key
1283 * @param boolean If method NameQuote() is not used, whether to use backticks instead of driver-specific quotes
1284 * @return string Properly-quoted object's name
1285 */
1286 public function quoteName($name, $handlerKey = NULL, $useBackticks = FALSE) {
1287 $handlerKey = $handlerKey ? $handlerKey : $this->lastHandlerKey;
1288 $useNameQuote = isset($this->handlerCfg[$handlerKey]['config']['useNameQuote']) ? $this->handlerCfg[$handlerKey]['config']['useNameQuote'] : FALSE;
1289 if ($useNameQuote) {
1290 return $this->handlerInstance[$handlerKey]->DataDictionary->NameQuote($name);
1291 } else {
1292 $quote = $useBackticks ? '`' : $this->handlerInstance[$handlerKey]->nameQuote;
1293 return $quote . $name . $quote;
1294 }
1295 }
1296
1297 /**
1298 * Return MetaType for native field type (ADOdb only!)
1299 *
1300 * @param string native type as reported by admin_get_fields()
1301 * @param string Table name for which query type string. Important for detection of DBMS handler of the query!
1302 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1303 */
1304 public function MetaType($type, $table, $max_length = -1) {
1305 $this->lastHandlerKey = $this->handler_getFromTableList($table);
1306 $str = '';
1307 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1308 case 'native':
1309 $str = $type;
1310 break;
1311 case 'adodb':
1312 if (in_array($table, $this->cache_fieldType)) {
1313 $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1);
1314 $str = $rs->MetaType($type, $max_length);
1315 }
1316 break;
1317 case 'userdefined':
1318 $str = $this->handlerInstance[$this->lastHandlerKey]->MetaType($str,$table,$max_length);
1319 break;
1320 default:
1321 die('No handler found!!!');
1322 break;
1323 }
1324
1325 return $str;
1326 }
1327
1328
1329 /**
1330 * Return MetaType for native MySQL field type
1331 *
1332 * @param string native type as reported as in mysqldump files
1333 * @return string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1334 */
1335 public function MySQLMetaType($t) {
1336
1337 switch (strtoupper($t)) {
1338 case 'STRING':
1339 case 'CHAR':
1340 case 'VARCHAR':
1341 case 'TINYBLOB':
1342 case 'TINYTEXT':
1343 case 'ENUM':
1344 case 'SET': return 'C';
1345
1346 case 'TEXT':
1347 case 'LONGTEXT':
1348 case 'MEDIUMTEXT': return 'XL';
1349
1350 case 'IMAGE':
1351 case 'LONGBLOB':
1352 case 'BLOB':
1353 case 'MEDIUMBLOB': return 'B';
1354
1355 case 'YEAR':
1356 case 'DATE': return 'D';
1357
1358 case 'TIME':
1359 case 'DATETIME':
1360 case 'TIMESTAMP': return 'T';
1361
1362 case 'FLOAT':
1363 case 'DOUBLE': return 'F';
1364
1365 case 'INT':
1366 case 'INTEGER':
1367 case 'TINYINT':
1368 case 'SMALLINT':
1369 case 'MEDIUMINT':
1370 case 'BIGINT': return 'I8'; // we always return I8 to be on the safe side. Under some circumstances the fields are to small otherwise...
1371
1372 default: return 'N';
1373 }
1374 }
1375
1376 /**
1377 * Return actual MySQL type for meta field type
1378 *
1379 * @param string Meta type (currenly ADOdb syntax only, http://phplens.com/lens/adodb/docs-adodb.htm#metatype)
1380 * @return string native type as reported as in mysqldump files, uppercase
1381 */
1382 public function MySQLActualType($meta) {
1383 switch (strtoupper($meta)) {
1384 case 'C': return 'VARCHAR';
1385 case 'XL':
1386 case 'X': return 'LONGTEXT';
1387
1388 case 'C2': return 'VARCHAR';
1389 case 'X2': return 'LONGTEXT';
1390
1391 case 'B': return 'LONGBLOB';
1392
1393 case 'D': return 'DATE';
1394 case 'T': return 'DATETIME';
1395 case 'L': return 'TINYINT';
1396
1397 case 'I':
1398 case 'I1':
1399 case 'I2':
1400 case 'I4':
1401 case 'I8': return 'BIGINT'; // we only have I8 in DBAL, see MySQLMetaType()
1402
1403 case 'F': return 'DOUBLE';
1404 case 'N': return 'NUMERIC';
1405
1406 default: return $meta;
1407 }
1408 }
1409
1410
1411
1412
1413 /**************************************
1414 *
1415 * SQL wrapper functions (Overriding parent methods)
1416 * (For use in your applications)
1417 *
1418 **************************************/
1419
1420 /**
1421 * Returns the error status on the most recent sql() execution (based on $this->lastHandlerKey)
1422 *
1423 * @return string Handler error strings
1424 */
1425 public function sql_error() {
1426 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1427 case 'native':
1428 $output = mysql_error($this->handlerInstance[$this->lastHandlerKey]['link']);
1429 break;
1430 case 'adodb':
1431 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorMsg();
1432 break;
1433 case 'userdefined':
1434 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_error();
1435 break;
1436 }
1437 return $output;
1438 }
1439
1440 /**
1441 * Returns the error number on the most recent sql() execution (based on $this->lastHandlerKey)
1442 *
1443 * @return int Handler error number
1444 */
1445 public function sql_errno() {
1446 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1447 case 'native':
1448 $output = mysql_errno($this->handlerInstance[$this->lastHandlerKey]['link']);
1449 break;
1450 case 'adodb':
1451 $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorNo();
1452 break;
1453 case 'userdefined':
1454 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_errno();
1455 break;
1456 }
1457 return $output;
1458 }
1459
1460 /**
1461 * Returns the number of selected rows.
1462 *
1463 * @param pointer Result pointer / DBAL object
1464 * @return integer Number of resulting rows.
1465 */
1466 public function sql_num_rows(&$res) {
1467 if ($res === FALSE) return 0;
1468
1469 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1470 switch ($handlerType) {
1471 case 'native':
1472 $output = mysql_num_rows($res);
1473 break;
1474 case 'adodb':
1475 $output = method_exists($res, 'RecordCount') ? $res->RecordCount() : 0;
1476 break;
1477 case 'userdefined':
1478 $output = $res->sql_num_rows();
1479 break;
1480 }
1481 return $output;
1482 }
1483
1484 /**
1485 * Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows.
1486 *
1487 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1488 * @return array Associative array of result row.
1489 */
1490 public function sql_fetch_assoc(&$res) {
1491 $output = array();
1492
1493 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : (is_resource($res) ? 'native' : FALSE);
1494 switch ($handlerType) {
1495 case 'native':
1496 $output = mysql_fetch_assoc($res);
1497 $tableList = $this->resourceIdToTableNameMap[(string)$res]; // Reading list of tables from SELECT query:
1498 break;
1499 case 'adodb':
1500 // Check if method exists for the current $res object.
1501 // If a table exists in TCA but not in the db, a error
1502 // occured because $res is not a valid object.
1503 if (method_exists($res, 'FetchRow')) {
1504 $output = $res->FetchRow();
1505 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query:
1506
1507 // Removing all numeric/integer keys.
1508 // A workaround because in ADOdb we would need to know what we want before executing the query...
1509 if (is_array($output)) {
1510 foreach ($output as $key => $value) {
1511 if (is_integer($key)) {
1512 unset($output[$key]);
1513 }
1514 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.
1515 }
1516 }
1517 }
1518 break;
1519 case 'userdefined':
1520 $output = $res->sql_fetch_assoc();
1521 $tableList = $res->TYPO3_DBAL_tableList; // Reading list of tables from SELECT query:
1522 break;
1523 }
1524
1525 // Table/Fieldname mapping:
1526 if (is_array($output)) {
1527 if ($tables = $this->map_needMapping($tableList,TRUE)) {
1528 $output = $this->map_assocArray($output,$tables,1);
1529 }
1530 }
1531
1532 // Return result:
1533 return $output;
1534 }
1535
1536 /**
1537 * Returns an array that corresponds to the fetched row, or FALSE if there are no more rows.
1538 * The array contains the values in numerical indices.
1539 *
1540 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1541 * @return array Array with result rows.
1542 */
1543 public function sql_fetch_row(&$res) {
1544 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1545 switch ($handlerType) {
1546 case 'native':
1547 $output = mysql_fetch_row($res);
1548 break;
1549 case 'adodb':
1550 // Check if method exists for the current $res object.
1551 // If a table exists in TCA but not in the db, a error
1552 // occured because $res is not a valid object.
1553 if (method_exists($res, 'FetchRow')) {
1554 $output = $res->FetchRow();
1555
1556 // Removing all assoc. keys.
1557 // A workaround because in ADOdb we would need to know what we want before executing the query...
1558 if (is_array($output)) {
1559 foreach ($output as $key => $value) {
1560 if (!is_integer($key)) unset($output[$key]);
1561 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.
1562 }
1563 }
1564 }
1565 break;
1566 case 'userdefined':
1567 $output = $res->sql_fetch_row();
1568 break;
1569 }
1570 return $output;
1571 }
1572
1573 /**
1574 * Free result memory / unset result object
1575 *
1576 * @param pointer MySQL result pointer to free / DBAL object
1577 * @return boolean Returns TRUE on success or FALSE on failure.
1578 */
1579 public function sql_free_result(&$res) {
1580 if ($res === FALSE) return FALSE;
1581
1582 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1583 switch ($handlerType) {
1584 case 'native':
1585 $output = mysql_free_result($res);
1586 break;
1587 case 'adodb':
1588 if (method_exists($res, 'Close')) {
1589 $res->Close();
1590 unset($res);
1591 $output = TRUE;
1592 } else {
1593 $output = FALSE;
1594 }
1595 break;
1596 case 'userdefined':
1597 unset($res);
1598 break;
1599 }
1600 return $output;
1601 }
1602
1603 /**
1604 * Get the ID generated from the previous INSERT operation
1605 *
1606 * @return integer The uid of the last inserted record.
1607 */
1608 public function sql_insert_id() {
1609 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1610 case 'native':
1611 $output = mysql_insert_id($this->handlerInstance[$this->lastHandlerKey]['link']);
1612 break;
1613 case 'adodb':
1614 $output = $this->handlerInstance[$this->lastHandlerKey]->last_insert_id;
1615 break;
1616 case 'userdefined':
1617 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_insert_id();
1618 break;
1619 }
1620 return $output;
1621 }
1622
1623 /**
1624 * Returns the number of rows affected by the last INSERT, UPDATE or DELETE query
1625 *
1626 * @return integer Number of rows affected by last query
1627 */
1628 public function sql_affected_rows() {
1629 switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
1630 case 'native':
1631 $output = mysql_affected_rows();
1632 break;
1633 case 'adodb':
1634 $output = $this->handlerInstance[$this->lastHandlerKey]->Affected_Rows();
1635 break;
1636 case 'userdefined':
1637 $output = $this->handlerInstance[$this->lastHandlerKey]->sql_affected_rows();
1638 break;
1639 }
1640 return $output;
1641 }
1642
1643 /**
1644 * Move internal result pointer
1645 *
1646 * @param pointer MySQL result pointer (of SELECT query) / DBAL object
1647 * @param integer Seek result number.
1648 * @return boolean Returns TRUE on success or FALSE on failure.
1649 */
1650 public function sql_data_seek(&$res, $seek) {
1651 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1652 switch ($handlerType) {
1653 case 'native':
1654 $output = mysql_data_seek($res,$seek);
1655 break;
1656 case 'adodb':
1657 $output = $res->Move($seek);
1658 break;
1659 case 'userdefined':
1660 $output = $res->sql_data_seek($seek);
1661 break;
1662 }
1663 return $output;
1664 }
1665
1666 /**
1667 * Get the type of the specified field in a result
1668 *
1669 * If the first parameter is a string, it is used as table name for the lookup.
1670 *
1671 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name
1672 * @param integer Field index. In case of ADOdb a string (field name!) FIXME
1673 * @return string Returns the type of the specified field index
1674 */
1675 public function sql_field_metatype($table, $field) {
1676 // If $table and/or $field are mapped, use the original names instead
1677 foreach ($this->mapping as $tableName => $tableMapInfo) {
1678 if (isset($tableMapInfo['mapTableName']) && $tableMapInfo['mapTableName'] === $table) {
1679 // Table name is mapped => use original name
1680 $table = $tableName;
1681 }
1682
1683 if (isset($tableMapInfo['mapFieldNames'])) {
1684 foreach ($tableMapInfo['mapFieldNames'] as $fieldName => $fieldMapInfo) {
1685 if ($fieldMapInfo === $field) {
1686 // Field name is mapped => use original name
1687 $field = $fieldName;
1688 }
1689 }
1690 }
1691 }
1692
1693 return $this->cache_fieldType[$table][$field]['metaType'];
1694 }
1695
1696 /**
1697 * Get the type of the specified field in a result
1698 *
1699 * If the first parameter is a string, it is used as table name for the lookup.
1700 *
1701 * @param pointer MySQL result pointer (of SELECT query) / DBAL object / table name
1702 * @param integer Field index. In case of ADOdb a string (field name!) FIXME
1703 * @return string Returns the type of the specified field index
1704 */
1705 public function sql_field_type(&$res,$pointer) {
1706 if ($res === null) {
1707 debug(array('no res in sql_field_type!'));
1708 return 'text';
1709 }
1710 elseif (is_string($res)){
1711 if ($res === 'tx_dbal_debuglog') return 'text';
1712 $handlerType = 'adodb';
1713 }
1714 else {
1715 $handlerType = is_object($res) ? $res->TYPO3_DBAL_handlerType : 'native';
1716 }
1717
1718 switch ($handlerType) {
1719 case 'native':
1720 $output = mysql_field_type($res,$pointer);
1721 break;
1722 case 'adodb':
1723 if (is_string($pointer)){
1724 $output = $this->cache_fieldType[$res][$pointer]['type'];
1725 }
1726
1727 break;
1728 case 'userdefined':
1729 $output = $res->sql_field_type($pointer);
1730 break;
1731 }
1732
1733 return $output;
1734 }
1735
1736
1737
1738
1739
1740
1741
1742
1743 /**********
1744 *
1745 * Legacy functions, bound to _DEFAULT handler. (Overriding parent methods)
1746 * Deprecated or still experimental.
1747 *
1748 **********/
1749
1750 /**
1751 * Executes query (on DEFAULT handler!)
1752 * DEPRECATED - use exec_* functions from this class instead!
1753 *
1754 * @param string Database name
1755 * @param string Query to execute
1756 * @return pointer Result pointer
1757 * @deprecated since TYPO3 4.1
1758 */
1759 public function sql($db,$query) {
1760 return $this->sql_query($query);
1761 }
1762
1763 /**
1764 * Executes a query
1765 * EXPERIMENTAL - This method will make its best to handle the query correctly
1766 * but if it cannot, it will simply pass the query to DEFAULT handler.
1767 *
1768 * You should use exec_* function from this class instead!
1769 * If you don't, anything that does not use the _DEFAULT handler will probably break!
1770 *
1771 * This method was deprecated in TYPO3 4.1 but is considered experimental since TYPO3 4.4
1772 * as it tries to handle the query correctly anyway.
1773 *
1774 * @param string Query to execute
1775 * @return pointer Result pointer / DBAL object
1776 */
1777 public function sql_query($query) {
1778 // This method is heavily used by Extbase, try to handle it with DBAL-native methods
1779 $queryParts = $this->SQLparser->parseSQL($query);
1780 if (is_array($queryParts) && t3lib_div::inList('SELECT,UPDATE,INSERT,DELETE', $queryParts['type'])) {
1781 return $this->exec_query($queryParts);
1782 }
1783
1784 switch ($this->handlerCfg['_DEFAULT']['type']) {
1785 case 'native':
1786 $sqlResult = mysql_query($query, $this->handlerInstance['_DEFAULT']['link']);
1787 break;
1788 case 'adodb':
1789 $sqlResult = $this->handlerInstance['_DEFAULT']->Execute($query);
1790 $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
1791 break;
1792 case 'userdefined':
1793 $sqlResult = $this->handlerInstance['_DEFAULT']->sql_query($query);
1794 $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
1795 break;
1796 }
1797
1798 if ($this->printErrors && $this->sql_error()) {
1799 debug(array($this->lastQuery, $this->sql_error()));
1800 }
1801
1802 return $sqlResult;
1803 }
1804
1805 /**
1806 * Opening the _DEFAULT connection handler to the database.
1807 * This is typically done by the scripts "init.php" in the backend or "index_ts.php" in the frontend (tslib_fe->connectToMySQL())
1808 * You wouldn't need to use this at any time - let TYPO3 core handle this.
1809 *
1810 * @param string Database host IP/domain
1811 * @param string Username to connect with.
1812 * @param string Password to connect with.
1813 * @return mixed Returns handler connection value
1814 * @deprecated since TYPO3 4.1
1815 * @see handler_init()
1816 */
1817 public function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password) {
1818 // Overriding the _DEFAULT handler configuration of username, password, localhost and database name:
1819 $this->handlerCfg['_DEFAULT']['config']['username'] = $TYPO3_db_username;
1820 $this->handlerCfg['_DEFAULT']['config']['password'] = $TYPO3_db_password;
1821 $this->handlerCfg['_DEFAULT']['config']['host'] = $TYPO3_db_host;
1822 $this->handlerCfg['_DEFAULT']['config']['database'] = TYPO3_db;
1823
1824 // Initializing and output value:
1825 $sqlResult = $this->handler_init('_DEFAULT');
1826 return $sqlResult;
1827 }
1828
1829 /**
1830 * Select database for _DEFAULT handler.
1831 *
1832 * @param string Database to connect to.
1833 * @return boolean Always returns TRUE; function is obsolete, database selection is made in handler_init() function!
1834 * @deprecated since TYPO3 4.1
1835 */
1836 public function sql_select_db($TYPO3_db) {
1837 return TRUE;
1838 }
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854 /**************************************
1855 *
1856 * SQL admin functions
1857 * (For use in the Install Tool and Extension Manager)
1858 *
1859 **************************************/
1860
1861 /**
1862 * Listing databases from current MySQL connection. NOTICE: It WILL try to select those databases and thus break selection of current database.
1863 * Use in Install Tool only!
1864 * Usage count/core: 1
1865 *
1866 * @return array Each entry represents a database name
1867 */
1868 public function admin_get_dbs() {
1869 $dbArr = array();
1870 switch ($this->handlerCfg['_DEFAULT']['type']) {
1871 case 'native':
1872 $db_list = mysql_list_dbs($this->link);
1873 while ($row = mysql_fetch_object($db_list)) {
1874 if ($this->sql_select_db($row->Database)) {
1875 $dbArr[] = $row->Database;
1876 }
1877 }
1878 break;
1879 case 'adodb':
1880 // check needed for install tool - otherwise it will just die because the call to
1881 // MetaDatabases is done on a stdClass instance
1882 if (method_exists($this->handlerInstance['_DEFAULT'],'MetaDatabases')) {
1883 $sqlDBs = $this->handlerInstance['_DEFAULT']->MetaDatabases();
1884 if (is_array($sqlDBs)) {
1885 foreach ($sqlDBs as $k => $theDB) {
1886 $dbArr[] = $theDB;
1887 }
1888 }
1889 }
1890 break;
1891 case 'userdefined':
1892 $dbArr = $this->handlerInstance['_DEFAULT']->admin_get_tables();
1893 break;
1894 }
1895
1896 return $dbArr;
1897 }
1898
1899 /**
1900 * Returns the list of tables from the system (quering the DBMSs)
1901 * It looks up all tables from the DBMS of the _DEFAULT handler and then add all tables *configured* to be managed by other handlers
1902 *
1903 * 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.
1904 *
1905 * @return array Tables in an array (tablename is in both key and value)
1906 * @todo Should the check for Oracle Recycle Bin stuff be moved elsewhere?
1907 * @todo Should return table details in value! see t3lib_db::admin_get_tables()
1908 */
1909 public function admin_get_tables() {
1910 $whichTables = array();
1911
1912 // Getting real list of tables:
1913 switch ($this->handlerCfg['_DEFAULT']['type']) {
1914 case 'native':
1915 $tables_result = mysql_query('SHOW TABLE STATUS FROM `' . TYPO3_db . '`', $this->handlerInstance['_DEFAULT']['link']);
1916 if (!$this->sql_error()) {
1917 while ($theTable = $this->sql_fetch_assoc($tables_result)) {
1918 $whichTables[current($theTable)] = current($theTable);
1919 }
1920 }
1921 break;
1922 case 'adodb':
1923 $sqlTables = $this->handlerInstance['_DEFAULT']->MetaTables('TABLES');
1924 while (list($k, $theTable) = each($sqlTables)) {
1925 if (preg_match('/BIN\$/', $theTable)) continue; // skip tables from the Oracle 10 Recycle Bin
1926 $whichTables[$theTable] = $theTable;
1927 }
1928 break;
1929 case 'userdefined':
1930 $whichTables = $this->handlerInstance['_DEFAULT']->admin_get_tables();
1931 break;
1932 }
1933
1934 // Check mapping:
1935 if (is_array($this->mapping) && count($this->mapping)) {
1936
1937 // Mapping table names in reverse, first getting list of real table names:
1938 $tMap = array();
1939 foreach ($this->mapping as $tN => $tMapInfo) {
1940 if (isset($tMapInfo['mapTableName'])) $tMap[$tMapInfo['mapTableName']]=$tN;
1941 }
1942
1943 // Do mapping:
1944 $newList=array();
1945 foreach ($whichTables as $tN) {
1946 if (isset($tMap[$tN])) $tN = $tMap[$tN];
1947 $newList[$tN] = $tN;
1948 }
1949
1950 $whichTables = $newList;
1951 }
1952
1953 // Adding tables configured to reside in other DBMS (handler by other handlers than the default):
1954 if (is_array($this->table2handlerKeys)) {
1955 foreach ($this->table2handlerKeys as $key => $handlerKey) {
1956 $whichTables[$key] = $key;
1957 }
1958 }
1959
1960 return $whichTables;
1961 }
1962
1963 /**
1964 * Returns information about each field in the $table (quering the DBMS)
1965 * In a DBAL this should look up the right handler for the table and return compatible information
1966 * 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
1967 *
1968 * @param string Table name
1969 * @return array Field information in an associative array with fieldname => field row
1970 */
1971 public function admin_get_fields($tableName) {
1972 $output = array();
1973
1974 // Do field mapping if needed:
1975 $ORIG_tableName = $tableName;
1976 if ($tableArray = $this->map_needMapping($tableName)) {
1977
1978 // Table name:
1979 if ($this->mapping[$tableName]['mapTableName']) {
1980 $tableName = $this->mapping[$tableName]['mapTableName'];
1981 }
1982 }
1983
1984 // Find columns
1985 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
1986 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1987 case 'native':
1988 $columns_res = mysql_query('SHOW columns FROM '.$tableName, $this->handlerInstance[$this->lastHandlerKey]['link']);
1989 while($fieldRow = mysql_fetch_assoc($columns_res)) {
1990 $output[$fieldRow['Field']] = $fieldRow;
1991 }
1992 break;
1993 case 'adodb':
1994 $fieldRows = $this->handlerInstance[$this->lastHandlerKey]->MetaColumns($tableName, FALSE);
1995 if (is_array($fieldRows)) {
1996 foreach ($fieldRows as $k => $fieldRow) {
1997 settype($fieldRow, 'array');
1998 $fieldRow['Field'] = $fieldRow['name'];
1999 $ntype = $this->MySQLActualType($this->MetaType($fieldRow['type'],$tableName));
2000 $ntype .= (($fieldRow['max_length'] != -1) ? (($ntype == 'INT') ? '(11)' :'('.$fieldRow['max_length'].')') : '');
2001 $fieldRow['Type'] = strtolower($ntype);
2002 $fieldRow['Null'] = '';
2003 $fieldRow['Key'] = '';
2004 $fieldRow['Default'] = $fieldRow['default_value'];
2005 $fieldRow['Extra'] = '';
2006 $output[$fieldRow['name']] = $fieldRow;
2007 }
2008 }
2009 break;
2010 case 'userdefined':
2011 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_fields($tableName);
2012 break;
2013 }
2014
2015 // mapping should be done:
2016 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2017 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2018
2019 $newOutput = array();
2020 foreach ($output as $fN => $fInfo) {
2021 if (isset($revFields[$fN])) {
2022 $fN = $revFields[$fN];
2023 $fInfo['Field'] = $fN;
2024 }
2025 $newOutput[$fN] = $fInfo;
2026 }
2027 $output = $newOutput;
2028 }
2029
2030 return $output;
2031 }
2032
2033 /**
2034 * Returns information about each index key in the $table (quering the DBMS)
2035 * In a DBAL this should look up the right handler for the table and return compatible information
2036 *
2037 * @param string Table name
2038 * @return array Key information in a numeric array
2039 */
2040 public function admin_get_keys($tableName) {
2041 $output = array();
2042
2043 // Do field mapping if needed:
2044 $ORIG_tableName = $tableName;
2045 if ($tableArray = $this->map_needMapping($tableName)) {
2046
2047 // Table name:
2048 if ($this->mapping[$tableName]['mapTableName']) {
2049 $tableName = $this->mapping[$tableName]['mapTableName'];
2050 }
2051 }
2052
2053 // Find columns
2054 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_tableName);
2055 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2056 case 'native':
2057 $keyRes = mysql_query('SHOW keys FROM '.$tableName, $this->handlerInstance[$this->lastHandlerKey]['link']);
2058 while($keyRow = mysql_fetch_assoc($keyRes)) {
2059 $output[] = $keyRow;
2060 }
2061 break;
2062 case 'adodb':
2063 $keyRows = $this->handlerInstance[$this->lastHandlerKey]->MetaIndexes($tableName);
2064 if ($keyRows !== FALSE) {
2065 while (list($k, $theKey) = each($keyRows)) {
2066 $theKey['Table'] = $tableName;
2067 $theKey['Non_unique'] = (int) !$theKey['unique'];
2068 $theKey['Key_name'] = str_replace($tableName.'_','',$k);
2069
2070 // the following are probably not needed anyway...
2071 $theKey['Collation'] = '';
2072 $theKey['Cardinality'] = '';
2073 $theKey['Sub_part'] = '';
2074 $theKey['Packed'] = '';
2075 $theKey['Null'] = '';
2076 $theKey['Index_type'] = '';
2077 $theKey['Comment'] = '';
2078
2079 // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2080 $keycols = $theKey['columns'];
2081 while (list($c, $theCol) = each($keycols)) {
2082 $theKey['Seq_in_index'] = $c+1;
2083 $theKey['Column_name'] = $theCol;
2084 $output[] = $theKey;
2085 }
2086 }
2087 }
2088 $priKeyRow = $this->handlerInstance[$this->lastHandlerKey]->MetaPrimaryKeys($tableName);
2089 $theKey = array();
2090 $theKey['Table'] = $tableName;
2091 $theKey['Non_unique'] = 0;
2092 $theKey['Key_name'] = 'PRIMARY';
2093
2094 // the following are probably not needed anyway...
2095 $theKey['Collation'] = '';
2096 $theKey['Cardinality'] = '';
2097 $theKey['Sub_part'] = '';
2098 $theKey['Packed'] = '';
2099 $theKey['Null'] = '';
2100 $theKey['Index_type'] = '';
2101 $theKey['Comment'] = '';
2102
2103 // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2104 if ($priKeyRow !== FALSE) {
2105 while (list($c, $theCol) = each($priKeyRow)) {
2106 $theKey['Seq_in_index'] = $c+1;
2107 $theKey['Column_name'] = $theCol;
2108 $output[] = $theKey;
2109 }
2110 }
2111 break;
2112 case 'userdefined':
2113 $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_keys($tableName);
2114 break;
2115 }
2116
2117 // mapping should be done:
2118 if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2119 $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2120
2121 $newOutput = array();
2122 foreach ($output as $kN => $kInfo) {
2123 // Table:
2124 $kInfo['Table'] = $ORIG_tableName;
2125
2126 // Column
2127 if (isset($revFields[$kInfo['Column_name']])) {
2128 $kInfo['Column_name'] = $revFields[$kInfo['Column_name']];
2129 }
2130
2131 // Write it back:
2132 $newOutput[$kN] = $kInfo;
2133 }
2134 $output = $newOutput;
2135 }
2136
2137 return $output;
2138 }
2139
2140 /**
2141 * mysql() wrapper function, used by the Install Tool.
2142 *
2143 * @return array
2144 */
2145 public function admin_get_charsets() {
2146 return array();
2147 }
2148
2149 /**
2150 * mysql() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
2151 *
2152 * @param string Query to execute
2153 * @return pointer Result pointer
2154 */
2155 public function admin_query($query) {
2156 $parsedQuery = $this->SQLparser->parseSQL($query);
2157 $ORIG_table = $parsedQuery['TABLE'];
2158
2159 if (is_array($parsedQuery)) {
2160
2161 // Process query based on type:
2162 switch ($parsedQuery['type']) {
2163 case 'CREATETABLE':
2164 case 'ALTERTABLE':
2165 case 'DROPTABLE':
2166 if (file_exists(PATH_typo3conf.'temp_fieldInfo.php')) unlink(PATH_typo3conf.'temp_fieldInfo.php');
2167 $this->map_genericQueryParsed($parsedQuery);
2168 break;
2169 case 'INSERT':
2170 $this->map_genericQueryParsed($parsedQuery);
2171 break;
2172 case 'CREATEDATABASE':
2173 die('Creating a database with DBAL is not supported. Did you really read the manual?');
2174 break;
2175 default:
2176 die('ERROR: Invalid Query type ('.$parsedQuery['type'].') for ->admin_query() function!: "'.htmlspecialchars($query).'"');
2177 break;
2178 }
2179
2180 // Setting query array (for other applications to access if needed)
2181 $this->lastParsedAndMappedQueryArray = $parsedQuery;
2182
2183 // Execute query (based on handler derived from the TABLE name which we actually know for once!)
2184 $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_table);
2185 switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2186 case 'native':
2187 // Compiling query:
2188 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2189
2190 if ($this->lastParsedAndMappedQueryArray['type']=='INSERT') {
2191 return mysql_query($compiledQuery, $this->link);
2192 }
2193 return mysql_query($compiledQuery[0], $this->link);
2194 break;
2195 case 'adodb':
2196 // Compiling query:
2197 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2198 if ($this->lastParsedAndMappedQueryArray['type']=='INSERT') {
2199 return $this->exec_INSERTquery($this->lastParsedAndMappedQueryArray['TABLE'],$compiledQuery);
2200 }
2201 return $this->handlerInstance[$this->lastHandlerKey]->DataDictionary->ExecuteSQLArray($compiledQuery);
2202 break;
2203 case 'userdefined':
2204 // Compiling query:
2205 $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2206
2207 return $this->handlerInstance[$this->lastHandlerKey]->admin_query($compiledQuery);
2208 break;
2209 }
2210 } else die('ERROR: Query could not be parsed: "'.htmlspecialchars($parsedQuery).'". Query: "'.htmlspecialchars($query).'"');
2211 }
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222 /************************************
2223 *
2224 * Handler management
2225 *
2226 **************************************/
2227
2228 /**
2229 * Return the handler key pointing to an appropriate database handler as found in $this->handlerCfg array
2230 * 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!)
2231 *
2232 * @param string Table list, eg. "pages" or "pages, tt_content" or "pages AS A, tt_content AS B"
2233 * @return string Handler key (see $this->handlerCfg array) for table
2234 */
2235 public function handler_getFromTableList($tableList) {
2236
2237 $key = $tableList;
2238
2239 if (!isset($this->cache_handlerKeyFromTableList[$key])) {
2240
2241 // Get tables separated:
2242 $_tableList = $tableList;
2243 $tableArray = $this->SQLparser->parseFromTables($_tableList);
2244
2245 // If success, traverse the tables:
2246 if (is_array($tableArray) && count($tableArray)) {
2247 $outputHandlerKey = '';
2248
2249 foreach ($tableArray as $vArray) {
2250 // Find handler key, select "_DEFAULT" if none is specifically configured:
2251 $handlerKey = $this->table2handlerKeys[$vArray['table']] ? $this->table2handlerKeys[$vArray['table']] : '_DEFAULT';
2252
2253 // In case of separate handler keys for joined tables:
2254 if ($outputHandlerKey && $handlerKey != $outputHandlerKey) {
2255 die('DBAL fatal error: Tables in this list "'.$tableList.'" didn\'t use the same DB handler!');
2256 }
2257
2258 $outputHandlerKey = $handlerKey;
2259 }
2260
2261 // Check initialized state; if handler is NOT initialized (connected) then we will connect it!
2262 if (!isset($this->handlerInstance[$outputHandlerKey])) {
2263 $this->handler_init($outputHandlerKey);
2264 }
2265
2266 // Return handler key:
2267 $this->cache_handlerKeyFromTableList[$key] = $outputHandlerKey;
2268 } else {
2269 die('DBAL fatal error: No handler found in handler_getFromTableList() for: "'.$tableList.'" ('.$tableArray.')');
2270 }
2271 }
2272
2273 return $this->cache_handlerKeyFromTableList[$key];
2274 }
2275
2276 /**
2277 * Initialize handler (connecting to database)
2278 *
2279 * @param string Handler key
2280 * @return boolean If connection went well, return TRUE
2281 * @see handler_getFromTableList()
2282 */
2283 public function handler_init($handlerKey) {
2284
2285 // Find handler configuration:
2286 $cfgArray = $this->handlerCfg[$handlerKey];
2287 $handlerType = (string)$cfgArray['type'];
2288 $output = FALSE;
2289
2290 if (is_array($cfgArray)) {
2291 switch ($handlerType) {
2292 case 'native':
2293 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
2294 $link = mysql_connect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], TRUE);
2295 } else {
2296 $link = mysql_pconnect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password']);
2297 }
2298
2299 // Set handler instance:
2300 $this->handlerInstance[$handlerKey] = array('handlerType' => 'native', 'link' => $link);
2301
2302 // If link succeeded:
2303 if ($link) {
2304 // For default, set ->link (see t3lib_DB)
2305 if ($handlerKey == '_DEFAULT') {
2306 $this->link = $link;
2307 }
2308
2309 // Select database as well:
2310 if (mysql_select_db($cfgArray['config']['database'], $link)) {
2311 $output = TRUE;
2312 }
2313 $setDBinit = t3lib_div::trimExplode(chr(10), $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'], 1);
2314 foreach ($setDBinit as $v) {
2315 if (mysql_query($v, $this->link) === FALSE) {
2316 t3lib_div::sysLog('Could not initialize DB connection with query "'.$v.'".','Core',3);
2317 }
2318 }
2319 } else {
2320 t3lib_div::sysLog('Could not connect to MySQL server '.$cfgArray['config']['host'].' with user '.$cfgArray['config']['username'].'.','Core',4);
2321 }
2322 break;
2323 case 'adodb':
2324 $output = TRUE;
2325 require_once(t3lib_extMgm::extPath('adodb').'adodb/adodb.inc.php');
2326 if (!defined('ADODB_FORCE_NULLS')) define('ADODB_FORCE_NULLS', 1);
2327 $GLOBALS['ADODB_FORCE_TYPE'] = ADODB_FORCE_VALUE;
2328 $GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_BOTH;
2329
2330 $this->handlerInstance[$handlerKey] = &ADONewConnection($cfgArray['config']['driver']);
2331
2332 // Set driver-specific options
2333 if (isset($cfgArray['config']['driverOptions'])) {
2334 foreach ($cfgArray['config']['driverOptions'] as $optionName => $optionValue) {
2335 $optionSetterName = 'set' . ucfirst($optionName);
2336 if (method_exists($this->handlerInstance[$handlerKey], $optionSetterName)) {
2337 $this->handlerInstance[$handlerKey]->$optionSetterName($optionValue);
2338 } else {
2339 $this->handlerInstance[$handlerKey]->$optionName = $optionValue;
2340 }
2341 }
2342 }
2343
2344 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
2345 $this->handlerInstance[$handlerKey]->Connect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''),$cfgArray['config']['username'],$cfgArray['config']['password'],$cfgArray['config']['database']);
2346 } else {
2347 $this->handlerInstance[$handlerKey]->PConnect($cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : ''),$cfgArray['config']['username'],$cfgArray['config']['password'],$cfgArray['config']['database']);
2348 }
2349 if (!$this->handlerInstance[$handlerKey]->isConnected()) {
2350 $dsn = $cfgArray['config']['driver'].'://'.$cfgArray['config']['username'].
2351 (strlen($cfgArray['config']['password']) ? ':XXXX@' : '').
2352 $cfgArray['config']['host'].(isset($cfgArray['config']['port']) ? ':'.$cfgArray['config']['port'] : '').'/'.$cfgArray['config']['database'].
2353 ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'] ? '' : '?persistent=1');
2354 t3lib_div::sysLog('Could not connect to DB server using ADOdb on '.$cfgArray['config']['host'].' with user '.$cfgArray['config']['username'].'.','Core',4);
2355 error_log('DBAL error: Connection to '.$dsn.' failed. Maybe PHP doesn\'t support the database?');
2356 $output = FALSE;
2357 } else {
2358 $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
2359 $this->handlerInstance[$handlerKey]->last_insert_id = 0;
2360 if (isset($cfgArray['config']['sequenceStart'])) {
2361 $this->handlerInstance[$handlerKey]->sequenceStart = $cfgArray['config']['sequenceStart'];
2362 } else {
2363 $this->handlerInstance[$handlerKey]->sequenceStart = 1;
2364 }
2365 }
2366 break;
2367 case 'userdefined':
2368 // Find class file:
2369 $fileName = t3lib_div::getFileAbsFileName($cfgArray['config']['classFile']);
2370 if (@is_file($fileName)) {
2371 require_once($fileName);
2372 } else die('DBAL error: "'.$fileName.'" was not a file to include.');
2373
2374 // Initialize:
2375 $this->handlerInstance[$handlerKey] = t3lib_div::makeInstance($cfgArray['config']['class']);
2376 $this->handlerInstance[$handlerKey]->init($cfgArray,$this);
2377
2378 if (is_object($this->handlerInstance[$handlerKey])) {
2379 $output = TRUE;
2380 }
2381 break;
2382 default:
2383 die('ERROR: Invalid handler type: "'.$cfgArray['type'].'"');
2384 break;
2385 }
2386
2387 return $output;
2388 } else die('ERROR: No handler for key "'.$handlerKey.'"');
2389 }
2390
2391
2392 /**
2393 * Checks whether the DBAL is currently inside an operation running on the "native" DB handler (i.e. MySQL)
2394 *
2395 * @return boolean True if running on "native" DB handler (i.e. MySQL)
2396 */
2397 public function runningNative() {
2398 return ((string)$this->handlerCfg[$this->lastHandlerKey]['type']==='native');
2399 }
2400
2401
2402 /**
2403 * Checks whether the ADOdb handler is running with a driver that contains the argument
2404 *
2405 * @param string $driver Driver name, matched with strstr().
2406 * @return boolean True if running with the given driver
2407 */
2408 public function runningADOdbDriver($driver) {
2409 return strstr($this->handlerCfg[$this->lastHandlerKey]['config']['driver'], $driver);
2410 }
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421 /************************************
2422 *
2423 * Table/Field mapping
2424 *
2425 **************************************/
2426
2427 /**
2428 * Checks if mapping is needed for a table(list)
2429 *
2430 * @param string List of tables in query
2431 * @param boolean If TRUE, it will check only if FIELDs are configured and ignore the mapped table name if any.
2432 * @return mixed Returns an array of table names (parsed version of input table) if mapping is needed, otherwise just FALSE.
2433 */
2434 protected function map_needMapping($tableList, $fieldMappingOnly = FALSE) {
2435 $key = $tableList.'|'.$fieldMappingOnly;
2436 if (!isset($this->cache_mappingFromTableList[$key])) {
2437 $this->cache_mappingFromTableList[$key] = FALSE; // Default:
2438
2439 $tables = $this->SQLparser->parseFromTables($tableList);
2440 if (is_array($tables)) {
2441 foreach ($tables as $tableCfg) {
2442 if ($fieldMappingOnly) {
2443 if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
2444 $this->cache_mappingFromTableList[$key] = $tables;
2445 } elseif (is_array($tableCfg['JOIN'])) {
2446 foreach ($tableCfg['JOIN'] as $join) {
2447 if (is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
2448 $this->cache_mappingFromTableList[$key] = $tables;
2449 break;
2450 }
2451 }
2452 }
2453 } else {
2454 if (is_array($this->mapping[$tableCfg['table']])) {
2455 $this->cache_mappingFromTableList[$key] = $tables;
2456 } elseif (is_array($tableCfg['JOIN'])) {
2457 foreach ($tableCfg['JOIN'] as $join) {
2458 if (is_array($this->mapping[$join['withTable']])) {
2459 $this->cache_mappingFromTableList[$key] = $tables;
2460 break;
2461 }
2462 }
2463 }
2464 }
2465 }
2466 }
2467 }
2468
2469 return $this->cache_mappingFromTableList[$key];
2470 }
2471
2472 /**
2473 * Takes an associated array with field => value pairs and remaps the field names if configured for this table in $this->mapping array.
2474 * Be careful not to map a field name to another existing fields name (although you can use this to swap fieldnames of course...:-)
2475 * Observe mapping problems with join-results (more than one table): Joined queries should always prefix the table name to avoid problems with this.
2476 * Observe that alias fields are not mapped of course (should not be a problem though)
2477 *
2478 * @param array Input array, associative keys
2479 * @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!
2480 * @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!)
2481 * @return array Output array, with mapped associative keys.
2482 */
2483 protected function map_assocArray($input, $tables, $rev = FALSE) {
2484 // Traverse tables from query (hopefully only one table):
2485 foreach ($tables as $tableCfg) {
2486 if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
2487
2488 // Get the map (reversed if needed):
2489 if ($rev) {
2490 $theMap = array_flip($this->mapping[$tableCfg['table']]['mapFieldNames']);
2491 } else {
2492 $theMap = $this->mapping[$tableCfg['table']]['mapFieldNames'];
2493 }
2494
2495 // Traverse selected record, map fieldnames:
2496 $output = array();
2497 foreach ($input as $fN => $value) {
2498
2499 // Set the field name, change it if found in mapping array:
2500 if ($theMap[$fN]) {
2501 $newKey = $theMap[$fN];
2502 } else {
2503 $newKey = $fN;
2504 }
2505
2506 // Set value to fieldname:
2507 $output[$newKey] = $value;
2508 }
2509
2510 // When done, override the $input array with the result:
2511 $input = $output;
2512 }
2513 }
2514
2515 // Return input array (which might have been altered in the mean time)
2516 return $input;
2517 }
2518
2519 /**
2520 * Remaps table/field names in a SELECT query's parts
2521 * Notice: All arguments are passed by reference!
2522 *
2523 * @param string List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
2524 * @param string Table(s) from which to select. This is what comes right after "FROM ...". Require value.
2525 * @param string Where clause. This is what comes right after "WHERE ...". Can be blank.
2526 * @param string Group by field(s)
2527 * @param string Order by field(s)
2528 * @return void
2529 * @see exec_SELECTquery()
2530 */
2531 protected function map_remapSELECTQueryParts(&$select_fields, &$from_table, &$where_clause, &$groupBy, &$orderBy) {
2532 // Tables:
2533 $tables = $this->SQLparser->parseFromTables($from_table);
2534 $defaultTable = $tables[0]['table'];
2535 foreach ($tables as $k => $v) {
2536 if ($this->mapping[$v['table']]['mapTableName']) {
2537 $tables[$k]['table'] = $this->mapping[$v['table']]['mapTableName'];
2538 }
2539 // Mapping JOINS
2540 if (is_array($v['JOIN'])) {
2541 foreach($v['JOIN'] as $joinCnt => $join) {
2542 // Mapping withTable of the JOIN
2543 if ($this->mapping[$join['withTable']]['mapTableName']) {
2544 $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$join['withTable']]['mapTableName'];
2545 }
2546 $onPartsArray = array();
2547 // Mapping ON parts of the JOIN
2548 if (is_array($join['ON'])) {
2549 foreach ($join['ON'] as $onParts) {
2550 if (isset($this->mapping[$onParts['table']]['mapFieldNames'][$onParts['field']])) {
2551 $onParts['field'] = $this->mapping[$onParts['table']]['mapFieldNames'][$onParts['field']];
2552 }
2553 if (isset($this->mapping[$onParts['table']]['mapTableName'])) {
2554 $onParts['table'] = $this->mapping[$onParts['table']]['mapTableName'];
2555 }
2556 $onPartsArray[] = $onParts;
2557 }
2558 $tables[$k]['JOIN'][$joinCnt]['ON'] = $onPartsArray;
2559 }
2560 }
2561 }
2562 }
2563 $from_table = $this->SQLparser->compileFromTables($tables);
2564
2565 // Where clause:
2566 $whereParts = $this->SQLparser->parseWhereClause($where_clause);
2567 $this->map_sqlParts($whereParts,$defaultTable);
2568 $where_clause = $this->SQLparser->compileWhereClause($whereParts, FALSE);
2569
2570 // Select fields:
2571 $expFields = $this->SQLparser->parseFieldList($select_fields);
2572 $this->map_sqlParts($expFields,$defaultTable);
2573 $select_fields = $this->SQLparser->compileFieldList($expFields);
2574
2575 // Group By fields
2576 $expFields = $this->SQLparser->parseFieldList($groupBy);
2577 $this->map_sqlParts($expFields,$defaultTable);
2578 $groupBy = $this->SQLparser->compileFieldList($expFields);
2579
2580 // Order By fields
2581 $expFields = $this->SQLparser->parseFieldList($orderBy);
2582 $this->map_sqlParts($expFields,$defaultTable);
2583 $orderBy = $this->SQLparser->compileFieldList($expFields);
2584 }
2585
2586 /**
2587 * Generic mapping of table/field names arrays (as parsed by tx_dbal_sqlengine)
2588 *
2589 * @param array Array with parsed SQL parts; Takes both fields, tables, where-parts, group and order-by. Passed by reference.
2590 * @param string Default table name to assume if no table is found in $sqlPartArray
2591 * @return void
2592 * @access private
2593 * @see map_remapSELECTQueryParts()
2594 */
2595 protected function map_sqlParts(&$sqlPartArray, $defaultTable) {
2596 // Traverse sql Part array:
2597 if (is_array($sqlPartArray)) {
2598 foreach ($sqlPartArray as $k => $v) {
2599
2600 // Look for sublevel (WHERE parts only)
2601 if (is_array($sqlPartArray[$k]['sub'])) {
2602 $this->map_sqlParts($sqlPartArray[$k]['sub'], $defaultTable); // Call recursively!
2603 } elseif (isset($sqlPartArray[$k]['func'])) {
2604 $subqueryDefaultTable = $sqlPartArray[$k]['func']['subquery']['FROM'][0]['table'];
2605 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['SELECT'], $subqueryDefaultTable);
2606 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['FROM'], $subqueryDefaultTable);
2607 $this->map_sqlParts($sqlPartArray[$k]['func']['subquery']['WHERE'], $subqueryDefaultTable);
2608 } else {
2609 // For the field, look for table mapping (generic):
2610 $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable;
2611
2612 // Mapping field name, if set:
2613 if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']]) {
2614 $sqlPartArray[$k]['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']];
2615 }
2616
2617 // Mapping field name in SQL-functions like MIN(), MAX() or SUM()
2618 if ($this->mapping[$t]['mapFieldNames']) {
2619 $fieldArray = explode('.', $sqlPartArray[$k]['func_content']);
2620 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
2621 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2622 $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2623 }
2624 elseif (count($fieldArray) == 2) {
2625 // Map the external table
2626 $table = $fieldArray[0];
2627 if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
2628 $table = $this->mapping[$fieldArray[0]]['mapTableName'];
2629 }
2630 // Map the field itself
2631 $field = $fieldArray[1];
2632 if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
2633 $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
2634 }
2635 $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field;
2636 $sqlPartArray[$k]['func_content'] = $table . '.' . $field;
2637 }
2638
2639 // Mapping flow-control statements
2640 if (isset($sqlPartArray[$k]['flow-control'])) {
2641 if ($sqlPartArray[$k]['flow-control']['type'] === 'CASE' && isset($sqlPartArray[$k]['flow-control']['case_field'])) {
2642 $fieldArray = explode('.', $sqlPartArray[$k]['flow-control']['case_field']);
2643 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
2644 $sqlPartArray[$k]['flow-control']['case_field'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2645 }
2646 elseif (count($fieldArray) == 2) {
2647 // Map the external table
2648 $table = $fieldArray[0];
2649 if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
2650 $table = $this->mapping[$fieldArray[0]]['mapTableName'];
2651 }
2652 // Map the field itself
2653 $field = $fieldArray[1];
2654 if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
2655 $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
2656 }
2657 $sqlPartArray[$k]['flow-control']['case_field'] = $table . '.' . $field;
2658 }
2659 }
2660 }
2661 }
2662
2663 // Do we have a function (e.g., CONCAT)
2664 if (isset($v['value']['operator'])) {
2665 foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) {
2666 if (isset($this->mapping[$fieldDef['table']]['mapTableName'])) {
2667 $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$fieldDef['table']]['mapTableName'];
2668 }
2669 if (is_array($this->mapping[$fieldDef['table']]['mapFieldNames']) && isset($this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']])) {
2670 $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$fieldDef['table']]['mapFieldNames'][$fieldDef['field']];
2671 }
2672 }
2673 }
2674
2675 // Do we have a subquery (WHERE parts only)?
2676 if (isset($sqlPartArray[$k]['subquery'])) {
2677 $subqueryDefaultTable = $sqlPartArray[$k]['subquery']['FROM'][0]['table'];
2678 $this->map_sqlParts($sqlPartArray[$k]['subquery']['SELECT'], $subqueryDefaultTable);
2679 $this->map_sqlParts($sqlPartArray[$k]['subquery']['FROM'], $subqueryDefaultTable);
2680 $this->map_sqlParts($sqlPartArray[$k]['subquery']['WHERE'], $subqueryDefaultTable);
2681 }
2682
2683 // do we have a field name in the value?
2684 // this is a very simplistic check, beware
2685 if (!is_numeric($sqlPartArray[$k]['value'][0]) && !isset($sqlPartArray[$k]['value'][1])) {
2686 $fieldArray = explode('.', $sqlPartArray[$k]['value'][0]);
2687 if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
2688 $sqlPartArray[$k]['value'][0] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
2689 } elseif (count($fieldArray) == 2) {
2690 // Map the external table
2691 $table = $fieldArray[0];
2692 if (isset($this->mapping[$fieldArray[0]]['mapTableName'])) {
2693 $table = $this->mapping[$fieldArray[0]]['mapTableName'];
2694 }
2695 // Map the field itself
2696 $field = $fieldArray[1];
2697 if (is_array($this->mapping[$fieldArray[0]]['mapFieldNames']) && isset($this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]])) {
2698 $field = $this->mapping[$fieldArray[0]]['mapFieldNames'][$fieldArray[1]];
2699 }
2700 $sqlPartArray[$k]['value'][0] = $table . '.' . $field;
2701 }
2702 }
2703
2704 // Map table?
2705 if ($sqlPartArray[$k]['table'] && $this->mapping[$sqlPartArray[$k]['table']]['mapTableName']) {
2706 $sqlPartArray[$k]['table'] = $this->mapping[$sqlPartArray[$k]['table']]['mapTableName'];
2707 }
2708 }
2709 }
2710 }
2711 }
2712
2713 /**
2714 * Will do table/field mapping on a general tx_dbal_sqlengine-compliant SQL query
2715 * (May still not support all query types...)
2716 *
2717 * @param array Parsed QUERY as from tx_dbal_sqlengine::parseSQL(). NOTICE: Passed by reference!
2718 * @return void
2719 * @see tx_dbal_sqlengine::parseSQL()
2720 */
2721 protected function map_genericQueryParsed(&$parsedQuery) {
2722
2723 // Getting table - same for all:
2724 $table = $parsedQuery['TABLE'];
2725 if ($table) {
2726 // Do field mapping if needed:
2727 if ($tableArray = $this->map_needMapping($table)) {
2728
2729 // Table name:
2730 if ($this->mapping[$table]['mapTableName']) {
2731 $parsedQuery['TABLE'] = $this->mapping[$table]['mapTableName'];
2732 }
2733
2734 // Based on type, do additional changes:
2735 switch ($parsedQuery['type']) {
2736 case 'ALTERTABLE':
2737
2738 // Changing field name:
2739 $newFieldName = $this->mapping[$table]['mapFieldNames'][$parsedQuery['FIELD']];
2740 if ($newFieldName) {
2741 if ($parsedQuery['FIELD'] == $parsedQuery['newField']) {
2742 $parsedQuery['FIELD'] = $parsedQuery['newField'] = $newFieldName;
2743 } else $parsedQuery['FIELD'] = $newFieldName;
2744 }
2745
2746 // Changing key field names:
2747 if (is_array($parsedQuery['fields'])) {
2748 $this->map_fieldNamesInArray($table,$parsedQuery['fields']);
2749 }
2750 break;
2751 case 'CREATETABLE':
2752 // Remapping fields:
2753 if (is_array($parsedQuery['FIELDS'])) {
2754 $newFieldsArray = array();
2755 foreach ($parsedQuery['FIELDS'] as $fN => $fInfo) {
2756 if ($this->mapping[$table]['mapFieldNames'][$fN]) {
2757 $fN = $this->mapping[$table]['mapFieldNames'][$fN];
2758 }
2759 $newFieldsArray[$fN] = $fInfo;
2760 }
2761 $parsedQuery['FIELDS'] = $newFieldsArray;
2762 }
2763
2764 // Remapping keys:
2765 if (is_array($parsedQuery['KEYS'])) {
2766 foreach ($parsedQuery['KEYS'] as $kN => $kInfo) {
2767 $this->map_fieldNamesInArray($table,$parsedQuery['KEYS'][$kN]);
2768 }
2769 }
2770 break;
2771
2772 /// ... and here support for all other query types should be!
2773
2774 }
2775 }
2776 } else die('ERROR, mapping: No table found in parsed Query array...');
2777 }
2778
2779 /**
2780 * Re-mapping field names in array
2781 *
2782 * @param string (TYPO3) Table name for fields.
2783 * @param array Array of fieldnames to remap. Notice: Passed by reference!
2784 * @return void
2785 */
2786 protected function map_fieldNamesInArray($table,&$fieldArray) {
2787 if (is_array($this->mapping[$table]['mapFieldNames'])) {
2788 foreach ($fieldArray as $k => $v) {
2789 if ($this->mapping[$table]['mapFieldNames'][$v]) {
2790 $fieldArray[$k] = $this->mapping[$table]['mapFieldNames'][$v];
2791 }
2792 }
2793 }
2794 }
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812 /**************************************
2813 *
2814 * Debugging
2815 *
2816 **************************************/
2817
2818 /**
2819 * Debug handler for query execution
2820 *
2821 * @param string Function name from which this function is called.
2822 * @param string Execution time in ms of the query
2823 * @param array In-data of various kinds.
2824 * @return void
2825 * @access private
2826 */
2827 public function debugHandler($function,$execTime,$inData) {
2828 // we don't want to log our own log/debug SQL
2829 $script = substr(PATH_thisScript,strlen(PATH_site));
2830
2831 if (substr($script,-strlen('dbal/mod1/index.php'))!='dbal/mod1/index.php' && !strstr($inData['args'][0], 'tx_dbal_debuglog')) {
2832 $data = array();
2833 $errorFlag = 0;
2834 $joinTable = '';
2835
2836 if ($this->sql_error()) {