TYPO3 CMS  TYPO3_7-6
DatabaseConnection.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
23 {
27  protected $printErrors = false;
28 
33  public $debug = false;
34 
39  public $conf = [];
40 
45  public $mapping = [];
46 
51  protected $table2handlerKeys = [];
52 
57  public $handlerCfg = [
58  '_DEFAULT' => [
59  'type' => 'native',
60  'config' => [
61  'username' => '',
62  // Set by default (overridden)
63  'password' => '',
64  // Set by default (overridden)
65  'host' => '',
66  // Set by default (overridden)
67  'database' => '',
68  // Set by default (overridden)
69  'driver' => '',
70  // ONLY "adodb" type; eg. "mysql"
71  'sequenceStart' => 1,
72  // ONLY "adodb", first number in sequences/serials/...
73  'useNameQuote' => 0,
74  // ONLY "adodb", whether to use NameQuote() method from ADOdb to quote names
75  'quoteClob' => false
76  ]
77  ]
78  ];
79 
89  public $handlerInstance = [];
90 
95  public $lastHandlerKey = '';
96 
101  protected $lastQuery = '';
102 
108 
113 
118 
123 
128  public $cache_autoIncFields = [];
129 
133  public $cache_fieldType = [];
134 
138  public $cache_primaryKeys = [];
139 
143  protected $cacheIdentifier = 'DatabaseConnection_fieldInfo';
144 
150  public $SQLparser;
151 
155  protected $installerSql = null;
156 
162  protected $queryCache;
163 
171  protected $mysqlDataTypeMapping = [
172  MYSQLI_TYPE_TINY => 'tinyint',
173  MYSQLI_TYPE_CHAR => 'tinyint',
174  MYSQLI_TYPE_SHORT => 'smallint',
175  MYSQLI_TYPE_LONG => 'int',
176  MYSQLI_TYPE_FLOAT => 'float',
177  MYSQLI_TYPE_DOUBLE => 'double',
178  MYSQLI_TYPE_TIMESTAMP => 'timestamp',
179  MYSQLI_TYPE_LONGLONG => 'bigint',
180  MYSQLI_TYPE_INT24 => 'mediumint',
181  MYSQLI_TYPE_DATE => 'date',
182  MYSQLI_TYPE_NEWDATE => 'date',
183  MYSQLI_TYPE_TIME => 'time',
184  MYSQLI_TYPE_DATETIME => 'datetime',
185  MYSQLI_TYPE_YEAR => 'year',
186  MYSQLI_TYPE_BIT => 'bit',
187  MYSQLI_TYPE_INTERVAL => 'interval',
188  MYSQLI_TYPE_ENUM => 'enum',
189  MYSQLI_TYPE_SET => 'set',
190  MYSQLI_TYPE_TINY_BLOB => 'blob',
191  MYSQLI_TYPE_MEDIUM_BLOB => 'blob',
192  MYSQLI_TYPE_LONG_BLOB => 'blob',
193  MYSQLI_TYPE_BLOB => 'blob',
194  MYSQLI_TYPE_VAR_STRING => 'varchar',
195  MYSQLI_TYPE_STRING => 'char',
196  MYSQLI_TYPE_DECIMAL => 'decimal',
197  MYSQLI_TYPE_NEWDECIMAL => 'decimal',
198  MYSQLI_TYPE_GEOMETRY => 'geometry'
199  ];
200 
204  protected $dbmsSpecifics;
205 
210  public function __construct()
211  {
212  // Set SQL parser object for internal use:
213  $this->SQLparser = GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlParser::class, $this);
214  $this->installerSql = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService::class);
215  $this->queryCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('dbal');
216  // Set internal variables with configuration:
217  $this->conf = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal'];
218  }
219 
225  public function initialize()
226  {
227  // Set outside configuration:
228  if (isset($this->conf['mapping'])) {
229  $this->mapping = $this->conf['mapping'];
230  }
231  if (isset($this->conf['table2handlerKeys'])) {
232  $this->table2handlerKeys = $this->conf['table2handlerKeys'];
233  }
234 
235  $specificsClassName = Specifics\NullSpecifics::class;
236  if (isset($this->conf['handlerCfg'])) {
237  $this->handlerCfg = $this->conf['handlerCfg'];
238 
239  if (isset($this->handlerCfg['_DEFAULT']['config']['driver'])) {
240  // load DBMS specifics
241  $driver = $this->handlerCfg['_DEFAULT']['config']['driver'];
242  $className = 'TYPO3\\CMS\\Dbal\\Database\\Specifics\\' . ucfirst(strtolower($driver)) . 'Specifics';
243  if (class_exists($className)) {
244  if (!is_subclass_of($className, Specifics\AbstractSpecifics::class)) {
245  throw new \InvalidArgumentException($className . ' must inherit from ' . Specifics\AbstractSpecifics::class, 1416919866);
246  }
247  $specificsClassName = $className;
248  }
249  }
250  }
251  $this->dbmsSpecifics = GeneralUtility::makeInstance($specificsClassName);
252  $this->cacheFieldInfo();
253  // Debugging settings:
254  $this->printErrors = !empty($this->conf['debugOptions']['printErrors']);
255  $this->debug = !empty($this->conf['debugOptions']['enabled']);
256  }
257 
263  public function getSpecifics()
264  {
265  return $this->dbmsSpecifics;
266  }
267 
271  protected function getFieldInfoCache()
272  {
273  return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_phpcode');
274  }
275 
281  public function clearCachedFieldInfo()
282  {
283  $this->getFieldInfoCache()->flushByTag('DatabaseConnection');
284  }
285 
291  public function cacheFieldInfo()
292  {
293  $phpCodeCache = $this->getFieldInfoCache();
294  // try to fetch cache
295  // cache is flushed when admin_query() is called
296  if ($phpCodeCache->has($this->cacheIdentifier)) {
297  $fieldInformation = $phpCodeCache->requireOnce($this->cacheIdentifier);
298  $this->cache_autoIncFields = $fieldInformation['incFields'];
299  $this->cache_fieldType = $fieldInformation['fieldTypes'];
300  $this->cache_primaryKeys = $fieldInformation['primaryKeys'];
301  } else {
302  $this->analyzeCachingTables();
303  $this->analyzeExtensionTables();
304  $completeFieldInformation = $this->getCompleteFieldInformation();
305  $phpCodeCache->set($this->cacheIdentifier, $this->getCacheableString($completeFieldInformation), ['DatabaseConnection']);
306  }
307  }
308 
316  protected function analyzeCachingTables()
317  {
318  $schemaService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\DatabaseSchemaService::class);
319  $this->parseAndAnalyzeSql($schemaService->getCachingFrameworkRequiredDatabaseSchema());
320  }
321 
328  protected function analyzeExtensionTables()
329  {
330  if (isset($GLOBALS['TYPO3_LOADED_EXT']) && (is_array($GLOBALS['TYPO3_LOADED_EXT']) || $GLOBALS['TYPO3_LOADED_EXT'] instanceof \ArrayAccess)) {
331  foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extensionConfiguration) {
332  $isArray = (is_array($extensionConfiguration) || $extensionConfiguration instanceof \ArrayAccess);
333  if (!$isArray || ($isArray && !isset($extensionConfiguration['ext_tables.sql']))) {
334  continue;
335  }
336  $extensionsSql = file_get_contents($extensionConfiguration['ext_tables.sql']);
337  $this->parseAndAnalyzeSql($extensionsSql);
338  }
339  }
340  }
341 
348  protected function parseAndAnalyzeSql($sql)
349  {
350  $parsedSql = $this->installerSql->getFieldDefinitions_fileContent($sql);
351  $this->analyzeFields($parsedSql);
352  }
353 
360  protected function getCompleteFieldInformation()
361  {
362  return ['incFields' => $this->cache_autoIncFields, 'fieldTypes' => $this->cache_fieldType, 'primaryKeys' => $this->cache_primaryKeys];
363  }
364 
372  protected function getCacheableString(array $fieldInformation)
373  {
374  $cacheString = 'return ';
375  $cacheString .= var_export($fieldInformation, true);
376  $cacheString .= ';';
377  return $cacheString;
378  }
379 
386  protected function analyzeFields($parsedExtSQL)
387  {
388  foreach ($parsedExtSQL as $table => $tdef) {
389  // check if table is mapped
390  if (isset($this->mapping[$table])) {
391  $table = $this->mapping[$table]['mapTableName'];
392  }
393  if (is_array($tdef['fields'])) {
394  foreach ($tdef['fields'] as $field => $fdefString) {
395  $fdef = $this->SQLparser->parseFieldDef($fdefString);
396  $fieldType = isset($fdef['fieldType']) ? $fdef['fieldType'] : '';
397  $this->cache_fieldType[$table][$field]['type'] = $fieldType;
398  $this->cache_fieldType[$table][$field]['metaType'] = $this->dbmsSpecifics->getMetaFieldType($fieldType);
399  $this->cache_fieldType[$table][$field]['notnull'] = isset($fdef['featureIndex']['NOTNULL']) && !$this->SQLparser->checkEmptyDefaultValue($fdef['featureIndex']) ? 1 : 0;
400  if (isset($fdef['featureIndex']['DEFAULT'])) {
401  $default = $fdef['featureIndex']['DEFAULT']['value'][0];
402  if (isset($fdef['featureIndex']['DEFAULT']['value'][1])) {
403  $default = $fdef['featureIndex']['DEFAULT']['value'][1] . $default . $fdef['featureIndex']['DEFAULT']['value'][1];
404  }
405  $this->cache_fieldType[$table][$field]['default'] = $default;
406  }
407  if (isset($fdef['featureIndex']['AUTO_INCREMENT'])) {
408  $this->cache_autoIncFields[$table] = $field;
409  }
410  if (isset($tdef['keys']['PRIMARY'])) {
411  $this->cache_primaryKeys[$table] = substr($tdef['keys']['PRIMARY'], 13, -1);
412  }
413  }
414  }
415  }
416  }
417 
426  protected function mapCachedFieldInfo(array $fieldInfo)
427  {
428  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'])) {
429  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'] as $mappedTable => $mappedConf) {
430  if (array_key_exists($mappedTable, $fieldInfo['incFields'])) {
431  $mappedTableAlias = $mappedConf['mapTableName'];
432  if (isset($mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]])) {
433  $fieldInfo['incFields'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]];
434  } else {
435  $fieldInfo['incFields'][$mappedTableAlias] = $fieldInfo['incFields'][$mappedTable];
436  }
437  }
438  if (array_key_exists($mappedTable, $fieldInfo['fieldTypes'])) {
439  $tempMappedFieldConf = [];
440  foreach ($fieldInfo['fieldTypes'][$mappedTable] as $field => $fieldConf) {
441  $tempMappedFieldConf[$mappedConf['mapFieldNames'][$field]] = $fieldConf;
442  }
443  $fieldInfo['fieldTypes'][$mappedConf['mapTableName']] = $tempMappedFieldConf;
444  }
445  if (array_key_exists($mappedTable, $fieldInfo['primaryKeys'])) {
446  $mappedTableAlias = $mappedConf['mapTableName'];
447  if (isset($mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]])) {
448  $fieldInfo['primaryKeys'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]];
449  } else {
450  $fieldInfo['primaryKeys'][$mappedTableAlias] = $fieldInfo['primaryKeys'][$mappedTable];
451  }
452  }
453  }
454  }
455  return $fieldInfo;
456  }
457 
458  /************************************
459  *
460  * Query Building (Overriding parent methods)
461  * These functions are extending counterparts in the parent class.
462  *
463  **************************************/
464  /*
465  * From the ADOdb documentation, this is what we do (_Execute for SELECT, _query for the other actions)Execute()
466  * is the default way to run queries. You can use the low-level functions _Execute() and _query() to reduce query overhead.
467  * Both these functions share the same parameters as Execute().If you do not have any bind parameters or your database
468  * supports binding (without emulation), then you can call _Execute() directly.
469  * Calling this function bypasses bind emulation. Debugging is still supported in _Execute().If you do not require
470  * debugging facilities nor emulated binding, and do not require a recordset to be returned, then you can call _query.
471  * This is great for inserts, updates and deletes. Calling this function bypasses emulated binding, debugging,
472  * and recordset handling. Either the resultid, TRUE or FALSE are returned by _query().
473  */
474 
485  public function exec_INSERTquery($table, $fields_values, $no_quote_fields = false)
486  {
487  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
488  // Do field mapping if needed:
489  $ORIG_tableName = $table;
490  if ($tableArray = $this->map_needMapping($table)) {
491  // Field mapping of array:
492  $fields_values = $this->map_assocArray($fields_values, $tableArray);
493  // Table name:
494  if ($this->mapping[$table]['mapTableName']) {
495  $table = $this->mapping[$table]['mapTableName'];
496  }
497  }
498  // Select API:
499  $this->lastHandlerKey = $this->handler_getFromTableList($table);
500  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
501  $sqlResult = null;
502  switch ($hType) {
503  case 'native':
504  $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
505  if (is_string($this->lastQuery)) {
506  $sqlResult = $this->query($this->lastQuery);
507  } else {
508  $sqlResult = $this->query($this->lastQuery[0]);
509  $new_id = $this->sql_insert_id();
510  $where = $this->cache_autoIncFields[$table] . '=' . $new_id;
511  foreach ($this->lastQuery[1] as $field => $content) {
512  $stmt = 'UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table, true) . ' WHERE ' . $this->quoteWhereClause($where);
513  $this->query($stmt);
514  }
515  }
516  break;
517  case 'adodb':
518  // auto generate ID for auto_increment fields if not present (static import needs this!)
519  // should we check the table name here (static_*)?
520  if (isset($this->cache_autoIncFields[$table])) {
521  if (!isset($fields_values[$this->cache_autoIncFields[$table]]) && !$this->handlerInstance[$this->lastHandlerKey]->hasInsertID) {
522  // The table does not support auto-incremented fields, fall back to
523  // using a sequence table to simulate the auto-increment
524  $fields_values[$this->cache_autoIncFields[$table]] = $this->handlerInstance[$this->lastHandlerKey]->GenID($table . '_' . $this->cache_autoIncFields[$table], $this->handlerInstance[$this->lastHandlerKey]->sequenceStart);
525  }
526  }
527  $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
528  if (is_string($this->lastQuery)) {
529  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, false);
530  $this->updateLastInsertId($table, $fields_values);
531  } else {
532  $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
533  if ((string)$this->lastQuery[0] !== '') {
534  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], false);
535  $new_id = $this->updateLastInsertId($table, $fields_values);
536  }
537  if (is_array($this->lastQuery[1])) {
538  foreach ($this->lastQuery[1] as $field => $content) {
539  if (empty($content)) {
540  continue;
541  }
542  if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
543  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id));
544  } elseif (isset($this->cache_primaryKeys[$table])) {
545  $where = '';
546  $pks = explode(',', $this->cache_primaryKeys[$table]);
547  foreach ($pks as $pk) {
548  if (isset($fields_values[$pk])) {
549  $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND ';
550  }
551  }
552  $where = $this->quoteWhereClause($where . '1=1');
553  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $where);
554  } else {
555  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(false);
556  // Should never ever happen
557  throw new \RuntimeException('Could not update BLOB >>>> no WHERE clause found!', 1321860519);
558  }
559  }
560  }
561  if (is_array($this->lastQuery[2])) {
562  foreach ($this->lastQuery[2] as $field => $content) {
563  if (empty($content)) {
564  continue;
565  }
566  if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
567  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id));
568  } elseif (isset($this->cache_primaryKeys[$table])) {
569  $where = '';
570  $pks = explode(',', $this->cache_primaryKeys[$table]);
571  foreach ($pks as $pk) {
572  if (isset($fields_values[$pk])) {
573  $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND ';
574  }
575  }
576  $where = $this->quoteWhereClause($where . '1=1');
577  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $where);
578  } else {
579  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(false);
580  // Should never ever happen
581  throw new \RuntimeException('Could not update CLOB >>>> no WHERE clause found!', 1310027337);
582  }
583  }
584  }
585  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
586  }
587  break;
588  case 'userdefined':
589  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_INSERTquery($table, $fields_values, $no_quote_fields);
590  break;
591  }
592  if ($this->printErrors && $this->sql_error()) {
593  debug([$this->lastQuery, $this->sql_error()]);
594  }
595  if ($this->debug) {
596  $this->debugHandler('exec_INSERTquery', GeneralUtility::milliseconds() - $pt, [
597  'handlerType' => $hType,
598  'args' => [$table, $fields_values],
599  'ORIG_tablename' => $ORIG_tableName
600  ]);
601  }
602  foreach ($this->postProcessHookObjects as $hookObject) {
603  $hookObject->exec_INSERTquery_postProcessAction($table, $fields_values, $no_quote_fields, $this);
604  }
605  // Return output:
606  return $sqlResult;
607  }
608 
618  public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = false)
619  {
620  $res = null;
621  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
622  $this->lastHandlerKey = $this->handler_getFromTableList($table);
623  $res = $this->query(parent::INSERTmultipleRows($table, $fields, $rows, $no_quote_fields));
624  } else {
625  foreach ($rows as $row) {
626  $fields_values = [];
627  foreach ($fields as $key => $value) {
628  $fields_values[$value] = $row[$key];
629  }
630  $res = $this->exec_INSERTquery($table, $fields_values, $no_quote_fields);
631  }
632  }
633  foreach ($this->postProcessHookObjects as $hookObject) {
634  $hookObject->exec_INSERTmultipleRows_postProcessAction($table, $fields, $rows, $no_quote_fields, $this);
635  }
636  return $res;
637  }
638 
649  public function exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields = false)
650  {
651  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
652  // Do table/field mapping:
653  $ORIG_tableName = $table;
654  if ($tableArray = $this->map_needMapping($table)) {
655  // Field mapping of array:
656  $fields_values = $this->map_assocArray($fields_values, $tableArray);
657  // Where clause table and field mapping:
658  $whereParts = $this->SQLparser->parseWhereClause($where);
659  $this->map_sqlParts($whereParts, $tableArray[0]['table']);
660  $where = $this->SQLparser->compileWhereClause($whereParts, false);
661  // Table name:
662  if ($this->mapping[$table]['mapTableName']) {
663  $table = $this->mapping[$table]['mapTableName'];
664  }
665  }
666  // Select API
667  $this->lastHandlerKey = $this->handler_getFromTableList($table);
668  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
669  $sqlResult = null;
670  switch ($hType) {
671  case 'native':
672  $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields);
673  if (is_string($this->lastQuery)) {
674  $sqlResult = $this->query($this->lastQuery);
675  } else {
676  $sqlResult = $this->query($this->lastQuery[0]);
677  foreach ($this->lastQuery[1] as $field => $content) {
678  $stmt = 'UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table, true) . ' WHERE ' . $this->quoteWhereClause($where);
679  $this->query($stmt);
680  }
681  }
682  break;
683  case 'adodb':
684  $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields);
685  if (is_string($this->lastQuery)) {
686  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, false);
687  } else {
688  $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
689  if ((string)$this->lastQuery[0] !== '') {
690  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], false);
691  }
692  if (is_array($this->lastQuery[1])) {
693  foreach ($this->lastQuery[1] as $field => $content) {
694  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where));
695  }
696  }
697  if (is_array($this->lastQuery[2])) {
698  foreach ($this->lastQuery[2] as $field => $content) {
699  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where));
700  }
701  }
702  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
703  }
704  break;
705  case 'userdefined':
706  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields);
707  break;
708  }
709  if ($this->printErrors && $this->sql_error()) {
710  debug([$this->lastQuery, $this->sql_error()]);
711  }
712  if ($this->debug) {
713  $this->debugHandler('exec_UPDATEquery', GeneralUtility::milliseconds() - $pt, [
714  'handlerType' => $hType,
715  'args' => [$table, $where, $fields_values],
716  'ORIG_from_table' => $ORIG_tableName
717  ]);
718  }
719  foreach ($this->postProcessHookObjects as $hookObject) {
720  $hookObject->exec_UPDATEquery_postProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
721  }
722  // Return result:
723  return $sqlResult;
724  }
725 
733  public function exec_DELETEquery($table, $where)
734  {
735  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
736  // Do table/field mapping:
737  $ORIG_tableName = $table;
738  if ($tableArray = $this->map_needMapping($table)) {
739  // Where clause:
740  $whereParts = $this->SQLparser->parseWhereClause($where);
741  $this->map_sqlParts($whereParts, $tableArray[0]['table']);
742  $where = $this->SQLparser->compileWhereClause($whereParts, false);
743  // Table name:
744  if ($this->mapping[$table]['mapTableName']) {
745  $table = $this->mapping[$table]['mapTableName'];
746  }
747  }
748  // Select API
749  $this->lastHandlerKey = $this->handler_getFromTableList($table);
750  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
751  $sqlResult = null;
752  switch ($hType) {
753  case 'native':
754  $this->lastQuery = $this->DELETEquery($table, $where);
755  $sqlResult = $this->query($this->lastQuery);
756  break;
757  case 'adodb':
758  $this->lastQuery = $this->DELETEquery($table, $where);
759  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, false);
760  break;
761  case 'userdefined':
762  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_DELETEquery($table, $where);
763  break;
764  }
765  if ($this->printErrors && $this->sql_error()) {
766  debug([$this->lastQuery, $this->sql_error()]);
767  }
768  if ($this->debug) {
769  $this->debugHandler('exec_DELETEquery', GeneralUtility::milliseconds() - $pt, [
770  'handlerType' => $hType,
771  'args' => [$table, $where],
772  'ORIG_from_table' => $ORIG_tableName
773  ]);
774  }
775  foreach ($this->postProcessHookObjects as $hookObject) {
776  $hookObject->exec_DELETEquery_postProcessAction($table, $where, $this);
777  }
778  // Return result:
779  return $sqlResult;
780  }
781 
795  public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '')
796  {
797  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
798  // Map table / field names if needed:
799  $ORIG_tableName = $from_table;
800  // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
801  $parsedFromTable = [];
802  $remappedParameters = [];
803  if ($tableArray = $this->map_needMapping($ORIG_tableName, false, $parsedFromTable)) {
804  $from = $parsedFromTable ? $parsedFromTable : $from_table;
805  $remappedParameters = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
806  }
807  // Get handler key and select API:
808  if (!empty($remappedParameters)) {
809  $mappedQueryParts = $this->compileSelectParameters($remappedParameters);
810  $fromTable = $mappedQueryParts[1];
811  } else {
812  $fromTable = $from_table;
813  }
814  $this->lastHandlerKey = $this->handler_getFromTableList($fromTable);
815  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
816  $sqlResult = null;
817  switch ($hType) {
818  case 'native':
819  if (!empty($remappedParameters)) {
820  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
821  }
822  $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
823  $sqlResult = $this->query($this->lastQuery);
824  $this->resourceIdToTableNameMap[serialize($sqlResult)] = $ORIG_tableName;
825  break;
826  case 'adodb':
827  if ($limit != '') {
828  $splitLimit = GeneralUtility::intExplode(',', $limit);
829  // Splitting the limit values:
830  if ($splitLimit[1]) {
831  // If there are two parameters, do mapping differently than otherwise:
832  $numrows = $splitLimit[1];
833  $offset = $splitLimit[0];
834  } else {
835  $numrows = $splitLimit[0];
836  $offset = 0;
837  }
838  if (!empty($remappedParameters)) {
839  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTqueryFromArray($remappedParameters), $numrows, $offset);
840  } else {
841  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy), $numrows, $offset);
842  }
843  $this->lastQuery = $sqlResult->sql;
844  } else {
845  if (!empty($remappedParameters)) {
846  $this->lastQuery = $this->SELECTqueryFromArray($remappedParameters);
847  } else {
848  $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
849  }
850  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery);
851  }
852  if (!is_object($sqlResult)) {
853  throw new \RuntimeException('ADOdb could not run this query: ' . $this->lastQuery, 1421053336);
854  }
855  $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
856  // Setting handler type in result object (for later recognition!)
857  $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
858  break;
859  case 'userdefined':
860  if (!empty($remappedParameters)) {
861  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
862  }
863  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
864  if (is_object($sqlResult)) {
865  $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
866  // Setting handler type in result object (for later recognition!)
867  $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
868  }
869  break;
870  }
871  if ($this->printErrors && $this->sql_error()) {
872  debug([$this->lastQuery, $this->sql_error()]);
873  }
874  if ($this->debug) {
875  $data = [
876  'handlerType' => $hType,
877  'args' => [$from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit],
878  'ORIG_from_table' => $ORIG_tableName
879  ];
880  if ($this->conf['debugOptions']['numberRows']) {
881  $data['numberRows'] = $this->sql_num_rows($sqlResult);
882  }
883  $this->debugHandler('exec_SELECTquery', GeneralUtility::milliseconds() - $pt, $data);
884  }
885  // Return handler.
886  return $sqlResult;
887  }
888 
895  public function exec_TRUNCATEquery($table)
896  {
897  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
898  // Do table/field mapping:
899  $ORIG_tableName = $table;
900  if ($tableArray = $this->map_needMapping($table)) {
901  // Table name:
902  if ($this->mapping[$table]['mapTableName']) {
903  $table = $this->mapping[$table]['mapTableName'];
904  }
905  }
906  // Select API
907  $this->lastHandlerKey = $this->handler_getFromTableList($table);
908  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
909  $sqlResult = null;
910  switch ($hType) {
911  case 'native':
912  $this->lastQuery = $this->TRUNCATEquery($table);
913  $sqlResult = $this->query($this->lastQuery);
914  break;
915  case 'adodb':
916  $this->lastQuery = $this->TRUNCATEquery($table);
917  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, false);
918  break;
919  case 'userdefined':
920  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_TRUNCATEquery($table);
921  break;
922  }
923  if ($this->printErrors && $this->sql_error()) {
924  debug([$this->lastQuery, $this->sql_error()]);
925  }
926  if ($this->debug) {
927  $this->debugHandler('exec_TRUNCATEquery', GeneralUtility::milliseconds() - $pt, [
928  'handlerType' => $hType,
929  'args' => [$table],
930  'ORIG_from_table' => $ORIG_tableName
931  ]);
932  }
933  foreach ($this->postProcessHookObjects as $hookObject) {
934  $hookObject->exec_TRUNCATEquery_postProcessAction($table, $this);
935  }
936  // Return result:
937  return $sqlResult;
938  }
939 
948  protected function exec_query(array $queryParts)
949  {
950  switch ($queryParts['type']) {
951  case 'SELECT':
952  $selectFields = $this->SQLparser->compileFieldList($queryParts['SELECT']);
953  $fromTables = $this->SQLparser->compileFromTables($queryParts['FROM']);
954  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
955  $groupBy = isset($queryParts['GROUPBY']) ? $this->SQLparser->compileFieldList($queryParts['GROUPBY']) : '';
956  $orderBy = isset($queryParts['ORDERBY']) ? $this->SQLparser->compileFieldList($queryParts['ORDERBY']) : '';
957  $limit = isset($queryParts['LIMIT']) ? $queryParts['LIMIT'] : '';
958  return $this->exec_SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy, $limit);
959  case 'UPDATE':
960  $table = $queryParts['TABLE'];
961  $fields = [];
962  foreach ($queryParts['FIELDS'] as $fN => $fV) {
963  $fields[$fN] = $fV[0];
964  }
965  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
966  return $this->exec_UPDATEquery($table, $whereClause, $fields);
967  case 'INSERT':
968  $table = $queryParts['TABLE'];
969  $values = [];
970  if (isset($queryParts['VALUES_ONLY']) && is_array($queryParts['VALUES_ONLY'])) {
971  $fields = $GLOBALS['TYPO3_DB']->cache_fieldType[$table];
972  $fc = 0;
973  foreach ($fields as $fn => $fd) {
974  $values[$fn] = $queryParts['VALUES_ONLY'][$fc++][0];
975  }
976  } else {
977  foreach ($queryParts['FIELDS'] as $fN => $fV) {
978  $values[$fN] = $fV[0];
979  }
980  }
981  return $this->exec_INSERTquery($table, $values);
982  case 'DELETE':
983  $table = $queryParts['TABLE'];
984  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
985  return $this->exec_DELETEquery($table, $whereClause);
986  case 'TRUNCATETABLE':
987  $table = $queryParts['TABLE'];
988  return $this->exec_TRUNCATEquery($table);
989  default:
990  return null;
991  }
992  }
993 
1001  protected function query($query)
1002  {
1003  if (!$this->isConnected()) {
1004  $this->connectDB();
1005  }
1006  return $this->handlerInstance[$this->lastHandlerKey]['link']->query($query);
1007  }
1008 
1009  /**************************************
1010  *
1011  * Query building
1012  *
1013  **************************************/
1022  public function INSERTquery($table, $fields_values, $no_quote_fields = false)
1023  {
1024  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
1025  if (!is_array($fields_values) || empty($fields_values)) {
1026  return '';
1027  }
1028  foreach ($this->preProcessHookObjects as $hookObject) {
1029  $hookObject->INSERTquery_preProcessAction($table, $fields_values, $no_quote_fields, $this);
1030  }
1031  if (is_string($no_quote_fields)) {
1032  $no_quote_fields = explode(',', $no_quote_fields);
1033  } elseif (!is_array($no_quote_fields)) {
1034  $no_quote_fields = [];
1035  }
1036  $blobFields = $clobFields = [];
1037  $nArr = [];
1038  $handlerKey = $this->handler_getFromTableList($table);
1039  $quoteClob = isset($this->handlerCfg[$handlerKey]['config']['quoteClob']) ? $this->handlerCfg[$handlerKey]['config']['quoteClob'] : false;
1040  foreach ($fields_values as $k => $v) {
1041  if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
1042  // we skip the field in the regular INSERT statement, it is only in blobfields
1043  $blobFields[$this->quoteFieldNames($k)] = $v;
1044  } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
1045  // we skip the field in the regular INSERT statement, it is only in clobfields
1046  $clobFields[$this->quoteFieldNames($k)] = $quoteClob ? $this->quoteStr($v, $table) : $v;
1047  } else {
1048  // Add slashes old-school:
1049  // cast numerical values
1050  $mt = $this->sql_field_metatype($table, $k);
1051  if ($mt[0] == 'I') {
1052  $v = (int)$v;
1053  } elseif ($mt[0] == 'F') {
1054  $v = (double) $v;
1055  }
1056  $nArr[$this->quoteFieldNames($k)] = !in_array($k, $no_quote_fields) ? $this->fullQuoteStr($v, $table, true) : $v;
1057  }
1058  }
1059  if (!empty($blobFields) || !empty($clobFields)) {
1060  $query = [];
1061  if (!empty($nArr)) {
1062  $query[0] = 'INSERT INTO ' . $this->quoteFromTables($table) . '
1063  (
1064  ' . implode(',
1065  ', array_keys($nArr)) . '
1066  ) VALUES (
1067  ' . implode(',
1068  ', $nArr) . '
1069  )';
1070  }
1071  if (!empty($blobFields)) {
1072  $query[1] = $blobFields;
1073  }
1074  if (!empty($clobFields)) {
1075  $query[2] = $clobFields;
1076  }
1077  if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
1078  $this->debug_lastBuiltQuery = $query[0];
1079  }
1080  } else {
1081  $query = 'INSERT INTO ' . $this->quoteFromTables($table) . '
1082  (
1083  ' . implode(',
1084  ', array_keys($nArr)) . '
1085  ) VALUES (
1086  ' . implode(',
1087  ', $nArr) . '
1088  )';
1089  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1090  $this->debug_lastBuiltQuery = $query;
1091  }
1092  }
1093  return $query;
1094  }
1095 
1105  public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = false)
1106  {
1107  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
1108  return parent::INSERTmultipleRows($table, $fields, $rows, $no_quote_fields);
1109  }
1110  $result = [];
1111  foreach ($rows as $row) {
1112  $fields_values = [];
1113  foreach ($fields as $key => $value) {
1114  $fields_values[$value] = $row[$key];
1115  }
1116  $rowQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
1117  if (is_array($rowQuery)) {
1118  $result[] = $rowQuery;
1119  } else {
1120  $result[][0] = $rowQuery;
1121  }
1122  }
1123  return $result;
1124  }
1125 
1137  public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = false)
1138  {
1139  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
1140  if (is_string($where)) {
1141  foreach ($this->preProcessHookObjects as $hookObject) {
1142  $hookObject->UPDATEquery_preProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
1143  }
1144  $blobFields = $clobFields = [];
1145  $nArr = [];
1146  if (is_array($fields_values) && !empty($fields_values)) {
1147  if (is_string($no_quote_fields)) {
1148  $no_quote_fields = explode(',', $no_quote_fields);
1149  } elseif (!is_array($no_quote_fields)) {
1150  $no_quote_fields = [];
1151  }
1152  $handlerKey = $this->handler_getFromTableList($table);
1153  $quoteClob = isset($this->handlerCfg[$handlerKey]['config']['quoteClob']) ? $this->handlerCfg[$handlerKey]['config']['quoteClob'] : false;
1154  foreach ($fields_values as $k => $v) {
1155  if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
1156  // we skip the field in the regular UPDATE statement, it is only in blobfields
1157  $blobFields[$this->quoteFieldNames($k)] = $v;
1158  } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
1159  // we skip the field in the regular UPDATE statement, it is only in clobfields
1160  $clobFields[$this->quoteFieldNames($k)] = $quoteClob ? $this->quoteStr($v, $table) : $v;
1161  } else {
1162  // Add slashes old-school:
1163  // cast numeric values
1164  $mt = $this->sql_field_metatype($table, $k);
1165  if ($mt[0] == 'I') {
1166  $v = (int)$v;
1167  } elseif ($mt[0] == 'F') {
1168  $v = (double) $v;
1169  }
1170  $nArr[] = $this->quoteFieldNames($k) . '=' . (!in_array($k, $no_quote_fields) ? $this->fullQuoteStr($v, $table, true) : $v);
1171  }
1172  }
1173  }
1174  if (!empty($blobFields) || !empty($clobFields)) {
1175  $query = [];
1176  if (!empty($nArr)) {
1177  $query[0] = 'UPDATE ' . $this->quoteFromTables($table) . '
1178  SET
1179  ' . implode(',
1180  ', $nArr) . ($where !== '' ? '
1181  WHERE
1182  ' . $this->quoteWhereClause($where) : '');
1183  }
1184  if (!empty($blobFields)) {
1185  $query[1] = $blobFields;
1186  }
1187  if (!empty($clobFields)) {
1188  $query[2] = $clobFields;
1189  }
1190  if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
1191  $this->debug_lastBuiltQuery = $query[0];
1192  }
1193  } else {
1194  $query = 'UPDATE ' . $this->quoteFromTables($table) . '
1195  SET
1196  ' . implode(',
1197  ', $nArr) . ($where !== '' ? '
1198  WHERE
1199  ' . $this->quoteWhereClause($where) : '');
1200  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1201  $this->debug_lastBuiltQuery = $query;
1202  }
1203  }
1204  return $query;
1205  } else {
1206  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 1270853887);
1207  }
1208  }
1209 
1218  public function DELETEquery($table, $where)
1219  {
1220  if (is_string($where)) {
1221  foreach ($this->preProcessHookObjects as $hookObject) {
1222  $hookObject->DELETEquery_preProcessAction($table, $where, $this);
1223  }
1224  $table = $this->quoteFromTables($table);
1225  $where = $this->quoteWhereClause($where);
1226  $query = 'DELETE FROM ' . $table . ($where !== '' ? ' WHERE ' . $where : '');
1227  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1228  $this->debug_lastBuiltQuery = $query;
1229  }
1230  return $query;
1231  } else {
1232  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !', 1310027383);
1233  }
1234  }
1235 
1247  public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '')
1248  {
1249  $this->lastHandlerKey = $this->handler_getFromTableList($from_table);
1250  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
1251  if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) {
1252  // Possibly rewrite the LIMIT to be PostgreSQL-compatible
1253  $splitLimit = GeneralUtility::intExplode(',', $limit);
1254  // Splitting the limit values:
1255  if ($splitLimit[1]) {
1256  // If there are two parameters, do mapping differently than otherwise:
1257  $numrows = $splitLimit[1];
1258  $offset = $splitLimit[0];
1259  $limit = $numrows . ' OFFSET ' . $offset;
1260  }
1261  }
1262  $select_fields = $this->quoteFieldNames($select_fields);
1263  $from_table = $this->quoteFromTables($from_table);
1264  $where_clause = $this->quoteWhereClause($where_clause);
1265  $groupBy = $this->quoteGroupBy($groupBy);
1266  $orderBy = $this->quoteOrderBy($orderBy);
1267  $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1268  // Call parent method to build actual query
1269  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1270  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1271  $this->debug_lastBuiltQuery = $query;
1272  }
1273  return $query;
1274  }
1275 
1282  protected function SELECTqueryFromArray(array $params)
1283  {
1284  // $select_fields
1285  $params[0] = $this->_quoteFieldNames($params[0]);
1286  // $from_table
1287  $params[1] = $this->_quoteFromTables($params[1]);
1288  // $where_clause
1289  if (!empty($params[2])) {
1290  $params[2] = $this->_quoteWhereClause($params[2]);
1291  }
1292  // $group_by
1293  if (!empty($params[3])) {
1294  $params[3] = $this->_quoteGroupBy($params[3]);
1295  }
1296  // $order_by
1297  if (!empty($params[4])) {
1298  $params[4] = $this->_quoteOrderBy($params[4]);
1299  }
1300  // Compile the SELECT parameters
1301  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($params);
1302  $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1303  // Call parent method to build actual query
1304  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1305  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1306  $this->debug_lastBuiltQuery = $query;
1307  }
1308  return $query;
1309  }
1310 
1318  protected function compileSelectParameters(array $params)
1319  {
1320  $select_fields = $this->SQLparser->compileFieldList($params[0]);
1321  $from_table = $this->SQLparser->compileFromTables($params[1]);
1322  $where_clause = !empty($params[2]) ? $this->SQLparser->compileWhereClause($params[2]) : '';
1323  $groupBy = !empty($params[3]) ? $this->SQLparser->compileFieldList($params[3]) : '';
1324  $orderBy = !empty($params[4]) ? $this->SQLparser->compileFieldList($params[4]) : '';
1325  return [$select_fields, $from_table, $where_clause, $groupBy, $orderBy];
1326  }
1327 
1334  public function TRUNCATEquery($table)
1335  {
1336  foreach ($this->preProcessHookObjects as $hookObject) {
1337  $hookObject->TRUNCATEquery_preProcessAction($table, $this);
1338  }
1339  $table = $this->quoteFromTables($table);
1340  // Build actual query
1341  $query = 'TRUNCATE TABLE ' . $table;
1342  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1343  $this->debug_lastBuiltQuery = $query;
1344  }
1345  return $query;
1346  }
1347 
1348  /**************************************
1349  *
1350  * Prepared Query Support
1351  *
1352  **************************************/
1365  public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = [])
1366  {
1367  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
1368  $precompiledParts = [];
1369  if ($this->queryCache) {
1370  $cacheKey = 'prepare_SELECTquery-' . \TYPO3\CMS\Dbal\QueryCache::getCacheKey([
1371  'selectFields' => $select_fields,
1372  'fromTable' => $from_table,
1373  'whereClause' => $where_clause,
1374  'groupBy' => $groupBy,
1375  'orderBy' => $orderBy,
1376  'limit' => $limit
1377  ]);
1378  if ($this->queryCache->has($cacheKey)) {
1379  $precompiledParts = $this->queryCache->get($cacheKey);
1380  if ($this->debug) {
1381  $data = [
1382  'args' => [$from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters],
1383  'precompiledParts' => $precompiledParts
1384  ];
1385  $this->debugHandler('prepare_SELECTquery (cache hit)', GeneralUtility::milliseconds() - $pt, $data);
1386  }
1387  }
1388  }
1389  $ORIG_tableName = '';
1390  if (empty($precompiledParts)) {
1391  // Map table / field names if needed:
1392  $ORIG_tableName = $from_table;
1393  // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
1394  $parsedFromTable = [];
1395  $queryComponents = [];
1396  if ($tableArray = $this->map_needMapping($ORIG_tableName, false, $parsedFromTable)) {
1397  $from = $parsedFromTable ? $parsedFromTable : $from_table;
1398  $components = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
1399  $queryComponents['SELECT'] = $components[0];
1400  $queryComponents['FROM'] = $components[1];
1401  $queryComponents['WHERE'] = $components[2];
1402  $queryComponents['GROUPBY'] = $components[3];
1403  $queryComponents['ORDERBY'] = $components[4];
1404  $queryComponents['parameters'] = $components[5];
1405  } else {
1406  $queryComponents = $this->getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1407  }
1408  $queryComponents['ORIG_tableName'] = $ORIG_tableName;
1409  if (!$this->runningNative()) {
1410  // Quotes all fields
1411  $queryComponents['SELECT'] = $this->_quoteFieldNames($queryComponents['SELECT']);
1412  $queryComponents['FROM'] = $this->_quoteFromTables($queryComponents['FROM']);
1413  $queryComponents['WHERE'] = $this->_quoteWhereClause($queryComponents['WHERE']);
1414  $queryComponents['GROUPBY'] = $this->_quoteGroupBy($queryComponents['GROUPBY']);
1415  $queryComponents['ORDERBY'] = $this->_quoteOrderBy($queryComponents['ORDERBY']);
1416  }
1417  $precompiledParts = $this->precompileSELECTquery($queryComponents);
1418  if ($this->queryCache) {
1419  try {
1420  $this->queryCache->set($cacheKey, $precompiledParts);
1421  } catch (\TYPO3\CMS\Core\Cache\Exception $e) {
1422  if ($this->debug) {
1423  GeneralUtility::devLog($e->getMessage(), 'dbal', 1);
1424  }
1425  }
1426  }
1427  }
1428  $preparedStatement = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\PreparedStatement::class, '', $from_table, $precompiledParts);
1429  /* @var $preparedStatement \TYPO3\CMS\Core\Database\PreparedStatement */
1430  // Bind values to parameters
1431  foreach ($input_parameters as $key => $value) {
1432  $preparedStatement->bindValue($key, $value, \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE);
1433  }
1434  if ($this->debug) {
1435  $data = [
1436  'args' => [$from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters],
1437  'ORIG_from_table' => $ORIG_tableName
1438  ];
1439  $this->debugHandler('prepare_SELECTquery', GeneralUtility::milliseconds() - $pt, $data);
1440  }
1441  // Return prepared statement
1442  return $preparedStatement;
1443  }
1444 
1457  protected function getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit)
1458  {
1459  $queryComponents = [
1460  'SELECT' => '',
1461  'FROM' => '',
1462  'WHERE' => '',
1463  'GROUPBY' => '',
1464  'ORDERBY' => '',
1465  'LIMIT' => '',
1466  'parameters' => []
1467  ];
1468  $this->lastHandlerKey = $this->handler_getFromTableList($from_table);
1469  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
1470  if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) {
1471  // Possibly rewrite the LIMIT to be PostgreSQL-compatible
1472  $splitLimit = GeneralUtility::intExplode(',', $limit);
1473  // Splitting the limit values:
1474  if ($splitLimit[1]) {
1475  // If there are two parameters, do mapping differently than otherwise:
1476  $numrows = $splitLimit[1];
1477  $offset = $splitLimit[0];
1478  $limit = $numrows . ' OFFSET ' . $offset;
1479  }
1480  }
1481  $queryComponents['LIMIT'] = $limit;
1482  $queryComponents['SELECT'] = $this->SQLparser->parseFieldList($select_fields);
1483  if ($this->SQLparser->parse_error) {
1484  throw new \InvalidArgumentException($this->SQLparser->parse_error, 1310027408);
1485  }
1486  $queryComponents['FROM'] = $this->SQLparser->parseFromTables($from_table);
1487  $queryComponents['WHERE'] = $this->SQLparser->parseWhereClause($where_clause, '', $queryComponents['parameters']);
1488  if (!is_array($queryComponents['WHERE'])) {
1489  throw new \InvalidArgumentException('Could not parse where clause', 1310027427);
1490  }
1491  $queryComponents['GROUPBY'] = $this->SQLparser->parseFieldList($groupBy);
1492  $queryComponents['ORDERBY'] = $this->SQLparser->parseFieldList($orderBy);
1493  // Return the query components
1494  return $queryComponents;
1495  }
1496 
1503  protected function precompileSELECTquery(array $components)
1504  {
1505  $parameterWrap = '__' . dechex(time()) . '__';
1506  foreach ($components['parameters'] as $key => $params) {
1507  if ($key === '?') {
1508  foreach ($params as $index => $param) {
1509  $components['parameters'][$key][$index][0] = $parameterWrap . $param[0] . $parameterWrap;
1510  }
1511  } else {
1512  $components['parameters'][$key][0] = $parameterWrap . $params[0] . $parameterWrap;
1513  }
1514  }
1515  $select_fields = $this->SQLparser->compileFieldList($components['SELECT']);
1516  $from_table = $this->SQLparser->compileFromTables($components['FROM']);
1517  $where_clause = $this->SQLparser->compileWhereClause($components['WHERE']);
1518  $groupBy = $this->SQLparser->compileFieldList($components['GROUPBY']);
1519  $orderBy = $this->SQLparser->compileFieldList($components['ORDERBY']);
1520  $limit = $components['LIMIT'];
1521  $precompiledParts = [];
1522  $this->lastHandlerKey = $this->handler_getFromTableList($components['ORIG_tableName']);
1523  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
1524  $precompiledParts['handler'] = $hType;
1525  $precompiledParts['ORIG_tableName'] = $components['ORIG_tableName'];
1526  switch ($hType) {
1527  case 'native':
1528  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1529  $precompiledParts['queryParts'] = explode($parameterWrap, $query);
1530  break;
1531  case 'adodb':
1532  $this->dbmsSpecifics->transformQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1533  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1534  $precompiledParts['queryParts'] = explode($parameterWrap, $query);
1535  $precompiledParts['LIMIT'] = $limit;
1536  break;
1537  case 'userdefined':
1538  $precompiledParts['queryParts'] = [
1539  'SELECT' => $select_fields,
1540  'FROM' => $from_table,
1541  'WHERE' => $where_clause,
1542  'GROUPBY' => $groupBy,
1543  'ORDERBY' => $orderBy,
1544  'LIMIT' => $limit
1545  ];
1546  break;
1547  }
1548  return $precompiledParts;
1549  }
1550 
1560  public function prepare_PREPAREDquery($query, array $queryComponents)
1561  {
1562  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
1563  // Get handler key and select API:
1564  $preparedStatement = null;
1565  switch ($queryComponents['handler']) {
1566  case 'native':
1567  $this->lastQuery = $query;
1568  $preparedStatement = parent::prepare_PREPAREDquery($this->lastQuery, $queryComponents);
1569  $this->resourceIdToTableNameMap[serialize($preparedStatement)] = $queryComponents['ORIG_tableName'];
1570  break;
1571  case 'adodb':
1573  $preparedStatement = GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\AdodbPreparedStatement::class, $query, $queryComponents, $this);
1574  if (!$preparedStatement->prepare()) {
1575  $preparedStatement = false;
1576  }
1577  break;
1578  case 'userdefined':
1579  throw new \RuntimeException('prepare_PREPAREDquery is not implemented for userdefined handlers', 1394620167);
1580  /*
1581  $queryParts = $queryComponents['queryParts'];
1582  $preparedStatement = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
1583  if (is_object($preparedStatement)) {
1584  $preparedStatement->TYPO3_DBAL_handlerType = 'userdefined';
1585  // Setting handler type in result object (for later recognition!)
1586  $preparedStatement->TYPO3_DBAL_tableList = $queryComponents['ORIG_tableName'];
1587  }
1588  break;
1589  */
1590  }
1591  if ($this->printErrors && $this->sql_error()) {
1592  debug([$this->lastQuery, $this->sql_error()]);
1593  }
1594  if ($this->debug) {
1595  $data = [
1596  'handlerType' => $queryComponents['handler'],
1597  'args' => $queryComponents,
1598  'ORIG_from_table' => $queryComponents['ORIG_tableName']
1599  ];
1600  $this->debugHandler('prepare_PREPAREDquery', GeneralUtility::milliseconds() - $pt, $data);
1601  }
1602  // Return result handler.
1603  return $preparedStatement;
1604  }
1605 
1606  /**************************************
1607  *
1608  * Functions for quoting table/field names
1609  *
1610  **************************************/
1617  protected function quoteSELECTsubquery(array $components)
1618  {
1619  $components['SELECT'] = $this->_quoteFieldNames($components['SELECT']);
1620  $components['FROM'] = $this->_quoteFromTables($components['FROM']);
1621  $components['WHERE'] = $this->_quoteWhereClause($components['WHERE']);
1622  return $components;
1623  }
1624 
1632  public function quoteFieldNames($select_fields)
1633  {
1634  if ($select_fields == '') {
1635  return '';
1636  }
1637  if ($this->runningNative()) {
1638  return $select_fields;
1639  }
1640  $select_fields = $this->SQLparser->parseFieldList($select_fields);
1641  if ($this->SQLparser->parse_error) {
1642  throw new \InvalidArgumentException($this->SQLparser->parse_error, 1310027490);
1643  }
1644  $select_fields = $this->_quoteFieldNames($select_fields);
1645  return $this->SQLparser->compileFieldList($select_fields);
1646  }
1647 
1655  protected function _quoteFieldNames(array $select_fields)
1656  {
1657  foreach ($select_fields as $k => $v) {
1658  if ($select_fields[$k]['field'] != '' && $select_fields[$k]['field'] != '*' && !is_numeric($select_fields[$k]['field'])) {
1659  $select_fields[$k]['field'] = $this->quoteName($select_fields[$k]['field']);
1660  }
1661  if ($select_fields[$k]['table'] != '' && !is_numeric($select_fields[$k]['table'])) {
1662  $select_fields[$k]['table'] = $this->quoteName($select_fields[$k]['table']);
1663  }
1664  if ($select_fields[$k]['as'] != '') {
1665  $select_fields[$k]['as'] = $this->quoteName($select_fields[$k]['as']);
1666  }
1667  if (isset($select_fields[$k]['func_content.']) && $select_fields[$k]['func_content.'][0]['func_content'] != '*') {
1668  $select_fields[$k]['func_content.'][0]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content.'][0]['func_content']);
1669  $select_fields[$k]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content']);
1670  }
1671  if (isset($select_fields[$k]['flow-control'])) {
1672  // Quoting flow-control statements
1673  if ($select_fields[$k]['flow-control']['type'] === 'CASE') {
1674  if (isset($select_fields[$k]['flow-control']['case_field'])) {
1675  $select_fields[$k]['flow-control']['case_field'] = $this->quoteFieldNames($select_fields[$k]['flow-control']['case_field']);
1676  }
1677  foreach ($select_fields[$k]['flow-control']['when'] as $key => $when) {
1678  $select_fields[$k]['flow-control']['when'][$key]['when_value'] = $this->_quoteWhereClause($when['when_value']);
1679  }
1680  }
1681  }
1682  }
1683  return $select_fields;
1684  }
1685 
1692  public function quoteFromTables($from_table)
1693  {
1694  if ($from_table === '') {
1695  return '';
1696  }
1697  if ($this->runningNative()) {
1698  return $from_table;
1699  }
1700  $from_table = $this->SQLparser->parseFromTables($from_table);
1701  $from_table = $this->_quoteFromTables($from_table);
1702  return $this->SQLparser->compileFromTables($from_table);
1703  }
1704 
1712  protected function _quoteFromTables(array $from_table)
1713  {
1714  foreach ($from_table as $k => $v) {
1715  $from_table[$k]['table'] = $this->quoteName($from_table[$k]['table']);
1716  if ($from_table[$k]['as'] != '') {
1717  $from_table[$k]['as'] = $this->quoteName($from_table[$k]['as']);
1718  }
1719  if (is_array($v['JOIN'])) {
1720  foreach ($v['JOIN'] as $joinCnt => $join) {
1721  $from_table[$k]['JOIN'][$joinCnt]['withTable'] = $this->quoteName($join['withTable']);
1722  $from_table[$k]['JOIN'][$joinCnt]['as'] = $join['as'] ? $this->quoteName($join['as']) : '';
1723  foreach ($from_table[$k]['JOIN'][$joinCnt]['ON'] as &$condition) {
1724  $condition['left']['table'] = $condition['left']['table'] ? $this->quoteName($condition['left']['table']) : '';
1725  $condition['left']['field'] = $this->quoteName($condition['left']['field']);
1726  $condition['right']['table'] = $condition['right']['table'] ? $this->quoteName($condition['right']['table']) : '';
1727  $condition['right']['field'] = $this->quoteName($condition['right']['field']);
1728  }
1729  }
1730  }
1731  }
1732  return $from_table;
1733  }
1734 
1742  public function quoteWhereClause($where_clause)
1743  {
1744  if ($where_clause === '' || $this->runningNative()) {
1745  return $where_clause;
1746  }
1747  $where_clause = $this->SQLparser->parseWhereClause($where_clause);
1748  if (is_array($where_clause)) {
1749  $where_clause = $this->_quoteWhereClause($where_clause);
1750  $where_clause = $this->SQLparser->compileWhereClause($where_clause);
1751  } else {
1752  throw new \InvalidArgumentException('Could not parse where clause', 1310027511);
1753  }
1754  return $where_clause;
1755  }
1756 
1764  protected function _quoteWhereClause(array $where_clause)
1765  {
1766  foreach ($where_clause as $k => $v) {
1767  // Look for sublevel:
1768  if (is_array($where_clause[$k]['sub'])) {
1769  $where_clause[$k]['sub'] = $this->_quoteWhereClause($where_clause[$k]['sub']);
1770  } elseif (isset($v['func'])) {
1771  switch ($where_clause[$k]['func']['type']) {
1772  case 'EXISTS':
1773  $where_clause[$k]['func']['subquery'] = $this->quoteSELECTsubquery($v['func']['subquery']);
1774  break;
1775  case 'FIND_IN_SET':
1776  // quoteStr that will be used for Oracle
1777  $pattern = str_replace($where_clause[$k]['func']['str'][1], '\\' . $where_clause[$k]['func']['str'][1], $where_clause[$k]['func']['str'][0]);
1778  // table is not really needed and may in fact be empty in real statements
1779  // but it's not overridden from \TYPO3\CMS\Core\Database\DatabaseConnection at the moment...
1780  $patternForLike = $this->escapeStrForLike($pattern, $where_clause[$k]['func']['table']);
1781  $where_clause[$k]['func']['str_like'] = $patternForLike;
1782  if ($where_clause[$k]['func']['table'] !== '') {
1783  if ($this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::CAST_FIND_IN_SET)) {
1784  $where_clause[$k]['func']['table'] = 'CAST(' . $this->quoteName($v['func']['table']);
1785  } else {
1786  $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
1787  }
1788  }
1789  if ($where_clause[$k]['func']['field'] !== '') {
1790  if ($this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::CAST_FIND_IN_SET)) {
1791  if ($where_clause[$k]['func']['table'] !== '') {
1792  $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']) . ' AS CHAR)';
1793  } else {
1794  $where_clause[$k]['func']['field'] = 'CAST(' . $this->quoteName($v['func']['field']) . ' AS CHAR)';
1795  }
1796  } else {
1797  $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
1798  }
1799  }
1800  break;
1801  case 'CAST':
1802  // Intentional fallthrough
1803  case 'IFNULL':
1804  // Intentional fallthrough
1805  case 'LOCATE':
1806  if ($where_clause[$k]['func']['table'] != '') {
1807  $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
1808  }
1809  if ($where_clause[$k]['func']['field'] != '') {
1810  $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
1811  }
1812  break;
1813  }
1814  } else {
1815  if ($where_clause[$k]['table'] != '') {
1816  $where_clause[$k]['table'] = $this->quoteName($where_clause[$k]['table']);
1817  }
1818  if (!is_numeric($where_clause[$k]['field'])) {
1819  $where_clause[$k]['field'] = $this->quoteName($where_clause[$k]['field']);
1820  }
1821  if (isset($where_clause[$k]['calc_table'])) {
1822  if ($where_clause[$k]['calc_table'] != '') {
1823  $where_clause[$k]['calc_table'] = $this->quoteName($where_clause[$k]['calc_table']);
1824  }
1825  if ($where_clause[$k]['calc_field'] != '') {
1826  $where_clause[$k]['calc_field'] = $this->quoteName($where_clause[$k]['calc_field']);
1827  }
1828  }
1829  }
1830  if ($where_clause[$k]['comparator']) {
1831  if (isset($v['value']['operator'])) {
1832  foreach ($where_clause[$k]['value']['args'] as $argK => $fieldDef) {
1833  $where_clause[$k]['value']['args'][$argK]['table'] = $this->quoteName($fieldDef['table']);
1834  $where_clause[$k]['value']['args'][$argK]['field'] = $this->quoteName($fieldDef['field']);
1835  }
1836  } else {
1837  // Detecting value type; list or plain:
1838  $comparator = $this->SQLparser->normalizeKeyword($where_clause[$k]['comparator']);
1839  if ($comparator === 'NOTIN' || $comparator === 'IN') {
1840  if (isset($v['subquery'])) {
1841  $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']);
1842  }
1843  } else {
1844  if (
1845  (!isset($where_clause[$k]['value'][1]) || $where_clause[$k]['value'][1] == '')
1846  && is_string($where_clause[$k]['value'][0]) && strstr($where_clause[$k]['value'][0], '.')
1847  ) {
1848  $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]);
1849  } elseif ($this->runningADOdbDriver('mssql')) {
1850  $where_clause[$k]['value'][0] = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($where_clause[$k]['value'][0]), 1, -1);
1851  }
1852  }
1853  }
1854  }
1855  }
1856  return $where_clause;
1857  }
1858 
1866  protected function quoteGroupBy($groupBy)
1867  {
1868  if ($groupBy === '') {
1869  return '';
1870  }
1871  if ($this->runningNative()) {
1872  return $groupBy;
1873  }
1874  $groupBy = $this->SQLparser->parseFieldList($groupBy);
1875  $groupBy = $this->_quoteGroupBy($groupBy);
1876  return $this->SQLparser->compileFieldList($groupBy);
1877  }
1878 
1886  protected function _quoteGroupBy(array $groupBy)
1887  {
1888  foreach ($groupBy as $k => $v) {
1889  $groupBy[$k]['field'] = $this->quoteName($groupBy[$k]['field']);
1890  if ($groupBy[$k]['table'] != '') {
1891  $groupBy[$k]['table'] = $this->quoteName($groupBy[$k]['table']);
1892  }
1893  }
1894  return $groupBy;
1895  }
1896 
1904  protected function quoteOrderBy($orderBy)
1905  {
1906  if ($orderBy === '') {
1907  return '';
1908  }
1909  if ($this->runningNative()) {
1910  return $orderBy;
1911  }
1912  $orderBy = $this->SQLparser->parseFieldList($orderBy);
1913  $orderBy = $this->_quoteOrderBy($orderBy);
1914  return $this->SQLparser->compileFieldList($orderBy);
1915  }
1916 
1924  protected function _quoteOrderBy(array $orderBy)
1925  {
1926  foreach ($orderBy as $k => $v) {
1927  if ($orderBy[$k]['table'] === '' && $v['field'] !== '' && ctype_digit($v['field'])) {
1928  continue;
1929  }
1930  $orderBy[$k]['field'] = $this->quoteName($orderBy[$k]['field']);
1931  if ($orderBy[$k]['table'] !== '') {
1932  $orderBy[$k]['table'] = $this->quoteName($orderBy[$k]['table']);
1933  }
1934  }
1935  return $orderBy;
1936  }
1937 
1938  /**************************************
1939  *
1940  * Various helper functions
1941  *
1942  **************************************/
1952  public function fullQuoteStr($str, $table, $allowNull = false)
1953  {
1954  if ($allowNull && $str === null) {
1955  return 'NULL';
1956  }
1957  return '\'' . $this->quoteStr($str, $table) . '\'';
1958  }
1959 
1971  public function quoteStr($str, $table)
1972  {
1973  $this->lastHandlerKey = $this->handler_getFromTableList($table);
1974  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1975  case 'native':
1976  if ($this->handlerInstance[$this->lastHandlerKey]['link']) {
1977  if (!$this->isConnected()) {
1978  $this->connectDB();
1979  }
1980  $str = $this->handlerInstance[$this->lastHandlerKey]['link']->real_escape_string($str);
1981  } else {
1982  // link may be null when unit testing DBAL
1983  $str = str_replace('\'', '\\\'', $str);
1984  }
1985  break;
1986  case 'adodb':
1987  if (!$this->isConnected()) {
1988  $this->connectDB();
1989  }
1990  $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1);
1991  break;
1992  case 'userdefined':
1993  $str = $this->handlerInstance[$this->lastHandlerKey]->quoteStr($str);
1994  break;
1995  default:
1996  throw new \RuntimeException('No handler found!!!', 1310027655);
1997  }
1998  return $str;
1999  }
2000 
2009  public function quoteName($name, $handlerKey = null, $useBackticks = false)
2010  {
2011  $handlerKey = $handlerKey ? $handlerKey : $this->lastHandlerKey;
2012  $useNameQuote = isset($this->handlerCfg[$handlerKey]['config']['useNameQuote']) ? $this->handlerCfg[$handlerKey]['config']['useNameQuote'] : false;
2013  if ($useNameQuote) {
2014  // Sometimes DataDictionary is not properly instantiated
2015  if (!is_object($this->handlerInstance[$handlerKey]->DataDictionary)) {
2016  $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
2017  }
2018  return $this->handlerInstance[$handlerKey]->DataDictionary->NameQuote($name);
2019  } else {
2020  $quote = $useBackticks ? '`' : $this->handlerInstance[$handlerKey]->nameQuote;
2021  return $quote . $name . $quote;
2022  }
2023  }
2024 
2035  public function MetaType($type, $table, $maxLength = -1)
2036  {
2038  return $this->getMetadata($type, $table, 'dummyFieldToBypassCache', $maxLength);
2039  }
2040 
2051  public function getMetadata($type, $table, $field, $maxLength = -1)
2052  {
2053  $this->lastHandlerKey = $this->handler_getFromTableList($table);
2054  $str = '';
2055  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2056  case 'native':
2057  $str = $type;
2058  break;
2059  case 'adodb':
2060  if (!empty($this->cache_fieldType[$table][$field])) {
2061  $str = $this->cache_fieldType[$table][$field]['metaType'];
2062  } else {
2063  $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1);
2064  $str = $rs->MetaType($type, $maxLength);
2065  }
2066  break;
2067  case 'userdefined':
2068  $str = $this->handlerInstance[$this->lastHandlerKey]->MetaType($str, $table, $maxLength);
2069  break;
2070  default:
2071  throw new \RuntimeException('No handler found!!!', 1310027685);
2072  }
2073  return $str;
2074  }
2075 
2083  public function MySQLMetaType($t)
2084  {
2086  return $this->dbmsSpecifics->getMetaFieldType($t);
2087  }
2088 
2096  public function MySQLActualType($meta)
2097  {
2099  return $this->dbmsSpecifics->getNativeFieldType($meta);
2100  }
2101 
2109  protected function updateLastInsertId($table, array $fieldValues)
2110  {
2111  if ($table === 'tx_dbal_debuglog') {
2112  return null;
2113  }
2114  $newId = null;
2115  if (isset($fieldValues[$this->cache_autoIncFields[$table]])) {
2116  $newId = $fieldValues[$this->cache_autoIncFields[$table]];
2117  } elseif ($this->handlerInstance[$this->lastHandlerKey]->hasInsertID && !empty($this->cache_autoIncFields[$table])) {
2118  // The table is able to retrieve the ID of the last insert
2119  $newId = $this->handlerInstance[$this->lastHandlerKey]->Insert_ID($table, $this->cache_autoIncFields[$table]);
2120  }
2121  if ($newId !== null) {
2122  $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $newId;
2123  }
2124  return $newId;
2125  }
2126 
2127  /*********************************************
2128  *
2129  * SqlSchemaMigrationService helper functions
2130  *
2131  *********************************************/
2139  public function getEquivalentIndexDefinition($indexSQL)
2140  {
2141  if ($this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX) && (bool)$this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX)) {
2142  return $indexSQL;
2143  }
2144 
2145  $strippedIndexSQL = preg_replace_callback(
2146  '/\A([^(]+)\((.*)\)\Z/',
2147  function ($matches) {
2148  return $matches[1] . '(' . preg_replace('/\((\d+)\)/', '', $matches[2]) . ')';
2149  },
2150  $indexSQL
2151  );
2152 
2153  return $strippedIndexSQL === null ? $indexSQL : $strippedIndexSQL;
2154  }
2155 
2163  public function getEquivalentFieldDefinition($fieldSQL)
2164  {
2165  if (!preg_match('/^([a-z0-9]+)(\(([^\)]+)\))?(.*)/', $fieldSQL, $components)) {
2166  return $fieldSQL;
2167  }
2168 
2169  $metaType = $this->dbmsSpecifics->getMetaFieldType($components[1]);
2170  $replacementType = $this->dbmsSpecifics->getNativeFieldType($metaType);
2171  $replacementLength = $components[2];
2172  $replacementExtra = '';
2173 
2174  // MySQL INT types support a display length that has no effect on the
2175  // actual range of values that can be stored, normalize to the default
2176  // display length returned by DBAL.
2177  if (substr($metaType, 0, 1) === 'I') {
2178  $replacementLength = $this->dbmsSpecifics->getNativeFieldLength($replacementType, $components[3]);
2179  }
2180 
2181  // MySQL TINYTEXT is equivalent to VARCHAR(255) DEFAULT NULL. MySQL TEXT
2182  // columns can not have a default value in contrast to VARCHAR, so the
2183  // `default NULL` gets appended to avoid false-positive schema changes.
2184  if ($components[1] === 'tinytext') {
2185  $replacementLength = '(255)';
2186  if (false !== stripos($components[0], ' NOT NULL')) {
2187  $replacementExtra = ' default \'\'';
2188  } else {
2189  $replacementExtra = ' default NULL';
2190  }
2191  }
2192 
2193  return str_replace($components[1] . $components[2], strtolower($replacementType) . $replacementLength, $components[0]) . $replacementExtra;
2194  }
2195 
2196  /**************************************
2197  *
2198  * SQL wrapper functions (Overriding parent methods)
2199  * (For use in your applications)
2200  *
2201  **************************************/
2207  public function sql_error()
2208  {
2209  $output = '';
2210  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2211  case 'native':
2212  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->error;
2213  break;
2214  case 'adodb':
2215  $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorMsg();
2216  break;
2217  case 'userdefined':
2218  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_error();
2219  break;
2220  }
2221  return $output;
2222  }
2223 
2229  public function sql_errno()
2230  {
2231  $output = 0;
2232  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2233  case 'native':
2234  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->errno;
2235  break;
2236  case 'adodb':
2237  $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorNo();
2238  break;
2239  case 'userdefined':
2240  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_errno();
2241  break;
2242  }
2243  return $output;
2244  }
2245 
2252  public function sql_num_rows($res)
2253  {
2254  if ($res === false) {
2255  return false;
2256  }
2257  $handlerType = $this->determineHandlerType($res);
2258  $output = 0;
2259  switch ($handlerType) {
2260  case 'native':
2261  $output = $res->num_rows;
2262  break;
2263  case 'adodb':
2264  $output = method_exists($res, 'RecordCount') ? $res->RecordCount() : 0;
2265  break;
2266  case 'userdefined':
2267  $output = $res->sql_num_rows();
2268  break;
2269  }
2270  return $output;
2271  }
2272 
2280  public function sql_fetch_assoc($res)
2281  {
2282  $tableList = '';
2283  $output = false;
2284  switch ($this->determineHandlerType($res)) {
2285  case 'native':
2286  $output = $res->fetch_assoc();
2287  $key = serialize($res);
2288  $tableList = $this->resourceIdToTableNameMap[$key];
2289  unset($this->resourceIdToTableNameMap[$key]);
2290  // Reading list of tables from SELECT query:
2291  break;
2292  case 'adodb':
2293  // Check if method exists for the current $res object.
2294  // If a table exists in TCA but not in the db, an error
2295  // occurred because $res is not a valid object.
2296  if (method_exists($res, 'FetchRow')) {
2297  $output = $res->FetchRow();
2298  $tableList = $res->TYPO3_DBAL_tableList;
2299  // Reading list of tables from SELECT query:
2300  // Removing all numeric/integer keys.
2301  // A workaround because in ADOdb we would need to know what we want before executing the query...
2302  // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So
2303  // we don't need to remove anything.
2304  if (is_array($output)) {
2305  if ($this->runningADOdbDriver('mssql')) {
2306  // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
2307  foreach ($output as $key => $value) {
2308  if ($value === ' ') {
2309  $output[$key] = '';
2310  }
2311  }
2312  } else {
2313  foreach ($output as $key => $value) {
2314  if (is_int($key)) {
2315  unset($output[$key]);
2316  }
2317  }
2318  }
2319  }
2320  }
2321  break;
2322  case 'userdefined':
2323  $output = $res->sql_fetch_assoc();
2324  $tableList = $res->TYPO3_DBAL_tableList;
2325  // Reading list of tables from SELECT query:
2326  break;
2327  }
2328  // Table/Fieldname mapping:
2329  if (is_array($output)) {
2330  if ($tables = $this->map_needMapping($tableList, true)) {
2331  $output = $this->map_assocArray($output, $tables, 1);
2332  }
2333  }
2334  if ($output === null) {
2335  // Needed for compatibility
2336  $output = false;
2337  }
2338  // Return result:
2339  return $output;
2340  }
2341 
2350  public function sql_fetch_row($res)
2351  {
2352  $output = false;
2353  switch ($this->determineHandlerType($res)) {
2354  case 'native':
2355  $output = $res->fetch_row();
2356  if ($output === null) {
2357  // Needed for compatibility
2358  $output = false;
2359  }
2360  break;
2361  case 'adodb':
2362  // Check if method exists for the current $res object.
2363  // If a table exists in TCA but not in the db, an error
2364  // occurred because $res is not a valid object.
2365  if (method_exists($res, 'FetchRow')) {
2366  $output = $res->FetchRow();
2367  // Removing all assoc. keys.
2368  // A workaround because in ADOdb we would need to know what we want before executing the query...
2369  // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So
2370  // we need to convert resultset.
2371  if (is_array($output)) {
2372  $keyIndex = 0;
2373  foreach ($output as $key => $value) {
2374  unset($output[$key]);
2375  if (is_int($key) || $this->runningADOdbDriver('mssql')) {
2376  $output[$keyIndex] = $value;
2377  if ($value === ' ') {
2378  // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
2379  $output[$keyIndex] = '';
2380  }
2381  $keyIndex++;
2382  }
2383  }
2384  }
2385  }
2386  break;
2387  case 'userdefined':
2388  $output = $res->sql_fetch_row();
2389  break;
2390  }
2391  if ($output === null) {
2392  // Needed for compatibility
2393  $output = false;
2394  }
2395  return $output;
2396  }
2397 
2405  public function sql_free_result($res)
2406  {
2407  if ($res === false) {
2408  return false;
2409  }
2410  $output = true;
2411  switch ($this->determineHandlerType($res)) {
2412  case 'native':
2413  $res->free();
2414  break;
2415  case 'adodb':
2416  if (method_exists($res, 'Close')) {
2417  $res->Close();
2418  unset($res);
2419  $output = true;
2420  } else {
2421  $output = false;
2422  }
2423  break;
2424  case 'userdefined':
2425  unset($res);
2426  break;
2427  }
2428  return $output;
2429  }
2430 
2437  protected function determineHandlerType($res)
2438  {
2439  if (is_object($res) && !$res instanceof \mysqli_result) {
2440  $handlerType = $res->TYPO3_DBAL_handlerType;
2441  } elseif ($res instanceof \mysqli_result) {
2442  $handlerType = 'native';
2443  } else {
2444  $handlerType = false;
2445  }
2446  return $handlerType;
2447  }
2448 
2454  public function sql_insert_id()
2455  {
2456  $output = 0;
2457  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2458  case 'native':
2459  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->insert_id;
2460  break;
2461  case 'adodb':
2462  $output = $this->handlerInstance[$this->lastHandlerKey]->last_insert_id;
2463  break;
2464  case 'userdefined':
2465  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_insert_id();
2466  break;
2467  }
2468  return $output;
2469  }
2470 
2476  public function sql_affected_rows()
2477  {
2478  $output = 0;
2479  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2480  case 'native':
2481  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->affected_rows;
2482  break;
2483  case 'adodb':
2484  $output = $this->handlerInstance[$this->lastHandlerKey]->Affected_Rows();
2485  break;
2486  case 'userdefined':
2487  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_affected_rows();
2488  break;
2489  }
2490  return $output;
2491  }
2492 
2500  public function sql_data_seek($res, $seek)
2501  {
2502  $output = true;
2503  switch ($this->determineHandlerType($res)) {
2504  case 'native':
2505  $output = $res->data_seek($seek);
2506  break;
2507  case 'adodb':
2508  $output = $res->Move($seek);
2509  break;
2510  case 'userdefined':
2511  $output = $res->sql_data_seek($seek);
2512  break;
2513  }
2514  return $output;
2515  }
2516 
2526  public function sql_field_metatype($table, $field)
2527  {
2528  // If $table and/or $field are mapped, use the original names instead
2529  foreach ($this->mapping as $tableName => $tableMapInfo) {
2530  if (isset($tableMapInfo['mapFieldNames'])) {
2531  foreach ($tableMapInfo['mapFieldNames'] as $fieldName => $fieldMapInfo) {
2532  if ($fieldMapInfo === $field) {
2533  // Field name is mapped => use original name
2534  $field = $fieldName;
2535  }
2536  }
2537  }
2538  }
2539  return $this->cache_fieldType[$table][$field]['metaType'];
2540  }
2541 
2550  public function sql_field_type($res, $pointer)
2551  {
2552  if ($res === null) {
2553  debug(['no res in sql_field_type!']);
2554  return 'text';
2555  } elseif (is_string($res)) {
2556  if ($res === 'tx_dbal_debuglog') {
2557  return 'text';
2558  }
2559  $handlerType = 'adodb';
2560  } else {
2561  $handlerType = $this->determineHandlerType($res);
2562  }
2563  $output = '';
2564  switch ($handlerType) {
2565  case 'native':
2566  $metaInfo = $res->fetch_field_direct($pointer);
2567  if ($metaInfo) {
2568  $output = $this->mysqlDataTypeMapping[$metaInfo->type];
2569  } else {
2570  $output = '';
2571  }
2572  break;
2573  case 'adodb':
2574  if (is_string($pointer)) {
2575  $output = $this->cache_fieldType[$res][$pointer]['type'];
2576  }
2577  break;
2578  case 'userdefined':
2579  $output = $res->sql_field_type($pointer);
2580  break;
2581  }
2582  return $output;
2583  }
2584 
2585  /**********
2586  *
2587  * Legacy functions, bound to _DEFAULT handler. (Overriding parent methods)
2588  * Deprecated or still experimental.
2589  *
2590  **********/
2607  public function sql_query($query)
2608  {
2609  $globalConfig = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['dbal']);
2610  if ($globalConfig['sql_query.']['passthrough']) {
2611  return parent::sql_query($query);
2612  }
2613  // This method is heavily used by Extbase, try to handle it with DBAL-native methods
2614  $queryParts = $this->SQLparser->parseSQL($query);
2615  if (is_array($queryParts)) {
2616  $operation = $queryParts['type'];
2617  if ($operation === 'SELECT' || $operation === 'UPDATE' || $operation === 'INSERT' || $operation === 'DELETE') {
2618  return $this->exec_query($queryParts);
2619  }
2620  }
2621  $sqlResult = null;
2622  switch ($this->handlerCfg['_DEFAULT']['type']) {
2623  case 'native':
2624  if (!$this->isConnected()) {
2625  $this->connectDB();
2626  }
2627  $sqlResult = $this->handlerInstance['_DEFAULT']['link']->query($query);
2628  break;
2629  case 'adodb':
2630  $sqlResult = $this->handlerInstance['_DEFAULT']->Execute($query);
2631  $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
2632  break;
2633  case 'userdefined':
2634  $sqlResult = $this->handlerInstance['_DEFAULT']->sql_query($query);
2635  $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
2636  break;
2637  }
2638  $this->lastHandlerKey = '_DEFAULT';
2639  if ($this->printErrors && $this->sql_error()) {
2640  debug([$this->lastQuery, $this->sql_error()]);
2641  }
2642  return $sqlResult;
2643  }
2644 
2650  public function sql_pconnect()
2651  {
2652  return $this->handler_init('_DEFAULT');
2653  }
2654 
2660  public function sql_select_db()
2661  {
2662  $databaseName = $this->handlerCfg[$this->lastHandlerKey]['config']['database'];
2663  $ret = true;
2664  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
2665  $ret = $this->handlerInstance[$this->lastHandlerKey]['link']->select_db($databaseName);
2666  }
2667  if (!$ret) {
2668  GeneralUtility::sysLog(
2669  'Could not select MySQL database ' . $databaseName . ': ' . $this->sql_error(),
2670  'core',
2672  );
2673  }
2674  return $ret;
2675  }
2676 
2677  /**************************************
2678  *
2679  * SQL admin functions
2680  * (For use in the Install Tool and Extension Manager)
2681  *
2682  **************************************/
2692  public function admin_get_dbs()
2693  {
2694  $dbArr = [];
2695  $this->lastHandlerKey = '_DEFAULT';
2696  switch ($this->handlerCfg['_DEFAULT']['type']) {
2697  case 'native':
2699  $db_list = $this->query('SELECT SCHEMA_NAME FROM information_schema.SCHEMATA');
2700  $oldDb = $this->handlerCfg[$this->lastHandlerKey]['config']['database'];
2701  while ($row = $db_list->fetch_object()) {
2702  $this->handlerCfg[$this->lastHandlerKey]['config']['database'] = $row->SCHEMA_NAME;
2703  if ($this->sql_select_db()) {
2704  $dbArr[] = $row->SCHEMA_NAME;
2705  }
2706  }
2707  $this->handlerCfg[$this->lastHandlerKey]['config']['database'] = $oldDb;
2708  $db_list->free();
2709  break;
2710  case 'adodb':
2711  // check needed for install tool - otherwise it will just die because the call to
2712  // MetaDatabases is done on a stdClass instance
2713  if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaDatabases')) {
2714  $sqlDBs = $this->handlerInstance['_DEFAULT']->MetaDatabases();
2715  if (is_array($sqlDBs)) {
2716  foreach ($sqlDBs as $k => $theDB) {
2717  $dbArr[] = $theDB;
2718  }
2719  }
2720  }
2721  break;
2722  case 'userdefined':
2723  $dbArr = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2724  break;
2725  }
2726  return $dbArr;
2727  }
2728 
2736  public function admin_get_tables()
2737  {
2738  $whichTables = [];
2739  // Getting real list of tables:
2740  $this->lastHandlerKey = '_DEFAULT';
2741  switch ($this->handlerCfg['_DEFAULT']['type']) {
2742  case 'native':
2743  $tables_result = $this->query('SHOW TABLE STATUS FROM `' . TYPO3_db . '`');
2744  if (!$this->sql_error()) {
2745  while ($theTable = $this->sql_fetch_assoc($tables_result)) {
2746  $whichTables[$theTable['Name']] = $theTable;
2747  }
2748  $tables_result->free();
2749  }
2750  break;
2751  case 'adodb':
2752  // check needed for install tool - otherwise it will just die because the call to
2753  // MetaTables is done on a stdClass instance
2754  if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaTables')) {
2755  $sqlTables = $this->handlerInstance['_DEFAULT']->MetaTables('TABLES');
2756  foreach ($sqlTables as $k => $theTable) {
2757  if (preg_match('/BIN\\$/', $theTable)) {
2758  // Skip tables from the Oracle 10 Recycle Bin
2759  continue;
2760  }
2761  $whichTables[$theTable] = ['Name' => $theTable];
2762  }
2763  }
2764  break;
2765  case 'userdefined':
2766  $whichTables = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2767  break;
2768  }
2769  // Check mapping:
2770  if (is_array($this->mapping) && !empty($this->mapping)) {
2771  // Mapping table names in reverse, first getting list of real table names:
2772  $tMap = [];
2773  foreach ($this->mapping as $tN => $tMapInfo) {
2774  if (isset($tMapInfo['mapTableName'])) {
2775  $tMap[$tMapInfo['mapTableName']] = $tN;
2776  }
2777  }
2778  // Do mapping:
2779  $newList = [];
2780  foreach ($whichTables as $tN => $tDefinition) {
2781  if (isset($tMap[$tN])) {
2782  $tN = $tMap[$tN];
2783  $tDefinition = ['Name' => $tN];
2784  }
2785  $newList[$tN] = $tDefinition;
2786  }
2787  $whichTables = $newList;
2788  }
2789  // Adding tables configured to reside in other DBMS (handler by other handlers than the default):
2790  if (is_array($this->table2handlerKeys)) {
2791  foreach ($this->table2handlerKeys as $key => $handlerKey) {
2792  $whichTables[$key] = ['Name' => $key];
2793  }
2794  }
2795  return $whichTables;
2796  }
2797 
2809  public function admin_get_fields($tableName)
2810  {
2811  $output = [];
2812  // Do field mapping if needed:
2813  $ORIG_tableName = $tableName;
2814  if ($tableArray = $this->map_needMapping($tableName)) {
2815  // Table name:
2816  if ($this->mapping[$tableName]['mapTableName']) {
2817  $tableName = $this->mapping[$tableName]['mapTableName'];
2818  }
2819  }
2820  // Find columns
2821  $this->lastHandlerKey = $this->handler_getFromTableList($tableName);
2822  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2823  case 'native':
2825  $columns_res = $this->query('SHOW columns FROM ' . $tableName);
2826  while ($fieldRow = $columns_res->fetch_assoc()) {
2827  $output[$fieldRow['Field']] = $fieldRow;
2828  }
2829  $columns_res->free();
2830  break;
2831  case 'adodb':
2832  $fieldRows = $this->handlerInstance[$this->lastHandlerKey]->MetaColumns($tableName, false);
2833  if (is_array($fieldRows)) {
2834  foreach ($fieldRows as $k => $fieldRow) {
2835  settype($fieldRow, 'array');
2836  $metaType = $this->getMetadata($fieldRow['type'], $tableName, $fieldRow['name']);
2837  $output[$fieldRow['name']] = $this->dbmsSpecifics->transformFieldRowToMySQL($fieldRow, $metaType);
2838  }
2839  }
2840  break;
2841  case 'userdefined':
2842  $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_fields($tableName);
2843  break;
2844  }
2845  // mapping should be done:
2846  if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2847  $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2848  $newOutput = [];
2849  foreach ($output as $fN => $fInfo) {
2850  if (isset($revFields[$fN])) {
2851  $fN = $revFields[$fN];
2852  $fInfo['Field'] = $fN;
2853  }
2854  $newOutput[$fN] = $fInfo;
2855  }
2856  $output = $newOutput;
2857  }
2858  return $output;
2859  }
2860 
2868  public function admin_get_keys($tableName)
2869  {
2870  $output = [];
2871  // Do field mapping if needed:
2872  $ORIG_tableName = $tableName;
2873  if ($tableArray = $this->map_needMapping($tableName)) {
2874  // Table name:
2875  if ($this->mapping[$tableName]['mapTableName']) {
2876  $tableName = $this->mapping[$tableName]['mapTableName'];
2877  }
2878  }
2879  // Find columns
2880  $this->lastHandlerKey = $this->handler_getFromTableList($tableName);
2881  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2882  case 'native':
2884  $keyRes = $this->query('SHOW keys FROM ' . $tableName);
2885  while ($keyRow = $keyRes->fetch_assoc()) {
2886  $output[] = $keyRow;
2887  }
2888  $keyRes->free();
2889  break;
2890  case 'adodb':
2891  $keyRows = $this->handlerInstance[$this->lastHandlerKey]->MetaIndexes($tableName);
2892  if ($keyRows !== false) {
2893  foreach ($keyRows as $k => $theKey) {
2894  $theKey['Table'] = $tableName;
2895  $theKey['Non_unique'] = (int)(!$theKey['unique']);
2896  $theKey['Key_name'] = str_replace(hash('crc32b', $tableName) . '_', '', $k);
2897  // the following are probably not needed anyway...
2898  $theKey['Collation'] = '';
2899  $theKey['Cardinality'] = '';
2900  $theKey['Sub_part'] = '';
2901  $theKey['Packed'] = '';
2902  $theKey['Null'] = '';
2903  $theKey['Index_type'] = '';
2904  $theKey['Comment'] = '';
2905  // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2906  $keycols = $theKey['columns'];
2907  foreach ($keycols as $c => $theCol) {
2908  $theKey['Seq_in_index'] = $c + 1;
2909  $theKey['Column_name'] = $theCol;
2910  $output[] = $theKey;
2911  }
2912  }
2913  }
2914  $priKeyRow = $this->handlerInstance[$this->lastHandlerKey]->MetaPrimaryKeys($tableName);
2915  $theKey = [];
2916  $theKey['Table'] = $tableName;
2917  $theKey['Non_unique'] = 0;
2918  $theKey['Key_name'] = 'PRIMARY';
2919  // the following are probably not needed anyway...
2920  $theKey['Collation'] = '';
2921  $theKey['Cardinality'] = '';
2922  $theKey['Sub_part'] = '';
2923  $theKey['Packed'] = '';
2924  $theKey['Null'] = '';
2925  $theKey['Index_type'] = '';
2926  $theKey['Comment'] = '';
2927  // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2928  if ($priKeyRow !== false) {
2929  foreach ($priKeyRow as $c => $theCol) {
2930  $theKey['Seq_in_index'] = $c + 1;
2931  $theKey['Column_name'] = $theCol;
2932  $output[] = $theKey;
2933  }
2934  }
2935  break;
2936  case 'userdefined':
2937  $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_keys($tableName);
2938  break;
2939  }
2940  // mapping should be done:
2941  if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2942  $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2943  $newOutput = [];
2944  foreach ($output as $kN => $kInfo) {
2945  // Table:
2946  $kInfo['Table'] = $ORIG_tableName;
2947  // Column
2948  if (isset($revFields[$kInfo['Column_name']])) {
2949  $kInfo['Column_name'] = $revFields[$kInfo['Column_name']];
2950  }
2951  // Write it back:
2952  $newOutput[$kN] = $kInfo;
2953  }
2954  $output = $newOutput;
2955  }
2956  return $output;
2957  }
2958 
2971  public function admin_get_charsets()
2972  {
2973  $output = [];
2974  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
2976  $columns_res = $this->query('SHOW CHARACTER SET');
2977  if ($columns_res !== false) {
2978  while ($row = $columns_res->fetch_assoc()) {
2979  $output[$row['Charset']] = $row;
2980  }
2981  $columns_res->free();
2982  }
2983  }
2984  return $output;
2985  }
2986 
2994  public function admin_query($query)
2995  {
2996  $parsedQuery = $this->SQLparser->parseSQL($query);
2997  if (!is_array($parsedQuery)) {
2998  throw new \InvalidArgumentException('ERROR: Query could not be parsed: "' . htmlspecialchars($parsedQuery) . '". Query: "' . htmlspecialchars($query) . '"', 1310027793);
2999  }
3000  $ORIG_table = $parsedQuery['TABLE'];
3001  // Process query based on type:
3002  switch ($parsedQuery['type']) {
3003  case 'CREATETABLE':
3004  case 'ALTERTABLE':
3005  $this->createMappingsIfRequired($parsedQuery);
3006  // Fall-through next instruction
3007  case 'DROPTABLE':
3008  $this->clearCachedFieldInfo();
3009  $this->map_genericQueryParsed($parsedQuery);
3010  break;
3011  case 'INSERT':
3012 
3013  case 'TRUNCATETABLE':
3014  $this->map_genericQueryParsed($parsedQuery);
3015  break;
3016  case 'CREATEDATABASE':
3017  throw new \InvalidArgumentException('Creating a database with DBAL is not supported. Did you really read the manual?', 1310027716);
3018  break;
3019  default:
3020  throw new \InvalidArgumentException('ERROR: Invalid Query type (' . $parsedQuery['type'] . ') for ->admin_query() function!: "' . htmlspecialchars($query) . '"', 1310027740);
3021  }
3022  // Setting query array (for other applications to access if needed)
3023  $this->lastParsedAndMappedQueryArray = $parsedQuery;
3024  // Execute query (based on handler derived from the TABLE name which we actually know for once!)
3025  $result = null;
3026  $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_table);
3027  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
3028  case 'native':
3029  // Compiling query:
3030  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
3031  if (!is_array($compiledQuery)) {
3032  $result = $this->query($compiledQuery);
3033  } else {
3034  $result = $this->query($compiledQuery[0]);
3035  }
3036  break;
3037  case 'adodb':
3038  // Compiling query:
3039  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
3040  switch ($this->lastParsedAndMappedQueryArray['type']) {
3041  case 'INSERT':
3042  $result = $this->exec_INSERTquery($this->lastParsedAndMappedQueryArray['TABLE'], $compiledQuery);
3043  break;
3044  case 'TRUNCATETABLE':
3045  $result = $this->exec_TRUNCATEquery($this->lastParsedAndMappedQueryArray['TABLE']);
3046  break;
3047  default:
3048  if (!is_array($compiledQuery)) {
3049  $compiledQuery = [$compiledQuery];
3050  }
3051  $result = $this->handlerInstance[$this->lastHandlerKey]->DataDictionary->ExecuteSQLArray($compiledQuery);
3052  }
3053  break;
3054  case 'userdefined':
3055  // Compiling query:
3056  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
3057  $result = $this->handlerInstance[$this->lastHandlerKey]->admin_query($compiledQuery);
3058  default:
3059  }
3060  return $result;
3061  }
3062 
3063  /************************************
3064  *
3065  * Handler management
3066  *
3067  **************************************/
3077  public function handler_getFromTableList($tableList)
3078  {
3079  $key = $tableList;
3080  if (!isset($this->cache_handlerKeyFromTableList[$key])) {
3081  // Get tables separated:
3082  $_tableList = $tableList;
3083  $tableArray = $this->SQLparser->parseFromTables($_tableList);
3084  // If success, traverse the tables:
3085  if (is_array($tableArray) && !empty($tableArray)) {
3086  $outputHandlerKey = '';
3087  foreach ($tableArray as $vArray) {
3088  // Find handler key, select "_DEFAULT" if none is specifically configured:
3089  $handlerKey = $this->table2handlerKeys[$vArray['table']] ? $this->table2handlerKeys[$vArray['table']] : '_DEFAULT';
3090  // In case of separate handler keys for joined tables:
3091  if ($outputHandlerKey && $handlerKey != $outputHandlerKey) {
3092  throw new \RuntimeException('DBAL fatal error: Tables in this list "' . $tableList . '" didn\'t use the same DB handler!', 1310027833);
3093  }
3094  $outputHandlerKey = $handlerKey;
3095  }
3096  // Check initialized state; if handler is NOT initialized (connected) then we will connect it!
3097  if (!isset($this->handlerInstance[$outputHandlerKey])) {
3098  $this->handler_init($outputHandlerKey);
3099  }
3100  // Return handler key:
3101  $this->cache_handlerKeyFromTableList[$key] = $outputHandlerKey;
3102  } else {
3103  throw new \RuntimeException('DBAL fatal error: No handler found in handler_getFromTableList() for: "' . $tableList . '" (' . $tableArray . ')', 1310027933);
3104  }
3105  }
3106  return $this->cache_handlerKeyFromTableList[$key];
3107  }
3108 
3117  public function handler_init($handlerKey)
3118  {
3119  if (!isset($this->handlerCfg[$handlerKey]) || !is_array($this->handlerCfg[$handlerKey])) {
3120  throw new \RuntimeException('ERROR: No handler for key "' . $handlerKey . '"', 1310028018);
3121  }
3122  if ($handlerKey === '_DEFAULT') {
3123  // Overriding the _DEFAULT handler configuration of username, password, localhost and database name:
3124  $this->handlerCfg[$handlerKey]['config']['username'] = $this->databaseUsername;
3125  $this->handlerCfg[$handlerKey]['config']['password'] = $this->databaseUserPassword;
3126  $this->handlerCfg[$handlerKey]['config']['host'] = $this->databaseHost;
3127  $this->handlerCfg[$handlerKey]['config']['port'] = (int)$this->databasePort;
3128  $this->handlerCfg[$handlerKey]['config']['database'] = $this->databaseName;
3129  }
3130  $cfgArray = $this->handlerCfg[$handlerKey];
3131  if (!$cfgArray['config']['database']) {
3132  // Configuration is incomplete
3133  return false;
3134  }
3135 
3136  $output = false;
3137  switch ((string)$cfgArray['type']) {
3138  case 'native':
3139  $host = $cfgArray['config']['host'];
3140  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
3141  $host = 'p:' . $host;
3142  }
3143  $link = mysqli_init();
3144  $connected = $link->real_connect(
3145  $host,
3146  $cfgArray['config']['username'],
3147  $cfgArray['config']['password'],
3148  $cfgArray['config']['database'],
3149  isset($cfgArray['config']['port']) ? $cfgArray['config']['port'] : null
3150  );
3151  if ($connected) {
3152  // Set handler instance:
3153  $this->handlerInstance[$handlerKey] = ['handlerType' => 'native', 'link' => $link];
3154 
3155  if ($link->set_charset($this->connectionCharset) === false) {
3156  GeneralUtility::sysLog(
3157  'Error setting connection charset to "' . $this->connectionCharset . '"',
3158  'core',
3160  );
3161  }
3162 
3163  // For default, set ->link (see \TYPO3\CMS\Core\Database\DatabaseConnection)
3164  if ($handlerKey === '_DEFAULT') {
3165  $this->link = $link;
3166  $this->isConnected = true;
3167  $this->lastHandlerKey = $handlerKey;
3168  foreach ($this->initializeCommandsAfterConnect as $command) {
3169  if ($this->query($command) === false) {
3170  GeneralUtility::sysLog(
3171  'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
3172  'core',
3174  );
3175  }
3176  }
3177  $this->checkConnectionCharset();
3178  }
3179 
3180  $output = true;
3181  } else {
3182  GeneralUtility::sysLog('Could not connect to MySQL server ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'core', GeneralUtility::SYSLOG_SEVERITY_FATAL);
3183  }
3184  break;
3185  case 'adodb':
3186  $output = true;
3187  require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('adodb') . 'adodb/adodb.inc.php';
3188  if (!defined('ADODB_FORCE_NULLS')) {
3189  define('ADODB_FORCE_NULLS', 1);
3190  }
3191  $GLOBALS['ADODB_FORCE_TYPE'] = ADODB_FORCE_VALUE;
3192  $GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_BOTH;
3193  $this->handlerInstance[$handlerKey] = ADONewConnection($cfgArray['config']['driver']);
3194  // Set driver-specific options
3195  if (isset($cfgArray['config']['driverOptions'])) {
3196  foreach ($cfgArray['config']['driverOptions'] as $optionName => $optionValue) {
3197  $optionSetterName = 'set' . ucfirst($optionName);
3198  if (method_exists($this->handlerInstance[$handlerKey], $optionSetterName)) {
3199  $this->handlerInstance[$handlerKey]->{$optionSetterName}($optionValue);
3200  } else {
3201  $this->handlerInstance[$handlerKey]->{$optionName} = $optionValue;
3202  }
3203  }
3204  }
3205  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
3206  $this->handlerInstance[$handlerKey]->Connect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']);
3207  } else {
3208  $this->handlerInstance[$handlerKey]->PConnect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']);
3209  }
3210  if (!$this->handlerInstance[$handlerKey]->isConnected()) {
3211  $dsn = $cfgArray['config']['driver'] . '://' . $cfgArray['config']['username'] . ((string)$cfgArray['config']['password'] !== '' ? ':XXXX@' : '') . $cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : '') . '/' . $cfgArray['config']['database'] . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'] ? '' : '?persistent=1');
3212  GeneralUtility::sysLog('Could not connect to DB server using ADOdb on ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'core', GeneralUtility::SYSLOG_SEVERITY_FATAL);
3213  error_log('DBAL error: Connection to ' . $dsn . ' failed. Maybe PHP doesn\'t support the database?');
3214  $output = false;
3215  } else {
3216  $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
3217  $this->handlerInstance[$handlerKey]->last_insert_id = 0;
3218  if (isset($cfgArray['config']['sequenceStart'])) {
3219  $this->handlerInstance[$handlerKey]->sequenceStart = $cfgArray['config']['sequenceStart'];
3220  } else {
3221  $this->handlerInstance[$handlerKey]->sequenceStart = 1;
3222  }
3223  }
3224  break;
3225  case 'userdefined':
3226  // if not set class may also be loaded by autoload on demand
3227  if (isset($cfgArray['config']['classFile'])) {
3228  GeneralUtility::deprecationLog('The DBAL handler option "config.classFile" is deprecated since TYPO3 CMS 7, and will be removed with CMS 8. Make use of autoloading instead.');
3229  // Find class file:
3230  $fileName = GeneralUtility::getFileAbsFileName($cfgArray['config']['classFile']);
3231  if (@is_file($fileName)) {
3232  require_once $fileName;
3233  } else {
3234  throw new \RuntimeException('DBAL error: "' . $fileName . '" was not a file to include.', 1310027975);
3235  }
3236  }
3237  // Initialize:
3238  $this->handlerInstance[$handlerKey] = GeneralUtility::makeInstance($cfgArray['config']['class']);
3239  $this->handlerInstance[$handlerKey]->init($cfgArray, $this);
3240  if (is_object($this->handlerInstance[$handlerKey])) {
3241  $output = true;
3242  }
3243  break;
3244  default:
3245  throw new \RuntimeException('ERROR: Invalid handler type: "' . $cfgArray['type'] . '"', 1310027995);
3246  }
3247  return $output;
3248  }
3249 
3255  public function isConnected()
3256  {
3257  $result = false;
3258  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
3259  case 'native':
3260  $result = isset($this->handlerCfg[$this->lastHandlerKey]['link']);
3261  break;
3262  case 'adodb':
3263 
3264  case 'userdefined':
3265  $result = is_object($this->handlerInstance[$this->lastHandlerKey]) && $this->handlerInstance[$this->lastHandlerKey]->isConnected();
3266  break;
3267  }
3268  return $result;
3269  }
3270 
3276  public function runningNative()
3277  {
3278  return (string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native';
3279  }
3280 
3287  public function runningADOdbDriver($driver)
3288  {
3289  return strpos($this->handlerCfg[$this->lastHandlerKey]['config']['driver'], $driver) !== false;
3290  }
3291 
3297  public function getServerVersion()
3298  {
3299  $result = '';
3300  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
3301  case 'native':
3302  $result = $this->handlerInstance[$this->lastHandlerKey]['link']->server_info;
3303  break;
3304  case 'adodb':
3305  case 'userdefined':
3306  if (is_object($this->handlerInstance[$this->lastHandlerKey])) {
3307  $serverInfo = $this->handlerInstance[$this->lastHandlerKey]->ServerInfo();
3308  $result = $serverInfo['version'];
3309  }
3310  break;
3311  }
3312  return $result;
3313  }
3314 
3315  /************************************
3316  *
3317  * Table/Field mapping
3318  *
3319  **************************************/
3328  protected function map_needMapping($tableList, $fieldMappingOnly = false, array &$parsedTableList = [])
3329  {
3330  $key = $tableList . '|' . $fieldMappingOnly;
3331  if (!isset($this->cache_mappingFromTableList[$key])) {
3332  $this->cache_mappingFromTableList[$key] = false;
3333  // Default:
3334  $tables = $this->SQLparser->parseFromTables($tableList);
3335  if (is_array($tables)) {
3336  $parsedTableList = $tables;
3337  foreach ($tables as $tableCfg) {
3338  if ($fieldMappingOnly) {
3339  if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
3340  $this->cache_mappingFromTableList[$key] = $tables;
3341  } elseif (is_array($tableCfg['JOIN'])) {
3342  foreach ($tableCfg['JOIN'] as $join) {
3343  if (is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
3344  $this->cache_mappingFromTableList[$key] = $tables;
3345  break;
3346  }
3347  }
3348  }
3349  } else {
3350  if (is_array($this->mapping[$tableCfg['table']])) {
3351  $this->cache_mappingFromTableList[$key] = $tables;
3352  } elseif (is_array($tableCfg['JOIN'])) {
3353  foreach ($tableCfg['JOIN'] as $join) {
3354  if (is_array($this->mapping[$join['withTable']])) {
3355  $this->cache_mappingFromTableList[$key] = $tables;
3356  break;
3357  }
3358  }
3359  }
3360  }
3361  }
3362  }
3363  }
3364  return $this->cache_mappingFromTableList[$key];
3365  }
3366 
3378  protected function map_assocArray($input, $tables, $rev = false)
3379  {
3380  // Traverse tables from query (hopefully only one table):
3381  foreach ($tables as $tableCfg) {
3382  $tableKey = $this->getMappingKey($tableCfg['table']);
3383  if (is_array($this->mapping[$tableKey]['mapFieldNames'])) {
3384  // Get the map (reversed if needed):
3385  if ($rev) {
3386  $theMap = array_flip($this->mapping[$tableKey]['mapFieldNames']);
3387  } else {
3388  $theMap = $this->mapping[$tableKey]['mapFieldNames'];
3389  }
3390  // Traverse selected record, map fieldnames:
3391  $output = [];
3392  foreach ($input as $fN => $value) {
3393  // Set the field name, change it if found in mapping array:
3394  if ($theMap[$fN]) {
3395  $newKey = $theMap[$fN];
3396  } else {
3397  $newKey = $fN;
3398  }
3399  // Set value to fieldname:
3400  $output[$newKey] = $value;
3401  }
3402  // When done, override the $input array with the result:
3403  $input = $output;
3404  }
3405  }
3406  // Return input array (which might have been altered in the mean time)
3407  return $input;
3408  }
3409 
3421  protected function map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy)
3422  {
3423  // Backup current mapping as it may be altered if aliases on mapped tables are found
3424  $backupMapping = $this->mapping;
3425  // Tables:
3426  $tables = is_array($from_table) ? $from_table : $this->SQLparser->parseFromTables($from_table);
3427  $defaultTable = $tables[0]['table'];
3428  // Prepare mapping for aliased tables. This will copy the definition of the original table name.
3429  // The alias is prefixed with a database-incompatible character to prevent naming clash with real table name
3430  // Further access to $this->mapping should be made through $this->getMappingKey() method
3431  foreach ($tables as $k => $v) {
3432  if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
3433  $mappingKey = $this->getFreeMappingKey($v['as']);
3434  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
3435  }
3436  if (is_array($v['JOIN'])) {
3437  foreach ($v['JOIN'] as $joinCnt => $join) {
3438  if ($join['as'] && is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
3439  $mappingKey = $this->getFreeMappingKey($join['as']);
3440  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$join['withTable']]['mapFieldNames'];
3441  }
3442  }
3443  }
3444  }
3445  foreach ($tables as $k => $v) {
3446  $tableKey = $this->getMappingKey($v['table']);
3447  if ($this->mapping[$tableKey]['mapTableName']) {
3448  $tables[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
3449  }
3450  // Mapping JOINS
3451  if (is_array($v['JOIN'])) {
3452  foreach ($v['JOIN'] as $joinCnt => $join) {
3453  // Mapping withTable of the JOIN
3454  $withTableKey = $this->getMappingKey($join['withTable']);
3455  if ($this->mapping[$withTableKey]['mapTableName']) {
3456  $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$withTableKey]['mapTableName'];
3457  }
3458  $onPartsArray = [];
3459  // Mapping ON parts of the JOIN
3460  if (is_array($tables[$k]['JOIN'][$joinCnt]['ON'])) {
3461  foreach ($tables[$k]['JOIN'][$joinCnt]['ON'] as &$condition) {
3462  // Left side of the comparator
3463  $leftTableKey = $this->getMappingKey($condition['left']['table']);
3464  if (isset($this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']])) {
3465  $condition['left']['field'] = $this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']];
3466  }
3467  if (isset($this->mapping[$leftTableKey]['mapTableName'])) {
3468  $condition['left']['table'] = $this->mapping[$leftTableKey]['mapTableName'];
3469  }
3470  // Right side of the comparator
3471  $rightTableKey = $this->getMappingKey($condition['right']['table']);
3472  if (isset($this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']])) {
3473  $condition['right']['field'] = $this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']];
3474  }
3475  if (isset($this->mapping[$rightTableKey]['mapTableName'])) {
3476  $condition['right']['table'] = $this->mapping[$rightTableKey]['mapTableName'];
3477  }
3478  }
3479  }
3480  }
3481  }
3482  }
3483  $fromParts = $tables;
3484  // Where clause:
3485  $parameterReferences = [];
3486  $whereParts = $this->SQLparser->parseWhereClause($where_clause, '', $parameterReferences);
3487  $this->map_sqlParts($whereParts, $defaultTable);
3488  // Select fields:
3489  $selectParts = $this->SQLparser->parseFieldList($select_fields);
3490  $this->map_sqlParts($selectParts, $defaultTable);
3491  // Group By fields
3492  $groupByParts = $this->SQLparser->parseFieldList($groupBy);
3493  $this->map_sqlParts($groupByParts, $defaultTable);
3494  // Order By fields
3495  $orderByParts = $this->SQLparser->parseFieldList($orderBy);
3496  $this->map_sqlParts($orderByParts, $defaultTable);
3497  // Restore the original mapping
3498  $this->mapping = $backupMapping;
3499  return [$selectParts, $fromParts, $whereParts, $groupByParts, $orderByParts, $parameterReferences];
3500  }
3501 
3509  protected function getMappingKey($tableName)
3510  {
3511  // Search deepest alias mapping
3512  while (isset($this->mapping['*' . $tableName])) {
3513  $tableName = '*' . $tableName;
3514  }
3515  return $tableName;
3516  }
3517 
3524  protected function getFreeMappingKey($tableName)
3525  {
3526  while (isset($this->mapping[$tableName])) {
3527  $tableName = '*' . $tableName;
3528  }
3529  return $tableName;
3530  }
3531 
3540  protected function map_sqlParts(&$sqlPartArray, $defaultTable)
3541  {
3542  $defaultTableKey = $this->getMappingKey($defaultTable);
3543  // Traverse sql Part array:
3544  if (is_array($sqlPartArray)) {
3545  foreach ($sqlPartArray as $k => $v) {
3546  if (isset($sqlPartArray[$k]['type'])) {
3547  switch ($sqlPartArray[$k]['type']) {
3548  case 'flow-control':
3549  $temp = [$sqlPartArray[$k]['flow-control']];
3550  $this->map_sqlParts($temp, $defaultTable);
3551  // Call recursively!
3552  $sqlPartArray[$k]['flow-control'] = $temp[0];
3553  break;
3554  case 'CASE':
3555  if (isset($sqlPartArray[$k]['case_field'])) {
3556  $fieldArray = explode('.', $sqlPartArray[$k]['case_field']);
3557  $fieldArrayCount = count($fieldArray);
3558  if ($fieldArrayCount === 1 && is_array($this->mapping[$defaultTableKey]['mapFieldNames']) && isset($this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]])) {
3559  $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]];
3560  } elseif ($fieldArrayCount === 2) {
3561  // Map the external table
3562  $table = $fieldArray[0];
3563  $tableKey = $this->getMappingKey($table);
3564  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3565  $table = $this->mapping[$tableKey]['mapTableName'];
3566  }
3567  // Map the field itself
3568  $field = $fieldArray[1];
3569  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3570  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3571  }
3572  $sqlPartArray[$k]['case_field'] = $table . '.' . $field;
3573  }
3574  }
3575  foreach ($sqlPartArray[$k]['when'] as $key => $when) {
3576  $this->map_sqlParts($sqlPartArray[$k]['when'][$key]['when_value'], $defaultTable);
3577  }
3578  break;
3579  }
3580  }
3581  // Look for sublevel (WHERE parts only)
3582  if (is_array($sqlPartArray[$k]['sub'])) {
3583  $this->map_sqlParts($sqlPartArray[$k]['sub'], $defaultTable);
3584  } elseif (isset($sqlPartArray[$k]['func'])) {
3585  switch ($sqlPartArray[$k]['func']['type']) {
3586  case 'EXISTS':
3587  $this->map_subquery($sqlPartArray[$k]['func']['subquery']);
3588  break;
3589  case 'FIND_IN_SET':
3590 
3591  case 'IFNULL':
3592 
3593  case 'LOCATE':
3594  // For the field, look for table mapping (generic):
3595  $t = $sqlPartArray[$k]['func']['table'] ? $sqlPartArray[$k]['func']['table'] : $defaultTable;
3596  $t = $this->getMappingKey($t);
3597  if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]) {
3598  $sqlPartArray[$k]['func']['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']];
3599  }
3600  if ($this->mapping[$t]['mapTableName']) {
3601  $sqlPartArray[$k]['func']['table'] = $this->mapping[$t]['mapTableName'];
3602  }
3603  break;
3604  }
3605  } else {
3606  // For the field, look for table mapping (generic):
3607  $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable;
3608  $t = $this->getMappingKey($t);
3609  // Mapping field name, if set:
3610  if (is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']])) {
3611  $sqlPartArray[$k]['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']];
3612  }
3613  // Mapping field name in SQL-functions like MIN(), MAX() or SUM()
3614  if ($this->mapping[$t]['mapFieldNames']) {
3615  $fieldArray = explode('.', $sqlPartArray[$k]['func_content']);
3616  $fieldArrayCount = count($fieldArray);
3617  if ($fieldArrayCount === 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
3618  $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3619  $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3620  } elseif ($fieldArrayCount === 2) {
3621  // Map the external table
3622  $table = $fieldArray[0];
3623  $tableKey = $this->getMappingKey($table);
3624  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3625  $table = $this->mapping[$tableKey]['mapTableName'];
3626  }
3627  // Map the field itself
3628  $field = $fieldArray[1];
3629  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3630  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3631  }
3632  $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field;
3633  $sqlPartArray[$k]['func_content'] = $table . '.' . $field;
3634  }
3635  // Mapping flow-control statements
3636  if (isset($sqlPartArray[$k]['flow-control'])) {
3637  if (isset($sqlPartArray[$k]['flow-control']['type'])) {
3638  $temp = [$sqlPartArray[$k]['flow-control']];
3639  $this->map_sqlParts($temp, $t);
3640  // Call recursively!
3641  $sqlPartArray[$k]['flow-control'] = $temp[0];
3642  }
3643  }
3644  }
3645  // Do we have a function (e.g., CONCAT)
3646  if (isset($v['value']['operator'])) {
3647  foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) {
3648  $tableKey = $this->getMappingKey($fieldDef['table']);
3649  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3650  $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$tableKey]['mapTableName'];
3651  }
3652  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']])) {
3653  $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']];
3654  }
3655  }
3656  }
3657  // Do we have a subquery (WHERE parts only)?
3658  if (isset($sqlPartArray[$k]['subquery'])) {
3659  $this->map_subquery($sqlPartArray[$k]['subquery']);
3660  }
3661  // do we have a field name in the value?
3662  // this is a very simplistic check, beware
3663  if (!is_numeric($sqlPartArray[$k]['value'][0]) && !isset($sqlPartArray[$k]['value'][1])) {
3664  $fieldArray = explode('.', $sqlPartArray[$k]['value'][0]);
3665  $fieldArrayCount = count($fieldArray);
3666  if ($fieldArrayCount === 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
3667  $sqlPartArray[$k]['value'][0] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3668  } elseif ($fieldArrayCount === 2) {
3669  // Map the external table
3670  $table = $fieldArray[0];
3671  $tableKey = $this->getMappingKey($table);
3672  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3673  $table = $this->mapping[$tableKey]['mapTableName'];
3674  }
3675  // Map the field itself
3676  $field = $fieldArray[1];
3677  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3678  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3679  }
3680  $sqlPartArray[$k]['value'][0] = $table . '.' . $field;
3681  }
3682  }
3683  // Map table?
3684  $tableKey = $this->getMappingKey($sqlPartArray[$k]['table']);
3685  if ($sqlPartArray[$k]['table'] && $this->mapping[$tableKey]['mapTableName']) {
3686  $sqlPartArray[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
3687  }
3688  }
3689  }
3690  }
3691  }
3692 
3699  protected function map_subquery(&$parsedQuery)
3700  {
3701  // Backup current mapping as it may be altered
3702  $backupMapping = $this->mapping;
3703  foreach ($parsedQuery['FROM'] as $k => $v) {
3704  $mappingKey = $v['table'];
3705  if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
3706  $mappingKey = $this->getFreeMappingKey($v['as']);
3707  } else {
3708  // Should ensure that no alias is defined in the external query
3709  // which would correspond to a real table name in the subquery
3710  if ($this->getMappingKey($v['table']) !== $v['table']) {
3711  $mappingKey = $this->getFreeMappingKey($v['table']);
3712  // This is the only case when 'mapTableName' should be copied
3713  $this->mapping[$mappingKey]['mapTableName'] =& $this->mapping[$v['table']]['mapTableName'];
3714  }
3715  }
3716  if ($mappingKey !== $v['table']) {
3717  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
3718  }
3719  }
3720  // Perform subquery's remapping
3721  $defaultTable = $parsedQuery['FROM'][0]['table'];
3722  $this->map_sqlParts($parsedQuery['SELECT'], $defaultTable);
3723  $this->map_sqlParts($parsedQuery['FROM'], $defaultTable);
3724  $this->map_sqlParts($parsedQuery['WHERE'], $defaultTable);
3725  // Restore the mapping
3726  $this->mapping = $backupMapping;
3727  }
3728 
3738  protected function map_genericQueryParsed(&$parsedQuery)
3739  {
3740  // Getting table - same for all:
3741  $table = $parsedQuery['TABLE'];
3742  if (!$table) {
3743  throw new \InvalidArgumentException('ERROR, mapping: No table found in parsed Query array...', 1310028048);
3744  }
3745  // Do field mapping if needed:
3746  if ($tableArray = $this->map_needMapping($table)) {
3747  // Table name:
3748  if ($this->mapping[$table]['mapTableName']) {
3749  $parsedQuery['TABLE'] = $this->mapping[$table]['mapTableName'];
3750  }
3751  // Based on type, do additional changes:
3752  switch ($parsedQuery['type']) {
3753  case 'ALTERTABLE':
3754  // Changing field name:
3755  $newFieldName = $this->mapping[$table]['mapFieldNames'][$parsedQuery['FIELD']];
3756  if ($newFieldName) {
3757  if ($parsedQuery['FIELD'] == $parsedQuery['newField']) {
3758  $parsedQuery['FIELD'] = ($parsedQuery['newField'] = $newFieldName);
3759  } else {
3760  $parsedQuery['FIELD'] = $newFieldName;
3761  }
3762  }
3763  // Changing key field names:
3764  if (is_array($parsedQuery['fields'])) {
3765  $this->map_fieldNamesInArray($table, $parsedQuery['fields']);
3766  }
3767  break;
3768  case 'CREATETABLE':
3769  // Remapping fields:
3770  if (is_array($parsedQuery['FIELDS'])) {
3771  $newFieldsArray = [];
3772  foreach ($parsedQuery['FIELDS'] as $fN => $fInfo) {
3773  if ($this->mapping[$table]['mapFieldNames'][$fN]) {
3774  $fN = $this->mapping[$table]['mapFieldNames'][$fN];
3775  }
3776  $newFieldsArray[$fN] = $fInfo;
3777  }
3778  $parsedQuery['FIELDS'] = $newFieldsArray;
3779  }
3780  // Remapping keys:
3781  if (is_array($parsedQuery['KEYS'])) {
3782  foreach ($parsedQuery['KEYS'] as $kN => $kInfo) {
3783  $this->map_fieldNamesInArray($table, $parsedQuery['KEYS'][$kN]);
3784  }
3785  }
3786  break;
3787  }
3788  }
3789  }
3790 
3798  protected function map_fieldNamesInArray($table, &$fieldArray)
3799  {
3800  if (is_array($this->mapping[$table]['mapFieldNames'])) {
3801  foreach ($fieldArray as $k => $v) {
3802  if ($this->mapping[$table]['mapFieldNames'][$v]) {
3803  $fieldArray[$k] = $this->mapping[$table]['mapFieldNames'][$v];
3804  }
3805  }
3806  }
3807  }
3808 
3815  protected function createMappingsIfRequired($parsedQuery)
3816  {
3817  if (
3818  !$this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::TABLE_MAXLENGTH)
3819  && !$this->dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::FIELD_MAXLENGTH)
3820  ) {
3821  return;
3822  }
3823 
3824  $mappingConfiguration = [];
3825  $table = $parsedQuery['TABLE'];
3826  if (!isset($this->mapping[$table])) {
3827  $truncatedTable = $this->dbmsSpecifics->truncateIdentifier($table, Specifics\AbstractSpecifics::TABLE_MAXLENGTH);
3828  if ($table !== $truncatedTable) {
3829  $mappingConfiguration['mapTableName'] = $truncatedTable;
3830  }
3831  }
3832  foreach ($parsedQuery['FIELDS'] as $field => $_) {
3833  if (!isset($this->mapping[$table]['mapFieldNames'][$field])) {
3834  $truncatedField = $this->dbmsSpecifics->truncateIdentifier($field, Specifics\AbstractSpecifics::FIELD_MAXLENGTH);
3835  if ($field !== $truncatedField) {
3836  $mappingConfiguration['mapFieldNames'][$field] = $truncatedField;
3837  }
3838  }
3839  }
3840  if (!empty($mappingConfiguration)) {
3842  $objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
3844  $configurationManager = $objectManager->get(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
3845  $configurationManager->setLocalConfigurationValueByPath(
3846  'EXTCONF/dbal/mapping/' . $table,
3847  $mappingConfiguration
3848  );
3849 
3850  // renew mapping information
3851  $this->mapping = array_merge($this->mapping, [$table => $mappingConfiguration]);
3852  }
3853  }
3854 
3855  /**************************************
3856  *
3857  * Debugging
3858  *
3859  **************************************/
3869  public function debugHandler($function, $execTime, $inData)
3870  {
3871  // we don't want to log our own log/debug SQL
3873  if (substr($script, -strlen('dbal/mod1/index.php')) != 'dbal/mod1/index.php' && !strstr($inData['args'][0], 'tx_dbal_debuglog')) {
3874  $data = [];
3875  $errorFlag = 0;
3876  $joinTable = '';
3877  if ($this->sql_error()) {
3878  $data['sqlError'] = $this->sql_error();
3879  $errorFlag |= 1;
3880  }
3881  // if lastQuery is empty (for whatever reason) at least log inData.args
3882  if (empty($this->lastQuery)) {
3883  $query = implode(' ', $inData['args']);
3884  } else {
3885  $query = $this->lastQuery;
3886  }
3887  if ($this->conf['debugOptions']['numberRows']) {
3888  switch ($function) {
3889  case 'exec_INSERTquery':
3890 
3891  case 'exec_UPDATEquery':
3892 
3893  case 'exec_DELETEquery':
3894  $data['numberRows'] = $this->sql_affected_rows();
3895  break;
3896  case 'exec_SELECTquery':
3897  $data['numberRows'] = $inData['numberRows'];
3898  break;
3899  }
3900  }
3901  if ($this->conf['debugOptions']['backtrace']) {
3902  $backtrace = debug_backtrace(0);
3903  $data['backtrace'] = array_slice($backtrace, 1, $this->conf['debugOptions']['backtrace']);
3904  }
3905  switch ($function) {
3906  case 'exec_INSERTquery':
3907 
3908  case 'exec_UPDATEquery':
3909 
3910  case 'exec_DELETEquery':
3911  $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script);
3912  break;
3913  case 'exec_SELECTquery':
3914  // Get explain data:
3915  if ($this->conf['debugOptions']['EXPLAIN'] && ($inData['handlerType'] === 'adodb' || $inData['handlerType'] === 'native')) {
3916  $data['EXPLAIN'] = $this->debug_explain($this->lastQuery);
3917  }
3918  // Check parsing of Query:
3919  if ($this->conf['debugOptions']['parseQuery']) {
3920  $parseResults = [];
3921  $parseResults['SELECT'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][1]);
3922  $parseResults['FROM'] = $this->SQLparser->debug_parseSQLpart('FROM', $inData['args'][0]);
3923  $parseResults['WHERE'] = $this->SQLparser->debug_parseSQLpart('WHERE', $inData['args'][2]);
3924  $parseResults['GROUPBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][3]);
3925  // Using select field list syntax
3926  $parseResults['ORDERBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][4]);
3927  // Using select field list syntax
3928  foreach ($parseResults as $k => $v) {
3929  if ($v === '') {
3930  unset($parseResults[$k]);
3931  }
3932  }
3933  if (!empty($parseResults)) {
3934  $data['parseError'] = $parseResults;
3935  $errorFlag |= 2;
3936  }
3937  }
3938  // Checking joinTables:
3939  if ($this->conf['debugOptions']['joinTables']) {
3940  if (count(explode(',', $inData['ORIG_from_table'])) > 1) {
3941  $joinTable = $inData['args'][0];
3942  }
3943  }
3944  // Logging it:
3945  $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script);
3946  if (!empty($inData['args'][2])) {
3947  $this->debug_WHERE($inData['args'][0], $inData['args'][2], $script);
3948  }
3949  break;
3950  }
3951  }
3952  }
3953 
3962  public function debug_WHERE($table, $where, $script = '')
3963  {
3964  $insertArray = [
3965  'tstamp' => $GLOBALS['EXEC_TIME'],
3966  'beuser_id' => (int)$GLOBALS['BE_USER']->user['uid'],
3967  'script' => $script,
3968  'tablename' => $table,
3969  'whereclause' => $where
3970  ];
3971  $this->exec_INSERTquery('tx_dbal_debuglog_where', $insertArray);
3972  }
3973 
3985  public function debug_log($query, $ms, $data, $join, $errorFlag, $script = '')
3986  {
3987  if (is_array($query)) {
3988  $queryToLog = $query[0] . ' -- ';
3989  if (!empty($query[1])) {
3990  $queryToLog .= count($query[1]) . ' BLOB FIELDS: ' . implode(', ', array_keys($query[1]));
3991  }
3992  if (!empty($query[2])) {
3993  $queryToLog .= count($query[2]) . ' CLOB FIELDS: ' . implode(', ', array_keys($query[2]));
3994  }
3995  } else {
3996  $queryToLog = $query;
3997  }
3998  $insertArray = [
3999  'tstamp' => $GLOBALS['EXEC_TIME'],
4000  'beuser_id' => (int)$GLOBALS['BE_USER']->user['uid'],
4001  'script' => $script,
4002  'exec_time' => $ms,
4003  'table_join' => $join,
4004  'serdata' => serialize($data),
4005  'query' => $queryToLog,
4006  'errorFlag' => $errorFlag
4007  ];
4008  $this->exec_INSERTquery('tx_dbal_debuglog', $insertArray);
4009  }
4010 
4017  public function debug_explain($query)
4018  {
4019  $output = [];
4020  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
4021  switch ($hType) {
4022  case 'native':
4023  $res = $this->sql_query('EXPLAIN ' . $query);
4024  while ($row = $this->sql_fetch_assoc($res)) {
4025  $output[] = $row;
4026  }
4027  break;
4028  case 'adodb':
4029  switch ($this->handlerCfg['_DEFAULT']['config']['driver']) {
4030  case 'oci8':
4031  $this->sql_query('EXPLAIN PLAN ' . $query);
4032  $output[] = 'EXPLAIN PLAN data logged to default PLAN_TABLE';
4033  break;
4034  default:
4035  $res = $this->sql_query('EXPLAIN ' . $query);
4036  while ($row = $this->sql_fetch_assoc($res)) {
4037  $output[] = $row;
4038  }
4039  }
4040  break;
4041  }
4042  return $output;
4043  }
4044 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
INSERTquery($table, $fields_values, $no_quote_fields=false)
UPDATEquery($table, $where, $fields_values, $no_quote_fields=false)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static getCacheKey($config)
Definition: QueryCache.php:28
prepare_PREPAREDquery($query, array $queryComponents)
debugHandler($function, $execTime, $inData)
if(isset($_REQUEST['nrows'])) else $rs
Definition: server.php:94
exec_INSERTquery($table, $fields_values, $no_quote_fields=false)
exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields=false)
exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields=false)
quoteName($name, $handlerKey=null, $useBackticks=false)
map_needMapping($tableList, $fieldMappingOnly=false, array &$parsedTableList=[])
prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='', array $input_parameters=[])
debug_log($query, $ms, $data, $join, $errorFlag, $script='')
getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit)
$driver
Definition: server.php:36
map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy)
exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='')
SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='')
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
$host
Definition: server.php:37
fullQuoteStr($str, $table, $allowNull=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
$sql
Definition: server.php:84
getMetadata($type, $table, $field, $maxLength=-1)
INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields=false)
map_sqlParts(&$sqlPartArray, $defaultTable)