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