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