TYPO3 CMS  TYPO3_6-2
DatabaseConnection.php
Go to the documentation of this file.
1 <?php
3 
18 
27 
31  protected $printErrors = FALSE;
32 
37  public $debug = FALSE;
38 
43  public $conf = array();
44 
49  public $mapping = array();
50 
55  protected $table2handlerKeys = array();
56 
61  public $handlerCfg = array(
62  '_DEFAULT' => array(
63  'type' => 'native',
64  'config' => array(
65  'username' => '',
66  // Set by default (overridden)
67  'password' => '',
68  // Set by default (overridden)
69  'host' => '',
70  // Set by default (overridden)
71  'database' => '',
72  // Set by default (overridden)
73  'driver' => '',
74  // ONLY "adodb" type; eg. "mysql"
75  'sequenceStart' => 1,
76  // ONLY "adodb", first number in sequences/serials/...
77  'useNameQuote' => 0,
78  // ONLY "adodb", whether to use NameQuote() method from ADOdb to quote names
79  'quoteClob' => FALSE
80  )
81  )
82  );
83 
93  public $handlerInstance = array();
94 
99  public $lastHandlerKey = '';
100 
105  protected $lastQuery = '';
106 
111  protected $lastParsedAndMappedQueryArray = array();
112 
116  protected $resourceIdToTableNameMap = array();
117 
121  protected $cache_handlerKeyFromTableList = array();
122 
126  protected $cache_mappingFromTableList = array();
127 
132  public $cache_autoIncFields = array();
133 
137  public $cache_fieldType = array();
138 
142  public $cache_primaryKeys = array();
143 
147  protected $cacheIdentifier = 't3lib_db_fieldInfo';
148 
155  public $SQLparser;
156 
160  protected $installerSql = NULL;
161 
167  protected $queryCache;
168 
176  protected $mysqlDataTypeMapping = array(
177  MYSQLI_TYPE_TINY => 'tinyint',
178  MYSQLI_TYPE_CHAR => 'tinyint',
179  MYSQLI_TYPE_SHORT => 'smallint',
180  MYSQLI_TYPE_LONG => 'int',
181  MYSQLI_TYPE_FLOAT => 'float',
182  MYSQLI_TYPE_DOUBLE => 'double',
183  MYSQLI_TYPE_TIMESTAMP => 'timestamp',
184  MYSQLI_TYPE_LONGLONG => 'bigint',
185  MYSQLI_TYPE_INT24 => 'mediumint',
186  MYSQLI_TYPE_DATE => 'date',
187  MYSQLI_TYPE_NEWDATE => 'date',
188  MYSQLI_TYPE_TIME => 'time',
189  MYSQLI_TYPE_DATETIME => 'datetime',
190  MYSQLI_TYPE_YEAR => 'year',
191  MYSQLI_TYPE_BIT => 'bit',
192  MYSQLI_TYPE_INTERVAL => 'interval',
193  MYSQLI_TYPE_ENUM => 'enum',
194  MYSQLI_TYPE_SET => 'set',
195  MYSQLI_TYPE_TINY_BLOB => 'blob',
196  MYSQLI_TYPE_MEDIUM_BLOB => 'blob',
197  MYSQLI_TYPE_LONG_BLOB => 'blob',
198  MYSQLI_TYPE_BLOB => 'blob',
199  MYSQLI_TYPE_VAR_STRING => 'varchar',
200  MYSQLI_TYPE_STRING => 'char',
201  MYSQLI_TYPE_DECIMAL => 'decimal',
202  MYSQLI_TYPE_NEWDECIMAL => 'decimal',
203  MYSQLI_TYPE_GEOMETRY => 'geometry'
204  );
205 
209  protected $dbmsSpecifics;
210 
215  public function __construct() {
216  // Set SQL parser object for internal use:
217  $this->SQLparser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\SqlParser', $this);
218  $this->installerSql = GeneralUtility::makeInstance('TYPO3\\CMS\\Install\\Service\\SqlSchemaMigrationService');
219  $this->queryCache = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('dbal');
220  // Set internal variables with configuration:
221  $this->conf = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal'];
222  }
223 
229  public function initialize() {
230  // Set outside configuration:
231  if (isset($this->conf['mapping'])) {
232  $this->mapping = $this->conf['mapping'];
233  }
234  if (isset($this->conf['table2handlerKeys'])) {
235  $this->table2handlerKeys = $this->conf['table2handlerKeys'];
236  }
237  if (isset($this->conf['handlerCfg'])) {
238  $this->handlerCfg = $this->conf['handlerCfg'];
239 
240  if (isset($this->handlerCfg['_DEFAULT']['config']['driver'])) {
241  // load DBMS specifics
242  $driver = $this->handlerCfg['_DEFAULT']['config']['driver'];
243  $className = 'TYPO3\\CMS\\Dbal\\Database\\Specifics\\' . ucfirst(strtolower($driver));
244  if (class_exists($className)) {
245  if (!is_subclass_of($className, 'TYPO3\\CMS\\Dbal\\Database\\Specifics\\AbstractSpecifics')) {
246  throw new \InvalidArgumentException($className . ' must inherit from \\TYPO3\\CMS\\Dbal\\Database\\Specifics\\AbstractSpecifics', 1416919866);
247  }
248  $this->dbmsSpecifics = GeneralUtility::makeInstance($className);
249  }
250  }
251  }
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  return $this->dbmsSpecifics;
265  }
266 
270  protected function getFieldInfoCache() {
271  return GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_phpcode');
272  }
273 
279  public function clearCachedFieldInfo() {
280  $this->getFieldInfoCache()->flushByTag('t3lib_db');
281  }
282 
288  public function cacheFieldInfo() {
289  $phpCodeCache = $this->getFieldInfoCache();
290  // try to fetch cache
291  // cache is flushed when admin_query() is called
292  if ($phpCodeCache->has($this->cacheIdentifier)) {
293  $fieldInformation = $phpCodeCache->requireOnce($this->cacheIdentifier);
294  $this->cache_autoIncFields = $fieldInformation['incFields'];
295  $this->cache_fieldType = $fieldInformation['fieldTypes'];
296  $this->cache_primaryKeys = $fieldInformation['primaryKeys'];
297  } else {
298  $this->analyzeCachingTables();
299  $this->analyzeExtensionTables();
300  $completeFieldInformation = $this->getCompleteFieldInformation();
301  $phpCodeCache->set($this->cacheIdentifier, $this->getCacheableString($completeFieldInformation), array('t3lib_db'));
302  }
303  }
304 
312  protected function analyzeCachingTables() {
313  $this->parseAndAnalyzeSql(\TYPO3\CMS\Core\Cache\Cache::getDatabaseTableDefinitions());
314  }
315 
322  protected function analyzeExtensionTables() {
323  if (isset($GLOBALS['TYPO3_LOADED_EXT']) && (is_array($GLOBALS['TYPO3_LOADED_EXT']) || $GLOBALS['TYPO3_LOADED_EXT'] instanceof \ArrayAccess)) {
324  foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extensionConfiguration) {
325  $isArray = (is_array($extensionConfiguration) || $extensionConfiguration instanceof \ArrayAccess);
326  if (!$isArray || ($isArray && !isset($extensionConfiguration['ext_tables.sql']))) {
327  continue;
328  }
329  $extensionsSql = file_get_contents($extensionConfiguration['ext_tables.sql']);
330  $this->parseAndAnalyzeSql($extensionsSql);
331  }
332  }
333  }
334 
341  protected function parseAndAnalyzeSql($sql) {
342  $parsedSql = $this->installerSql->getFieldDefinitions_fileContent($sql);
343  $this->analyzeFields($parsedSql);
344  }
345 
352  protected function getCompleteFieldInformation() {
353  return array('incFields' => $this->cache_autoIncFields, 'fieldTypes' => $this->cache_fieldType, 'primaryKeys' => $this->cache_primaryKeys);
354  }
355 
363  protected function getCacheableString(array $fieldInformation) {
364  $cacheString = 'return ';
365  $cacheString .= var_export($fieldInformation, TRUE);
366  $cacheString .= ';';
367  return $cacheString;
368  }
369 
376  protected function analyzeFields($parsedExtSQL) {
377  foreach ($parsedExtSQL as $table => $tdef) {
378  // check if table is mapped
379  if (isset($this->mapping[$table])) {
380  $table = $this->mapping[$table]['mapTableName'];
381  }
382  if (is_array($tdef['fields'])) {
383  foreach ($tdef['fields'] as $field => $fdefString) {
384  $fdef = $this->SQLparser->parseFieldDef($fdefString);
385  $fieldType = isset($fdef['fieldType']) ? $fdef['fieldType'] : '';
386  $this->cache_fieldType[$table][$field]['type'] = $fieldType;
387  $this->cache_fieldType[$table][$field]['metaType'] = $this->MySQLMetaType($fieldType);
388  $this->cache_fieldType[$table][$field]['notnull'] = isset($fdef['featureIndex']['NOTNULL']) && !$this->SQLparser->checkEmptyDefaultValue($fdef['featureIndex']) ? 1 : 0;
389  if (isset($fdef['featureIndex']['DEFAULT'])) {
390  $default = $fdef['featureIndex']['DEFAULT']['value'][0];
391  if (isset($fdef['featureIndex']['DEFAULT']['value'][1])) {
392  $default = $fdef['featureIndex']['DEFAULT']['value'][1] . $default . $fdef['featureIndex']['DEFAULT']['value'][1];
393  }
394  $this->cache_fieldType[$table][$field]['default'] = $default;
395  }
396  if (isset($fdef['featureIndex']['AUTO_INCREMENT'])) {
397  $this->cache_autoIncFields[$table] = $field;
398  }
399  if (isset($tdef['keys']['PRIMARY'])) {
400  $this->cache_primaryKeys[$table] = substr($tdef['keys']['PRIMARY'], 13, -1);
401  }
402  }
403  }
404  }
405  }
406 
415  protected function mapCachedFieldInfo(array $fieldInfo) {
416  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'])) {
417  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['dbal']['mapping'] as $mappedTable => $mappedConf) {
418  if (array_key_exists($mappedTable, $fieldInfo['incFields'])) {
419  $mappedTableAlias = $mappedConf['mapTableName'];
420  if (isset($mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]])) {
421  $fieldInfo['incFields'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['incFields'][$mappedTable]];
422  } else {
423  $fieldInfo['incFields'][$mappedTableAlias] = $fieldInfo['incFields'][$mappedTable];
424  }
425  }
426  if (array_key_exists($mappedTable, $fieldInfo['fieldTypes'])) {
427  $tempMappedFieldConf = array();
428  foreach ($fieldInfo['fieldTypes'][$mappedTable] as $field => $fieldConf) {
429  $tempMappedFieldConf[$mappedConf['mapFieldNames'][$field]] = $fieldConf;
430  }
431  $fieldInfo['fieldTypes'][$mappedConf['mapTableName']] = $tempMappedFieldConf;
432  }
433  if (array_key_exists($mappedTable, $fieldInfo['primaryKeys'])) {
434  $mappedTableAlias = $mappedConf['mapTableName'];
435  if (isset($mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]])) {
436  $fieldInfo['primaryKeys'][$mappedTableAlias] = $mappedConf['mapFieldNames'][$fieldInfo['primaryKeys'][$mappedTable]];
437  } else {
438  $fieldInfo['primaryKeys'][$mappedTableAlias] = $fieldInfo['primaryKeys'][$mappedTable];
439  }
440  }
441  }
442  }
443  return $fieldInfo;
444  }
445 
446  /************************************
447  *
448  * Query Building (Overriding parent methods)
449  * These functions are extending counterparts in the parent class.
450  *
451  **************************************/
452  /*
453  * From the ADOdb documentation, this is what we do (_Execute for SELECT, _query for the other actions)Execute()
454  * is the default way to run queries. You can use the low-level functions _Execute() and _query() to reduce query overhead.
455  * Both these functions share the same parameters as Execute().If you do not have any bind parameters or your database
456  * supports binding (without emulation), then you can call _Execute() directly.
457  * Calling this function bypasses bind emulation. Debugging is still supported in _Execute().If you do not require
458  * debugging facilities nor emulated binding, and do not require a recordset to be returned, then you can call _query.
459  * This is great for inserts, updates and deletes. Calling this function bypasses emulated binding, debugging,
460  * and recordset handling. Either the resultid, TRUE or FALSE are returned by _query().
461  */
462 
473  public function exec_INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
474  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
475  // Do field mapping if needed:
476  $ORIG_tableName = $table;
477  if ($tableArray = $this->map_needMapping($table)) {
478  // Field mapping of array:
479  $fields_values = $this->map_assocArray($fields_values, $tableArray);
480  // Table name:
481  if ($this->mapping[$table]['mapTableName']) {
482  $table = $this->mapping[$table]['mapTableName'];
483  }
484  }
485  // Select API:
486  $this->lastHandlerKey = $this->handler_getFromTableList($table);
487  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
488  $sqlResult = NULL;
489  switch ($hType) {
490  case 'native':
491  $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
492  if (is_string($this->lastQuery)) {
493  $sqlResult = $this->query($this->lastQuery);
494  } else {
495  $sqlResult = $this->query($this->lastQuery[0]);
496  $new_id = $this->sql_insert_id();
497  $where = $this->cache_autoIncFields[$table] . '=' . $new_id;
498  foreach ($this->lastQuery[1] as $field => $content) {
499  $stmt = 'UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table, TRUE) . ' WHERE ' . $this->quoteWhereClause($where);
500  $this->query($stmt);
501  }
502  }
503  break;
504  case 'adodb':
505  // auto generate ID for auto_increment fields if not present (static import needs this!)
506  // should we check the table name here (static_*)?
507  if (isset($this->cache_autoIncFields[$table])) {
508  if (isset($fields_values[$this->cache_autoIncFields[$table]])) {
509  $new_id = $fields_values[$this->cache_autoIncFields[$table]];
510  if ($table != 'tx_dbal_debuglog') {
511  $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
512  }
513  } elseif (!$this->handlerInstance[$this->lastHandlerKey]->hasInsertID) {
514  // The table does not support auto-incremented fields, fall back to
515  // using a sequence table to simulate the auto-increment
516  $new_id = $this->handlerInstance[$this->lastHandlerKey]->GenID($table . '_' . $this->cache_autoIncFields[$table], $this->handlerInstance[$this->lastHandlerKey]->sequenceStart);
517  $fields_values[$this->cache_autoIncFields[$table]] = $new_id;
518  if ($table != 'tx_dbal_debuglog') {
519  $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
520  }
521  }
522  }
523  $this->lastQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
524  if (is_string($this->lastQuery)) {
525  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
526  } else {
527  $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
528  if (strlen($this->lastQuery[0])) {
529  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE);
530  if ($this->handlerInstance[$this->lastHandlerKey]->hasInsertID) {
531  // The table is able to retrieve the ID of the last insert, use it to update the blob below
532  $new_id = $this->handlerInstance[$this->lastHandlerKey]->Insert_ID($table, $this->cache_autoIncFields[$table]);
533  if ($table !== 'tx_dbal_debuglog') {
534  $this->handlerInstance[$this->lastHandlerKey]->last_insert_id = $new_id;
535  }
536  }
537  }
538  if (is_array($this->lastQuery[1])) {
539  foreach ($this->lastQuery[1] as $field => $content) {
540  if (empty($content)) {
541  continue;
542  }
543  if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
544  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id));
545  } elseif (isset($this->cache_primaryKeys[$table])) {
546  $where = '';
547  $pks = explode(',', $this->cache_primaryKeys[$table]);
548  foreach ($pks as $pk) {
549  if (isset($fields_values[$pk])) {
550  $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND ';
551  }
552  }
553  $where = $this->quoteWhereClause($where . '1=1');
554  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $where);
555  } else {
556  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE);
557  // Should never ever happen
558  throw new \RuntimeException('Could not update BLOB >>>> no WHERE clause found!', 1321860519);
559  }
560  }
561  }
562  if (is_array($this->lastQuery[2])) {
563  foreach ($this->lastQuery[2] as $field => $content) {
564  if (empty($content)) {
565  continue;
566  }
567  if (isset($this->cache_autoIncFields[$table]) && isset($new_id)) {
568  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($this->cache_autoIncFields[$table] . '=' . $new_id));
569  } elseif (isset($this->cache_primaryKeys[$table])) {
570  $where = '';
571  $pks = explode(',', $this->cache_primaryKeys[$table]);
572  foreach ($pks as $pk) {
573  if (isset($fields_values[$pk])) {
574  $where .= $pk . '=' . $this->fullQuoteStr($fields_values[$pk], $table) . ' AND ';
575  }
576  }
577  $where = $this->quoteWhereClause($where . '1=1');
578  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $where);
579  } else {
580  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans(FALSE);
581  // Should never ever happen
582  throw new \RuntimeException('Could not update CLOB >>>> no WHERE clause found!', 1310027337);
583  }
584  }
585  }
586  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
587  }
588  break;
589  case 'userdefined':
590  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_INSERTquery($table, $fields_values, $no_quote_fields);
591  break;
592  }
593  if ($this->printErrors && $this->sql_error()) {
594  debug(array($this->lastQuery, $this->sql_error()));
595  }
596  if ($this->debug) {
597  $this->debugHandler('exec_INSERTquery', GeneralUtility::milliseconds() - $pt, array(
598  'handlerType' => $hType,
599  'args' => array($table, $fields_values),
600  'ORIG_tablename' => $ORIG_tableName
601  ));
602  }
603  foreach ($this->postProcessHookObjects as $hookObject) {
604  $hookObject->exec_INSERTquery_postProcessAction($table, $fields_values, $no_quote_fields, $this);
605  }
606  // Return output:
607  return $sqlResult;
608  }
609 
619  public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
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 = array();
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  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
651  // Do table/field mapping:
652  $ORIG_tableName = $table;
653  if ($tableArray = $this->map_needMapping($table)) {
654  // Field mapping of array:
655  $fields_values = $this->map_assocArray($fields_values, $tableArray);
656  // Where clause table and field mapping:
657  $whereParts = $this->SQLparser->parseWhereClause($where);
658  $this->map_sqlParts($whereParts, $tableArray[0]['table']);
659  $where = $this->SQLparser->compileWhereClause($whereParts, FALSE);
660  // Table name:
661  if ($this->mapping[$table]['mapTableName']) {
662  $table = $this->mapping[$table]['mapTableName'];
663  }
664  }
665  // Select API
666  $this->lastHandlerKey = $this->handler_getFromTableList($table);
667  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
668  $sqlResult = NULL;
669  switch ($hType) {
670  case 'native':
671  $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields);
672  if (is_string($this->lastQuery)) {
673  $sqlResult = $this->query($this->lastQuery);
674  } else {
675  $sqlResult = $this->query($this->lastQuery[0]);
676  foreach ($this->lastQuery[1] as $field => $content) {
677  $stmt = 'UPDATE ' . $this->quoteFromTables($table) . ' SET ' . $this->quoteFromTables($field) . '=' . $this->fullQuoteStr($content, $table, TRUE) . ' WHERE ' . $this->quoteWhereClause($where);
678  $this->query($stmt);
679  }
680  }
681  break;
682  case 'adodb':
683  $this->lastQuery = $this->UPDATEquery($table, $where, $fields_values, $no_quote_fields);
684  if (is_string($this->lastQuery)) {
685  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
686  } else {
687  $this->handlerInstance[$this->lastHandlerKey]->StartTrans();
688  if (strlen($this->lastQuery[0])) {
689  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery[0], FALSE);
690  }
691  if (is_array($this->lastQuery[1])) {
692  foreach ($this->lastQuery[1] as $field => $content) {
693  $this->handlerInstance[$this->lastHandlerKey]->UpdateBlob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where));
694  }
695  }
696  if (is_array($this->lastQuery[2])) {
697  foreach ($this->lastQuery[2] as $field => $content) {
698  $this->handlerInstance[$this->lastHandlerKey]->UpdateClob($this->quoteFromTables($table), $field, $content, $this->quoteWhereClause($where));
699  }
700  }
701  $this->handlerInstance[$this->lastHandlerKey]->CompleteTrans();
702  }
703  break;
704  case 'userdefined':
705  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields);
706  break;
707  }
708  if ($this->printErrors && $this->sql_error()) {
709  debug(array($this->lastQuery, $this->sql_error()));
710  }
711  if ($this->debug) {
712  $this->debugHandler('exec_UPDATEquery', GeneralUtility::milliseconds() - $pt, array(
713  'handlerType' => $hType,
714  'args' => array($table, $where, $fields_values),
715  'ORIG_from_table' => $ORIG_tableName
716  ));
717  }
718  foreach ($this->postProcessHookObjects as $hookObject) {
719  $hookObject->exec_UPDATEquery_postProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
720  }
721  // Return result:
722  return $sqlResult;
723  }
724 
732  public function exec_DELETEquery($table, $where) {
733  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
734  // Do table/field mapping:
735  $ORIG_tableName = $table;
736  if ($tableArray = $this->map_needMapping($table)) {
737  // Where clause:
738  $whereParts = $this->SQLparser->parseWhereClause($where);
739  $this->map_sqlParts($whereParts, $tableArray[0]['table']);
740  $where = $this->SQLparser->compileWhereClause($whereParts, FALSE);
741  // Table name:
742  if ($this->mapping[$table]['mapTableName']) {
743  $table = $this->mapping[$table]['mapTableName'];
744  }
745  }
746  // Select API
747  $this->lastHandlerKey = $this->handler_getFromTableList($table);
748  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
749  $sqlResult = NULL;
750  switch ($hType) {
751  case 'native':
752  $this->lastQuery = $this->DELETEquery($table, $where);
753  $sqlResult = $this->query($this->lastQuery);
754  break;
755  case 'adodb':
756  $this->lastQuery = $this->DELETEquery($table, $where);
757  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
758  break;
759  case 'userdefined':
760  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_DELETEquery($table, $where);
761  break;
762  }
763  if ($this->printErrors && $this->sql_error()) {
764  debug(array($this->lastQuery, $this->sql_error()));
765  }
766  if ($this->debug) {
767  $this->debugHandler('exec_DELETEquery', GeneralUtility::milliseconds() - $pt, array(
768  'handlerType' => $hType,
769  'args' => array($table, $where),
770  'ORIG_from_table' => $ORIG_tableName
771  ));
772  }
773  foreach ($this->postProcessHookObjects as $hookObject) {
774  $hookObject->exec_DELETEquery_postProcessAction($table, $where, $this);
775  }
776  // Return result:
777  return $sqlResult;
778  }
779 
792  public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
793  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
794  // Map table / field names if needed:
795  $ORIG_tableName = $from_table;
796  // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
797  $parsedFromTable = array();
798  $remappedParameters = array();
799  if ($tableArray = $this->map_needMapping($ORIG_tableName, FALSE, $parsedFromTable)) {
800  $from = $parsedFromTable ? $parsedFromTable : $from_table;
801  $remappedParameters = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
802  }
803  // Get handler key and select API:
804  if (count($remappedParameters) > 0) {
805  $mappedQueryParts = $this->compileSelectParameters($remappedParameters);
806  $fromTable = $mappedQueryParts[1];
807  } else {
808  $fromTable = $from_table;
809  }
810  $this->lastHandlerKey = $this->handler_getFromTableList($fromTable);
811  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
812  $sqlResult = NULL;
813  switch ($hType) {
814  case 'native':
815  if (count($remappedParameters) > 0) {
816  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
817  }
818  $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
819  $sqlResult = $this->query($this->lastQuery);
820  $this->resourceIdToTableNameMap[serialize($sqlResult)] = $ORIG_tableName;
821  break;
822  case 'adodb':
823  if ($limit != '') {
824  $splitLimit = GeneralUtility::intExplode(',', $limit);
825  // Splitting the limit values:
826  if ($splitLimit[1]) {
827  // If there are two parameters, do mapping differently than otherwise:
828  $numrows = $splitLimit[1];
829  $offset = $splitLimit[0];
830  } else {
831  $numrows = $splitLimit[0];
832  $offset = 0;
833  }
834  if (count($remappedParameters) > 0) {
835  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTqueryFromArray($remappedParameters), $numrows, $offset);
836  } else {
837  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit($this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy), $numrows, $offset);
838  }
839  $this->lastQuery = $sqlResult->sql;
840  } else {
841  if (count($remappedParameters) > 0) {
842  $this->lastQuery = $this->SELECTqueryFromArray($remappedParameters);
843  } else {
844  $this->lastQuery = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
845  }
846  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_Execute($this->lastQuery);
847  }
848  if (!is_object($sqlResult)) {
849  debug(array($this->lastQuery, $this->sql_error()));
850  }
851  $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
852  // Setting handler type in result object (for later recognition!)
853  $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
854  break;
855  case 'userdefined':
856  if (count($remappedParameters) > 0) {
857  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($remappedParameters);
858  }
859  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
860  if (is_object($sqlResult)) {
861  $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
862  // Setting handler type in result object (for later recognition!)
863  $sqlResult->TYPO3_DBAL_tableList = $ORIG_tableName;
864  }
865  break;
866  }
867  if ($this->printErrors && $this->sql_error()) {
868  debug(array($this->lastQuery, $this->sql_error()));
869  }
870  if ($this->debug) {
871  $data = array(
872  'handlerType' => $hType,
873  'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit),
874  'ORIG_from_table' => $ORIG_tableName
875  );
876  if ($this->conf['debugOptions']['numberRows']) {
877  $data['numberRows'] = $this->sql_num_rows($sqlResult);
878  }
879  $this->debugHandler('exec_SELECTquery', GeneralUtility::milliseconds() - $pt, $data);
880  }
881  // Return handler.
882  return $sqlResult;
883  }
884 
891  public function exec_TRUNCATEquery($table) {
892  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
893  // Do table/field mapping:
894  $ORIG_tableName = $table;
895  if ($tableArray = $this->map_needMapping($table)) {
896  // Table name:
897  if ($this->mapping[$table]['mapTableName']) {
898  $table = $this->mapping[$table]['mapTableName'];
899  }
900  }
901  // Select API
902  $this->lastHandlerKey = $this->handler_getFromTableList($table);
903  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
904  $sqlResult = NULL;
905  switch ($hType) {
906  case 'native':
907  $this->lastQuery = $this->TRUNCATEquery($table);
908  $sqlResult = $this->query($this->lastQuery);
909  break;
910  case 'adodb':
911  $this->lastQuery = $this->TRUNCATEquery($table);
912  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->_query($this->lastQuery, FALSE);
913  break;
914  case 'userdefined':
915  $sqlResult = $this->handlerInstance[$this->lastHandlerKey]->exec_TRUNCATEquery($table);
916  break;
917  }
918  if ($this->printErrors && $this->sql_error()) {
919  debug(array($this->lastQuery, $this->sql_error()));
920  }
921  if ($this->debug) {
922  $this->debugHandler('exec_TRUNCATEquery', GeneralUtility::milliseconds() - $pt, array(
923  'handlerType' => $hType,
924  'args' => array($table),
925  'ORIG_from_table' => $ORIG_tableName
926  ));
927  }
928  foreach ($this->postProcessHookObjects as $hookObject) {
929  $hookObject->exec_TRUNCATEquery_postProcessAction($table, $this);
930  }
931  // Return result:
932  return $sqlResult;
933  }
934 
943  protected function exec_query(array $queryParts) {
944  switch ($queryParts['type']) {
945  case 'SELECT':
946  $selectFields = $this->SQLparser->compileFieldList($queryParts['SELECT']);
947  $fromTables = $this->SQLparser->compileFromTables($queryParts['FROM']);
948  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
949  $groupBy = isset($queryParts['GROUPBY']) ? $this->SQLparser->compileFieldList($queryParts['GROUPBY']) : '';
950  $orderBy = isset($queryParts['ORDERBY']) ? $this->SQLparser->compileFieldList($queryParts['ORDERBY']) : '';
951  $limit = isset($queryParts['LIMIT']) ? $queryParts['LIMIT'] : '';
952  return $this->exec_SELECTquery($selectFields, $fromTables, $whereClause, $groupBy, $orderBy, $limit);
953  case 'UPDATE':
954  $table = $queryParts['TABLE'];
955  $fields = array();
956  foreach ($queryParts['FIELDS'] as $fN => $fV) {
957  $fields[$fN] = $fV[0];
958  }
959  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
960  return $this->exec_UPDATEquery($table, $whereClause, $fields);
961  case 'INSERT':
962  $table = $queryParts['TABLE'];
963  $values = array();
964  if (isset($queryParts['VALUES_ONLY']) && is_array($queryParts['VALUES_ONLY'])) {
965  $fields = $GLOBALS['TYPO3_DB']->cache_fieldType[$table];
966  $fc = 0;
967  foreach ($fields as $fn => $fd) {
968  $values[$fn] = $queryParts['VALUES_ONLY'][$fc++][0];
969  }
970  } else {
971  foreach ($queryParts['FIELDS'] as $fN => $fV) {
972  $values[$fN] = $fV[0];
973  }
974  }
975  return $this->exec_INSERTquery($table, $values);
976  case 'DELETE':
977  $table = $queryParts['TABLE'];
978  $whereClause = isset($queryParts['WHERE']) ? $this->SQLparser->compileWhereClause($queryParts['WHERE']) : '1=1';
979  return $this->exec_DELETEquery($table, $whereClause);
980  case 'TRUNCATETABLE':
981  $table = $queryParts['TABLE'];
982  return $this->exec_TRUNCATEquery($table);
983  default:
984  return NULL;
985  }
986  }
987 
995  protected function query($query) {
996  if (!$this->isConnected()) {
997  $this->connectDB();
998  }
999  return $this->handlerInstance[$this->lastHandlerKey]['link']->query($query);
1000  }
1001 
1002  /**************************************
1003  *
1004  * Query building
1005  *
1006  **************************************/
1015  public function INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
1016  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
1017  if (!is_array($fields_values) || count($fields_values) === 0) {
1018  return '';
1019  }
1020  foreach ($this->preProcessHookObjects as $hookObject) {
1021  $hookObject->INSERTquery_preProcessAction($table, $fields_values, $no_quote_fields, $this);
1022  }
1023  if (is_string($no_quote_fields)) {
1024  $no_quote_fields = explode(',', $no_quote_fields);
1025  } elseif (!is_array($no_quote_fields)) {
1026  $no_quote_fields = array();
1027  }
1028  $blobFields = $clobFields = array();
1029  $nArr = array();
1030  $handlerKey = $this->handler_getFromTableList($table);
1031  $quoteClob = isset($this->handlerCfg[$handlerKey]['config']['quoteClob']) ? $this->handlerCfg[$handlerKey]['config']['quoteClob'] : FALSE;
1032  foreach ($fields_values as $k => $v) {
1033  if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
1034  // we skip the field in the regular INSERT statement, it is only in blobfields
1035  $blobFields[$this->quoteFieldNames($k)] = $v;
1036  } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
1037  // we skip the field in the regular INSERT statement, it is only in clobfields
1038  $clobFields[$this->quoteFieldNames($k)] = $quoteClob ? $this->quoteStr($v, $table) : $v;
1039  } else {
1040  // Add slashes old-school:
1041  // cast numerical values
1042  $mt = $this->sql_field_metatype($table, $k);
1043  if ($mt[0] == 'I') {
1044  $v = (int)$v;
1045  } elseif ($mt[0] == 'F') {
1046  $v = (double) $v;
1047  }
1048  $nArr[$this->quoteFieldNames($k)] = !in_array($k, $no_quote_fields) ? $this->fullQuoteStr($v, $table, TRUE) : $v;
1049  }
1050  }
1051  if (count($blobFields) || count($clobFields)) {
1052  $query = array();
1053  if (count($nArr)) {
1054  $query[0] = 'INSERT INTO ' . $this->quoteFromTables($table) . '
1055  (
1056  ' . implode(',
1057  ', array_keys($nArr)) . '
1058  ) VALUES (
1059  ' . implode(',
1060  ', $nArr) . '
1061  )';
1062  }
1063  if (count($blobFields)) {
1064  $query[1] = $blobFields;
1065  }
1066  if (count($clobFields)) {
1067  $query[2] = $clobFields;
1068  }
1069  if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
1070  $this->debug_lastBuiltQuery = $query[0];
1071  }
1072  } else {
1073  $query = 'INSERT INTO ' . $this->quoteFromTables($table) . '
1074  (
1075  ' . implode(',
1076  ', array_keys($nArr)) . '
1077  ) VALUES (
1078  ' . implode(',
1079  ', $nArr) . '
1080  )';
1081  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1082  $this->debug_lastBuiltQuery = $query;
1083  }
1084  }
1085  return $query;
1086  }
1087 
1097  public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
1098  if ((string) $this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
1099  return parent::INSERTmultipleRows($table, $fields, $rows, $no_quote_fields);
1100  }
1101  $result = array();
1102  foreach ($rows as $row) {
1103  $fields_values = array();
1104  foreach ($fields as $key => $value) {
1105  $fields_values[$value] = $row[$key];
1106  }
1107  $rowQuery = $this->INSERTquery($table, $fields_values, $no_quote_fields);
1108  if (is_array($rowQuery)) {
1109  $result[] = $rowQuery;
1110  } else {
1111  $result[][0] = $rowQuery;
1112  }
1113  }
1114  return $result;
1115  }
1116 
1128  public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
1129  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure).
1130  if (is_string($where)) {
1131  foreach ($this->preProcessHookObjects as $hookObject) {
1132  $hookObject->UPDATEquery_preProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
1133  }
1134  $blobFields = $clobFields = array();
1135  $nArr = array();
1136  if (is_array($fields_values) && count($fields_values)) {
1137  if (is_string($no_quote_fields)) {
1138  $no_quote_fields = explode(',', $no_quote_fields);
1139  } elseif (!is_array($no_quote_fields)) {
1140  $no_quote_fields = array();
1141  }
1142  $handlerKey = $this->handler_getFromTableList($table);
1143  $quoteClob = isset($this->handlerCfg[$handlerKey]['config']['quoteClob']) ? $this->handlerCfg[$handlerKey]['config']['quoteClob'] : FALSE;
1144  foreach ($fields_values as $k => $v) {
1145  if (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'B') {
1146  // we skip the field in the regular UPDATE statement, it is only in blobfields
1147  $blobFields[$this->quoteFieldNames($k)] = $v;
1148  } elseif (!$this->runningNative() && $this->sql_field_metatype($table, $k) == 'XL') {
1149  // we skip the field in the regular UPDATE statement, it is only in clobfields
1150  $clobFields[$this->quoteFieldNames($k)] = $quoteClob ? $this->quoteStr($v, $table) : $v;
1151  } else {
1152  // Add slashes old-school:
1153  // cast numeric values
1154  $mt = $this->sql_field_metatype($table, $k);
1155  if ($mt[0] == 'I') {
1156  $v = (int)$v;
1157  } elseif ($mt[0] == 'F') {
1158  $v = (double) $v;
1159  }
1160  $nArr[] = $this->quoteFieldNames($k) . '=' . (!in_array($k, $no_quote_fields) ? $this->fullQuoteStr($v, $table, TRUE) : $v);
1161  }
1162  }
1163  }
1164  if (count($blobFields) || count($clobFields)) {
1165  $query = array();
1166  if (count($nArr)) {
1167  $query[0] = 'UPDATE ' . $this->quoteFromTables($table) . '
1168  SET
1169  ' . implode(',
1170  ', $nArr) . (strlen($where) > 0 ? '
1171  WHERE
1172  ' . $this->quoteWhereClause($where) : '');
1173  }
1174  if (count($blobFields)) {
1175  $query[1] = $blobFields;
1176  }
1177  if (count($clobFields)) {
1178  $query[2] = $clobFields;
1179  }
1180  if (isset($query[0]) && ($this->debugOutput || $this->store_lastBuiltQuery)) {
1181  $this->debug_lastBuiltQuery = $query[0];
1182  }
1183  } else {
1184  $query = 'UPDATE ' . $this->quoteFromTables($table) . '
1185  SET
1186  ' . implode(',
1187  ', $nArr) . (strlen($where) > 0 ? '
1188  WHERE
1189  ' . $this->quoteWhereClause($where) : '');
1190  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1191  $this->debug_lastBuiltQuery = $query;
1192  }
1193  }
1194  return $query;
1195  } else {
1196  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 1270853887);
1197  }
1198  }
1199 
1208  public function DELETEquery($table, $where) {
1209  if (is_string($where)) {
1210  foreach ($this->preProcessHookObjects as $hookObject) {
1211  $hookObject->DELETEquery_preProcessAction($table, $where, $this);
1212  }
1213  $table = $this->quoteFromTables($table);
1214  $where = $this->quoteWhereClause($where);
1215  $query = 'DELETE FROM ' . $table . (strlen($where) > 0 ? ' WHERE ' . $where : '');
1216  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1217  $this->debug_lastBuiltQuery = $query;
1218  }
1219  return $query;
1220  } else {
1221  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !', 1310027383);
1222  }
1223  }
1224 
1236  public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
1237  $this->lastHandlerKey = $this->handler_getFromTableList($from_table);
1238  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
1239  if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) {
1240  // Possibly rewrite the LIMIT to be PostgreSQL-compatible
1241  $splitLimit = GeneralUtility::intExplode(',', $limit);
1242  // Splitting the limit values:
1243  if ($splitLimit[1]) {
1244  // If there are two parameters, do mapping differently than otherwise:
1245  $numrows = $splitLimit[1];
1246  $offset = $splitLimit[0];
1247  $limit = $numrows . ' OFFSET ' . $offset;
1248  }
1249  }
1250  $select_fields = $this->quoteFieldNames($select_fields);
1251  $from_table = $this->quoteFromTables($from_table);
1252  $where_clause = $this->quoteWhereClause($where_clause);
1253  $groupBy = $this->quoteGroupBy($groupBy);
1254  $orderBy = $this->quoteOrderBy($orderBy);
1255  // Call parent method to build actual query
1256  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1257  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1258  $this->debug_lastBuiltQuery = $query;
1259  }
1260  return $query;
1261  }
1262 
1269  protected function SELECTqueryFromArray(array $params) {
1270  // $select_fields
1271  $params[0] = $this->_quoteFieldNames($params[0]);
1272  // $from_table
1273  $params[1] = $this->_quoteFromTables($params[1]);
1274  // $where_clause
1275  if (count($params[2]) > 0) {
1276  $params[2] = $this->_quoteWhereClause($params[2]);
1277  }
1278  // $group_by
1279  if (count($params[3]) > 0) {
1280  $params[3] = $this->_quoteGroupBy($params[3]);
1281  }
1282  // $order_by
1283  if (count($params[4]) > 0) {
1284  $params[4] = $this->_quoteOrderBy($params[4]);
1285  }
1286  // Compile the SELECT parameters
1287  list($select_fields, $from_table, $where_clause, $groupBy, $orderBy) = $this->compileSelectParameters($params);
1288  // Call parent method to build actual query
1289  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1290  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1291  $this->debug_lastBuiltQuery = $query;
1292  }
1293  return $query;
1294  }
1295 
1303  protected function compileSelectParameters(array $params) {
1304  $select_fields = $this->SQLparser->compileFieldList($params[0]);
1305  $from_table = $this->SQLparser->compileFromTables($params[1]);
1306  $where_clause = count($params[2]) > 0 ? $this->SQLparser->compileWhereClause($params[2]) : '';
1307  $groupBy = count($params[3]) > 0 ? $this->SQLparser->compileFieldList($params[3]) : '';
1308  $orderBy = count($params[4]) > 0 ? $this->SQLparser->compileFieldList($params[4]) : '';
1309  return array($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1310  }
1311 
1318  public function TRUNCATEquery($table) {
1319  foreach ($this->preProcessHookObjects as $hookObject) {
1320  $hookObject->TRUNCATEquery_preProcessAction($table, $this);
1321  }
1322  $table = $this->quoteFromTables($table);
1323  // Build actual query
1324  $query = 'TRUNCATE TABLE ' . $table;
1325  if ($this->debugOutput || $this->store_lastBuiltQuery) {
1326  $this->debug_lastBuiltQuery = $query;
1327  }
1328  return $query;
1329  }
1330 
1331  /**************************************
1332  *
1333  * Prepared Query Support
1334  *
1335  **************************************/
1348  public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) {
1349  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
1350  $precompiledParts = array();
1351  if ($this->queryCache) {
1352  $cacheKey = 'prepare_SELECTquery-' . \TYPO3\CMS\Dbal\QueryCache::getCacheKey(array(
1353  'selectFields' => $select_fields,
1354  'fromTable' => $from_table,
1355  'whereClause' => $where_clause,
1356  'groupBy' => $groupBy,
1357  'orderBy' => $orderBy,
1358  'limit' => $limit
1359  ));
1360  if ($this->queryCache->has($cacheKey)) {
1361  $precompiledParts = $this->queryCache->get($cacheKey);
1362  if ($this->debug) {
1363  $data = array(
1364  'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters),
1365  'precompiledParts' => $precompiledParts
1366  );
1367  $this->debugHandler('prepare_SELECTquery (cache hit)', GeneralUtility::milliseconds() - $pt, $data);
1368  }
1369  }
1370  }
1371  $ORIG_tableName = '';
1372  if (count($precompiledParts) == 0) {
1373  // Map table / field names if needed:
1374  $ORIG_tableName = $from_table;
1375  // Saving table names in $ORIG_from_table since $from_table is transformed beneath:
1376  $parsedFromTable = array();
1377  $queryComponents = array();
1378  if ($tableArray = $this->map_needMapping($ORIG_tableName, FALSE, $parsedFromTable)) {
1379  $from = $parsedFromTable ? $parsedFromTable : $from_table;
1380  $components = $this->map_remapSELECTQueryParts($select_fields, $from, $where_clause, $groupBy, $orderBy);
1381  $queryComponents['SELECT'] = $components[0];
1382  $queryComponents['FROM'] = $components[1];
1383  $queryComponents['WHERE'] = $components[2];
1384  $queryComponents['GROUPBY'] = $components[3];
1385  $queryComponents['ORDERBY'] = $components[4];
1386  $queryComponents['parameters'] = $components[5];
1387  } else {
1388  $queryComponents = $this->getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1389  }
1390  $queryComponents['ORIG_tableName'] = $ORIG_tableName;
1391  if (!$this->runningNative()) {
1392  // Quotes all fields
1393  $queryComponents['SELECT'] = $this->_quoteFieldNames($queryComponents['SELECT']);
1394  $queryComponents['FROM'] = $this->_quoteFromTables($queryComponents['FROM']);
1395  $queryComponents['WHERE'] = $this->_quoteWhereClause($queryComponents['WHERE']);
1396  $queryComponents['GROUPBY'] = $this->_quoteGroupBy($queryComponents['GROUPBY']);
1397  $queryComponents['ORDERBY'] = $this->_quoteOrderBy($queryComponents['ORDERBY']);
1398  }
1399  $precompiledParts = $this->precompileSELECTquery($queryComponents);
1400  if ($this->queryCache) {
1401  try {
1402  $this->queryCache->set($cacheKey, $precompiledParts);
1403  } catch (\TYPO3\CMS\Core\Cache\Exception $e) {
1404  if ($this->debug) {
1405  GeneralUtility::devLog($e->getMessage(), 'dbal', 1);
1406  }
1407  }
1408  }
1409  }
1410  $preparedStatement = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\PreparedStatement', '', $from_table, $precompiledParts);
1411  /* @var $preparedStatement \TYPO3\CMS\Core\Database\PreparedStatement */
1412  // Bind values to parameters
1413  foreach ($input_parameters as $key => $value) {
1414  $preparedStatement->bindValue($key, $value, \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE);
1415  }
1416  if ($this->debug) {
1417  $data = array(
1418  'args' => array($from_table, $select_fields, $where_clause, $groupBy, $orderBy, $limit, $input_parameters),
1419  'ORIG_from_table' => $ORIG_tableName
1420  );
1421  $this->debugHandler('prepare_SELECTquery', GeneralUtility::milliseconds() - $pt, $data);
1422  }
1423  // Return prepared statement
1424  return $preparedStatement;
1425  }
1426 
1439  protected function getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit) {
1440  $queryComponents = array(
1441  'SELECT' => '',
1442  'FROM' => '',
1443  'WHERE' => '',
1444  'GROUPBY' => '',
1445  'ORDERBY' => '',
1446  'LIMIT' => '',
1447  'parameters' => array()
1448  );
1449  $this->lastHandlerKey = $this->handler_getFromTableList($from_table);
1450  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
1451  if ($hType === 'adodb' && $this->runningADOdbDriver('postgres')) {
1452  // Possibly rewrite the LIMIT to be PostgreSQL-compatible
1453  $splitLimit = GeneralUtility::intExplode(',', $limit);
1454  // Splitting the limit values:
1455  if ($splitLimit[1]) {
1456  // If there are two parameters, do mapping differently than otherwise:
1457  $numrows = $splitLimit[1];
1458  $offset = $splitLimit[0];
1459  $limit = $numrows . ' OFFSET ' . $offset;
1460  }
1461  }
1462  $queryComponents['LIMIT'] = $limit;
1463  $queryComponents['SELECT'] = $this->SQLparser->parseFieldList($select_fields);
1464  if ($this->SQLparser->parse_error) {
1465  throw new \InvalidArgumentException($this->SQLparser->parse_error, 1310027408);
1466  }
1467  $queryComponents['FROM'] = $this->SQLparser->parseFromTables($from_table);
1468  $queryComponents['WHERE'] = $this->SQLparser->parseWhereClause($where_clause, '', $queryComponents['parameters']);
1469  if (!is_array($queryComponents['WHERE'])) {
1470  throw new \InvalidArgumentException('Could not parse where clause', 1310027427);
1471  }
1472  $queryComponents['GROUPBY'] = $this->SQLparser->parseFieldList($groupBy);
1473  $queryComponents['ORDERBY'] = $this->SQLparser->parseFieldList($orderBy);
1474  // Return the query components
1475  return $queryComponents;
1476  }
1477 
1484  protected function precompileSELECTquery(array $components) {
1485  $parameterWrap = '__' . dechex(time()) . '__';
1486  foreach ($components['parameters'] as $key => $params) {
1487  if ($key === '?') {
1488  foreach ($params as $index => $param) {
1489  $components['parameters'][$key][$index][0] = $parameterWrap . $param[0] . $parameterWrap;
1490  }
1491  } else {
1492  $components['parameters'][$key][0] = $parameterWrap . $params[0] . $parameterWrap;
1493  }
1494  }
1495  $select_fields = $this->SQLparser->compileFieldList($components['SELECT']);
1496  $from_table = $this->SQLparser->compileFromTables($components['FROM']);
1497  $where_clause = $this->SQLparser->compileWhereClause($components['WHERE']);
1498  $groupBy = $this->SQLparser->compileFieldList($components['GROUPBY']);
1499  $orderBy = $this->SQLparser->compileFieldList($components['ORDERBY']);
1500  $limit = $components['LIMIT'];
1501  $precompiledParts = array();
1502  $this->lastHandlerKey = $this->handler_getFromTableList($components['ORIG_tableName']);
1503  $hType = (string) $this->handlerCfg[$this->lastHandlerKey]['type'];
1504  $precompiledParts['handler'] = $hType;
1505  $precompiledParts['ORIG_tableName'] = $components['ORIG_tableName'];
1506  switch ($hType) {
1507  case 'native':
1508  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
1509  $precompiledParts['queryParts'] = explode($parameterWrap, $query);
1510  break;
1511  case 'adodb':
1512  $query = parent::SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy);
1513  $precompiledParts['queryParts'] = explode($parameterWrap, $query);
1514  $precompiledParts['LIMIT'] = $limit;
1515  break;
1516  case 'userdefined':
1517  $precompiledParts['queryParts'] = array(
1518  'SELECT' => $select_fields,
1519  'FROM' => $from_table,
1520  'WHERE' => $where_clause,
1521  'GROUPBY' => $groupBy,
1522  'ORDERBY' => $orderBy,
1523  'LIMIT' => $limit
1524  );
1525  break;
1526  }
1527  return $precompiledParts;
1528  }
1529 
1539  public function prepare_PREPAREDquery($query, array $queryComponents) {
1540  $pt = $this->debug ? GeneralUtility::milliseconds() : 0;
1541  // Get handler key and select API:
1542  $preparedStatement = NULL;
1543  switch ($queryComponents['handler']) {
1544  case 'native':
1545  $this->lastQuery = $query;
1546  $preparedStatement = parent::prepare_PREPAREDquery($this->lastQuery, $queryComponents);
1547  $this->resourceIdToTableNameMap[serialize($preparedStatement)] = $queryComponents['ORIG_tableName'];
1548  break;
1549  case 'adodb':
1551  $preparedStatement = GeneralUtility::makeInstance('TYPO3\\CMS\\Dbal\\Database\\AdodbPreparedStatement', $query, $queryComponents, $this);
1552  if (!$preparedStatement->prepare()) {
1553  $preparedStatement = FALSE;
1554  }
1555  break;
1556  case 'userdefined':
1557  throw new \RuntimeException('prepare_PREPAREDquery is not implemented for userdefined handlers', 1394620167);
1558  /*
1559  $queryParts = $queryComponents['queryParts'];
1560  $preparedStatement = $this->handlerInstance[$this->lastHandlerKey]->exec_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
1561  if (is_object($preparedStatement)) {
1562  $preparedStatement->TYPO3_DBAL_handlerType = 'userdefined';
1563  // Setting handler type in result object (for later recognition!)
1564  $preparedStatement->TYPO3_DBAL_tableList = $queryComponents['ORIG_tableName'];
1565  }
1566  break;
1567  */
1568  }
1569  if ($this->printErrors && $this->sql_error()) {
1570  debug(array($this->lastQuery, $this->sql_error()));
1571  }
1572  if ($this->debug) {
1573  $data = array(
1574  'handlerType' => $queryComponents['handler'],
1575  'args' => $queryComponents,
1576  'ORIG_from_table' => $queryComponents['ORIG_tableName']
1577  );
1578  $this->debugHandler('prepare_PREPAREDquery', GeneralUtility::milliseconds() - $pt, $data);
1579  }
1580  // Return result handler.
1581  return $preparedStatement;
1582  }
1583 
1584  /**************************************
1585  *
1586  * Functions for quoting table/field names
1587  *
1588  **************************************/
1595  protected function quoteSELECTsubquery(array $components) {
1596  $components['SELECT'] = $this->_quoteFieldNames($components['SELECT']);
1597  $components['FROM'] = $this->_quoteFromTables($components['FROM']);
1598  $components['WHERE'] = $this->_quoteWhereClause($components['WHERE']);
1599  return $components;
1600  }
1601 
1609  public function quoteFieldNames($select_fields) {
1610  if ($select_fields == '') {
1611  return '';
1612  }
1613  if ($this->runningNative()) {
1614  return $select_fields;
1615  }
1616  $select_fields = $this->SQLparser->parseFieldList($select_fields);
1617  if ($this->SQLparser->parse_error) {
1618  throw new \InvalidArgumentException($this->SQLparser->parse_error, 1310027490);
1619  }
1620  $select_fields = $this->_quoteFieldNames($select_fields);
1621  return $this->SQLparser->compileFieldList($select_fields);
1622  }
1623 
1631  protected function _quoteFieldNames(array $select_fields) {
1632  foreach ($select_fields as $k => $v) {
1633  if ($select_fields[$k]['field'] != '' && $select_fields[$k]['field'] != '*' && !is_numeric($select_fields[$k]['field'])) {
1634  $select_fields[$k]['field'] = $this->quoteName($select_fields[$k]['field']);
1635  }
1636  if ($select_fields[$k]['table'] != '' && !is_numeric($select_fields[$k]['table'])) {
1637  $select_fields[$k]['table'] = $this->quoteName($select_fields[$k]['table']);
1638  }
1639  if ($select_fields[$k]['as'] != '') {
1640  $select_fields[$k]['as'] = $this->quoteName($select_fields[$k]['as']);
1641  }
1642  if (isset($select_fields[$k]['func_content.']) && $select_fields[$k]['func_content.'][0]['func_content'] != '*') {
1643  $select_fields[$k]['func_content.'][0]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content.'][0]['func_content']);
1644  $select_fields[$k]['func_content'] = $this->quoteFieldNames($select_fields[$k]['func_content']);
1645  }
1646  if (isset($select_fields[$k]['flow-control'])) {
1647  // Quoting flow-control statements
1648  if ($select_fields[$k]['flow-control']['type'] === 'CASE') {
1649  if (isset($select_fields[$k]['flow-control']['case_field'])) {
1650  $select_fields[$k]['flow-control']['case_field'] = $this->quoteFieldNames($select_fields[$k]['flow-control']['case_field']);
1651  }
1652  foreach ($select_fields[$k]['flow-control']['when'] as $key => $when) {
1653  $select_fields[$k]['flow-control']['when'][$key]['when_value'] = $this->_quoteWhereClause($when['when_value']);
1654  }
1655  }
1656  }
1657  }
1658  return $select_fields;
1659  }
1660 
1667  public function quoteFromTables($from_table) {
1668  if ($from_table === '') {
1669  return '';
1670  }
1671  if ($this->runningNative()) {
1672  return $from_table;
1673  }
1674  $from_table = $this->SQLparser->parseFromTables($from_table);
1675  $from_table = $this->_quoteFromTables($from_table);
1676  return $this->SQLparser->compileFromTables($from_table);
1677  }
1678 
1686  protected function _quoteFromTables(array $from_table) {
1687  foreach ($from_table as $k => $v) {
1688  $from_table[$k]['table'] = $this->quoteName($from_table[$k]['table']);
1689  if ($from_table[$k]['as'] != '') {
1690  $from_table[$k]['as'] = $this->quoteName($from_table[$k]['as']);
1691  }
1692  if (is_array($v['JOIN'])) {
1693  foreach ($v['JOIN'] as $joinCnt => $join) {
1694  $from_table[$k]['JOIN'][$joinCnt]['withTable'] = $this->quoteName($join['withTable']);
1695  $from_table[$k]['JOIN'][$joinCnt]['as'] = $join['as'] ? $this->quoteName($join['as']) : '';
1696  foreach ($from_table[$k]['JOIN'][$joinCnt]['ON'] as &$condition) {
1697  $condition['left']['table'] = $condition['left']['table'] ? $this->quoteName($condition['left']['table']) : '';
1698  $condition['left']['field'] = $this->quoteName($condition['left']['field']);
1699  $condition['right']['table'] = $condition['right']['table'] ? $this->quoteName($condition['right']['table']) : '';
1700  $condition['right']['field'] = $this->quoteName($condition['right']['field']);
1701  }
1702  }
1703  }
1704  }
1705  return $from_table;
1706  }
1707 
1715  public function quoteWhereClause($where_clause) {
1716  if ($where_clause === '' || $this->runningNative()) {
1717  return $where_clause;
1718  }
1719  $where_clause = $this->SQLparser->parseWhereClause($where_clause);
1720  if (is_array($where_clause)) {
1721  $where_clause = $this->_quoteWhereClause($where_clause);
1722  $where_clause = $this->SQLparser->compileWhereClause($where_clause);
1723  } else {
1724  throw new \InvalidArgumentException('Could not parse where clause', 1310027511);
1725  }
1726  return $where_clause;
1727  }
1728 
1736  protected function _quoteWhereClause(array $where_clause) {
1737  foreach ($where_clause as $k => $v) {
1738  // Look for sublevel:
1739  if (is_array($where_clause[$k]['sub'])) {
1740  $where_clause[$k]['sub'] = $this->_quoteWhereClause($where_clause[$k]['sub']);
1741  } elseif (isset($v['func'])) {
1742  switch ($where_clause[$k]['func']['type']) {
1743  case 'EXISTS':
1744  $where_clause[$k]['func']['subquery'] = $this->quoteSELECTsubquery($v['func']['subquery']);
1745  break;
1746  case 'FIND_IN_SET':
1747  // quoteStr that will be used for Oracle
1748  $pattern = str_replace($where_clause[$k]['func']['str'][1], '\\' . $where_clause[$k]['func']['str'][1], $where_clause[$k]['func']['str'][0]);
1749  // table is not really needed and may in fact be empty in real statements
1750  // but it's not overridden from \TYPO3\CMS\Core\Database\DatabaseConnection at the moment...
1751  $patternForLike = $this->escapeStrForLike($pattern, $where_clause[$k]['func']['table']);
1752  $where_clause[$k]['func']['str_like'] = $patternForLike;
1753  if ($where_clause[$k]['func']['table'] !== '') {
1754  $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
1755  }
1756  if ($where_clause[$k]['func']['field'] !== '') {
1757  if (!empty($this->dbmsSpecifics) && $this->dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::CAST_FIND_IN_SET)) {
1758  $where_clause[$k]['func']['field'] = 'CAST(' . $this->quoteName($v['func']['field']) . ' AS CHAR)';
1759  } else {
1760  $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
1761  }
1762  }
1763  break;
1764  case 'IFNULL':
1765  // Intentional fallthrough
1766  case 'LOCATE':
1767  if ($where_clause[$k]['func']['table'] != '') {
1768  $where_clause[$k]['func']['table'] = $this->quoteName($v['func']['table']);
1769  }
1770  if ($where_clause[$k]['func']['field'] != '') {
1771  $where_clause[$k]['func']['field'] = $this->quoteName($v['func']['field']);
1772  }
1773  break;
1774  }
1775  } else {
1776  if ($where_clause[$k]['table'] != '') {
1777  $where_clause[$k]['table'] = $this->quoteName($where_clause[$k]['table']);
1778  }
1779  if (!is_numeric($where_clause[$k]['field'])) {
1780  $where_clause[$k]['field'] = $this->quoteName($where_clause[$k]['field']);
1781  }
1782  if (isset($where_clause[$k]['calc_table'])) {
1783  if ($where_clause[$k]['calc_table'] != '') {
1784  $where_clause[$k]['calc_table'] = $this->quoteName($where_clause[$k]['calc_table']);
1785  }
1786  if ($where_clause[$k]['calc_field'] != '') {
1787  $where_clause[$k]['calc_field'] = $this->quoteName($where_clause[$k]['calc_field']);
1788  }
1789  }
1790  }
1791  if ($where_clause[$k]['comparator']) {
1792  if (isset($v['value']['operator'])) {
1793  foreach ($where_clause[$k]['value']['args'] as $argK => $fieldDef) {
1794  $where_clause[$k]['value']['args'][$argK]['table'] = $this->quoteName($fieldDef['table']);
1795  $where_clause[$k]['value']['args'][$argK]['field'] = $this->quoteName($fieldDef['field']);
1796  }
1797  } else {
1798  // Detecting value type; list or plain:
1799  if (GeneralUtility::inList('NOTIN,IN', strtoupper(str_replace(array(' ', LF, CR, TAB), '', $where_clause[$k]['comparator'])))) {
1800  if (isset($v['subquery'])) {
1801  $where_clause[$k]['subquery'] = $this->quoteSELECTsubquery($v['subquery']);
1802  }
1803  } else {
1804  if (
1805  (!isset($where_clause[$k]['value'][1]) || $where_clause[$k]['value'][1] == '')
1806  && is_string($where_clause[$k]['value'][0]) && strstr($where_clause[$k]['value'][0], '.')
1807  ) {
1808  $where_clause[$k]['value'][0] = $this->quoteFieldNames($where_clause[$k]['value'][0]);
1809  } elseif ($this->runningADOdbDriver('mssql')) {
1810  $where_clause[$k]['value'][0] = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($where_clause[$k]['value'][0]), 1, -1);
1811  }
1812  }
1813  }
1814  }
1815  }
1816  return $where_clause;
1817  }
1818 
1826  protected function quoteGroupBy($groupBy) {
1827  if ($groupBy === '') {
1828  return '';
1829  }
1830  if ($this->runningNative()) {
1831  return $groupBy;
1832  }
1833  $groupBy = $this->SQLparser->parseFieldList($groupBy);
1834  $groupBy = $this->_quoteGroupBy($groupBy);
1835  return $this->SQLparser->compileFieldList($groupBy);
1836  }
1837 
1845  protected function _quoteGroupBy(array $groupBy) {
1846  foreach ($groupBy as $k => $v) {
1847  $groupBy[$k]['field'] = $this->quoteName($groupBy[$k]['field']);
1848  if ($groupBy[$k]['table'] != '') {
1849  $groupBy[$k]['table'] = $this->quoteName($groupBy[$k]['table']);
1850  }
1851  }
1852  return $groupBy;
1853  }
1854 
1862  protected function quoteOrderBy($orderBy) {
1863  if ($orderBy === '') {
1864  return '';
1865  }
1866  if ($this->runningNative()) {
1867  return $orderBy;
1868  }
1869  $orderBy = $this->SQLparser->parseFieldList($orderBy);
1870  $orderBy = $this->_quoteOrderBy($orderBy);
1871  return $this->SQLparser->compileFieldList($orderBy);
1872  }
1873 
1881  protected function _quoteOrderBy(array $orderBy) {
1882  foreach ($orderBy as $k => $v) {
1883  if ($orderBy[$k]['table'] === '' && $v['field'] !== '' && ctype_digit($v['field'])) {
1884  continue;
1885  }
1886  $orderBy[$k]['field'] = $this->quoteName($orderBy[$k]['field']);
1887  if ($orderBy[$k]['table'] !== '') {
1888  $orderBy[$k]['table'] = $this->quoteName($orderBy[$k]['table']);
1889  }
1890  }
1891  return $orderBy;
1892  }
1893 
1894  /**************************************
1895  *
1896  * Various helper functions
1897  *
1898  **************************************/
1908  public function fullQuoteStr($str, $table, $allowNull = FALSE) {
1909  if ($allowNull && $str === NULL) {
1910  return 'NULL';
1911  }
1912  return '\'' . $this->quoteStr($str, $table) . '\'';
1913  }
1914 
1926  public function quoteStr($str, $table) {
1927  $this->lastHandlerKey = $this->handler_getFromTableList($table);
1928  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
1929  case 'native':
1930  if ($this->handlerInstance[$this->lastHandlerKey]['link']) {
1931  if (!$this->isConnected()) {
1932  $this->connectDB();
1933  }
1934  $str = $this->handlerInstance[$this->lastHandlerKey]['link']->real_escape_string($str);
1935  } else {
1936  // link may be null when unit testing DBAL
1937  $str = str_replace('\'', '\\\'', $str);
1938  }
1939  break;
1940  case 'adodb':
1941  if (!$this->isConnected()) {
1942  $this->connectDB();
1943  }
1944  $str = substr($this->handlerInstance[$this->lastHandlerKey]->qstr($str), 1, -1);
1945  break;
1946  case 'userdefined':
1947  $str = $this->handlerInstance[$this->lastHandlerKey]->quoteStr($str);
1948  break;
1949  default:
1950  throw new \RuntimeException('No handler found!!!', 1310027655);
1951  }
1952  return $str;
1953  }
1954 
1963  public function quoteName($name, $handlerKey = NULL, $useBackticks = FALSE) {
1964  $handlerKey = $handlerKey ? $handlerKey : $this->lastHandlerKey;
1965  $useNameQuote = isset($this->handlerCfg[$handlerKey]['config']['useNameQuote']) ? $this->handlerCfg[$handlerKey]['config']['useNameQuote'] : FALSE;
1966  if ($useNameQuote) {
1967  // Sometimes DataDictionary is not properly instantiated
1968  if (!is_object($this->handlerInstance[$handlerKey]->DataDictionary)) {
1969  $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
1970  }
1971  return $this->handlerInstance[$handlerKey]->DataDictionary->NameQuote($name);
1972  } else {
1973  $quote = $useBackticks ? '`' : $this->handlerInstance[$handlerKey]->nameQuote;
1974  return $quote . $name . $quote;
1975  }
1976  }
1977 
1987  public function MetaType($type, $table, $maxLength = -1) {
1988  $this->lastHandlerKey = $this->handler_getFromTableList($table);
1989  $str = '';
1990  switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) {
1991  case 'native':
1992  $str = $type;
1993  break;
1994  case 'adodb':
1995  if (in_array($table, $this->cache_fieldType)) {
1996  $rs = $this->handlerInstance[$this->lastHandlerKey]->SelectLimit('SELECT * FROM ' . $this->quoteFromTables($table), 1);
1997  $str = $rs->MetaType($type, $maxLength);
1998  }
1999  break;
2000  case 'userdefined':
2001  $str = $this->handlerInstance[$this->lastHandlerKey]->MetaType($str, $table, $maxLength);
2002  break;
2003  default:
2004  throw new \RuntimeException('No handler found!!!', 1310027685);
2005  }
2006  return $str;
2007  }
2008 
2015  public function MySQLMetaType($t) {
2016  switch (strtoupper($t)) {
2017  case 'STRING':
2018 
2019  case 'CHAR':
2020 
2021  case 'VARCHAR':
2022 
2023  case 'TINYBLOB':
2024 
2025  case 'TINYTEXT':
2026 
2027  case 'ENUM':
2028 
2029  case 'SET':
2030  return 'C';
2031  case 'TEXT':
2032 
2033  case 'LONGTEXT':
2034 
2035  case 'MEDIUMTEXT':
2036  return 'XL';
2037  case 'IMAGE':
2038 
2039  case 'LONGBLOB':
2040 
2041  case 'BLOB':
2042 
2043  case 'MEDIUMBLOB':
2044  return 'B';
2045  case 'YEAR':
2046 
2047  case 'DATE':
2048  return 'D';
2049  case 'TIME':
2050 
2051  case 'DATETIME':
2052 
2053  case 'TIMESTAMP':
2054  return 'T';
2055  case 'FLOAT':
2056 
2057  case 'DOUBLE':
2058  return 'F';
2059  case 'INT':
2060 
2061  case 'INTEGER':
2062 
2063  case 'TINYINT':
2064 
2065  case 'SMALLINT':
2066 
2067  case 'MEDIUMINT':
2068 
2069  case 'BIGINT':
2070  return 'I8';
2071  default:
2072  return 'N';
2073  }
2074  }
2075 
2082  public function MySQLActualType($meta) {
2083  switch (strtoupper($meta)) {
2084  case 'C':
2085  return 'VARCHAR';
2086  case 'XL':
2087 
2088  case 'X':
2089  return 'LONGTEXT';
2090  case 'C2':
2091  return 'VARCHAR';
2092  case 'X2':
2093  return 'LONGTEXT';
2094  case 'B':
2095  return 'LONGBLOB';
2096  case 'D':
2097  return 'DATE';
2098  case 'T':
2099  return 'DATETIME';
2100  case 'L':
2101  return 'TINYINT';
2102  case 'I':
2103 
2104  case 'I1':
2105 
2106  case 'I2':
2107 
2108  case 'I4':
2109 
2110  case 'I8':
2111  return 'BIGINT';
2112  case 'F':
2113  return 'DOUBLE';
2114  case 'N':
2115  return 'NUMERIC';
2116  default:
2117  return $meta;
2118  }
2119  }
2120 
2121  /**************************************
2122  *
2123  * SQL wrapper functions (Overriding parent methods)
2124  * (For use in your applications)
2125  *
2126  **************************************/
2132  public function sql_error() {
2133  $output = '';
2134  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2135  case 'native':
2136  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->error;
2137  break;
2138  case 'adodb':
2139  $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorMsg();
2140  break;
2141  case 'userdefined':
2142  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_error();
2143  break;
2144  }
2145  return $output;
2146  }
2147 
2153  public function sql_errno() {
2154  $output = 0;
2155  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2156  case 'native':
2157  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->errno;
2158  break;
2159  case 'adodb':
2160  $output = $this->handlerInstance[$this->lastHandlerKey]->ErrorNo();
2161  break;
2162  case 'userdefined':
2163  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_errno();
2164  break;
2165  }
2166  return $output;
2167  }
2168 
2175  public function sql_num_rows($res) {
2176  if ($res === FALSE) {
2177  return FALSE;
2178  }
2179  $handlerType = $this->determineHandlerType($res);
2180  $output = 0;
2181  switch ($handlerType) {
2182  case 'native':
2183  $output = $res->num_rows;
2184  break;
2185  case 'adodb':
2186  $output = method_exists($res, 'RecordCount') ? $res->RecordCount() : 0;
2187  break;
2188  case 'userdefined':
2189  $output = $res->sql_num_rows();
2190  break;
2191  }
2192  return $output;
2193  }
2194 
2202  public function sql_fetch_assoc($res) {
2203  $tableList = '';
2204  $output = FALSE;
2205  switch ($this->determineHandlerType($res)) {
2206  case 'native':
2207  $output = $res->fetch_assoc();
2208  $key = serialize($res);
2209  $tableList = $this->resourceIdToTableNameMap[$key];
2210  unset($this->resourceIdToTableNameMap[$key]);
2211  // Reading list of tables from SELECT query:
2212  break;
2213  case 'adodb':
2214  // Check if method exists for the current $res object.
2215  // If a table exists in TCA but not in the db, a error
2216  // occurred because $res is not a valid object.
2217  if (method_exists($res, 'FetchRow')) {
2218  $output = $res->FetchRow();
2219  $tableList = $res->TYPO3_DBAL_tableList;
2220  // Reading list of tables from SELECT query:
2221  // Removing all numeric/integer keys.
2222  // A workaround because in ADOdb we would need to know what we want before executing the query...
2223  // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So
2224  // we don't need to remove anything.
2225  if (is_array($output)) {
2226  if ($this->runningADOdbDriver('mssql')) {
2227  // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
2228  foreach ($output as $key => $value) {
2229  if ($value === ' ') {
2230  $output[$key] = '';
2231  }
2232  }
2233  } else {
2234  foreach ($output as $key => $value) {
2235  if (is_integer($key)) {
2236  unset($output[$key]);
2237  }
2238  }
2239  }
2240  }
2241  }
2242  break;
2243  case 'userdefined':
2244  $output = $res->sql_fetch_assoc();
2245  $tableList = $res->TYPO3_DBAL_tableList;
2246  // Reading list of tables from SELECT query:
2247  break;
2248  }
2249  // Table/Fieldname mapping:
2250  if (is_array($output)) {
2251  if ($tables = $this->map_needMapping($tableList, TRUE)) {
2252  $output = $this->map_assocArray($output, $tables, 1);
2253  }
2254  }
2255  if ($output === NULL) {
2256  // Needed for compatibility
2257  $output = FALSE;
2258  }
2259  // Return result:
2260  return $output;
2261  }
2262 
2271  public function sql_fetch_row($res) {
2272  $output = FALSE;
2273  switch ($this->determineHandlerType($res)) {
2274  case 'native':
2275  $output = $res->fetch_row();
2276  if ($output === NULL) {
2277  // Needed for compatibility
2278  $output = FALSE;
2279  }
2280  break;
2281  case 'adodb':
2282  // Check if method exists for the current $res object.
2283  // If a table exists in TCA but not in the db, a error
2284  // occurred because $res is not a valid object.
2285  if (method_exists($res, 'FetchRow')) {
2286  $output = $res->FetchRow();
2287  // Removing all assoc. keys.
2288  // A workaround because in ADOdb we would need to know what we want before executing the query...
2289  // MSSQL does not support ADODB_FETCH_BOTH and always returns an assoc. array instead. So
2290  // we need to convert resultset.
2291  if (is_array($output)) {
2292  $keyIndex = 0;
2293  foreach ($output as $key => $value) {
2294  unset($output[$key]);
2295  if (is_integer($key) || $this->runningADOdbDriver('mssql')) {
2296  $output[$keyIndex] = $value;
2297  if ($value === ' ') {
2298  // MSSQL does not know such thing as an empty string. So it returns one space instead, which we must fix.
2299  $output[$keyIndex] = '';
2300  }
2301  $keyIndex++;
2302  }
2303  }
2304  }
2305  }
2306  break;
2307  case 'userdefined':
2308  $output = $res->sql_fetch_row();
2309  break;
2310  }
2311  if ($output === NULL) {
2312  // Needed for compatibility
2313  $output = FALSE;
2314  }
2315  return $output;
2316  }
2317 
2325  public function sql_free_result($res) {
2326  if ($res === FALSE) {
2327  return FALSE;
2328  }
2329  $output = TRUE;
2330  switch ($this->determineHandlerType($res)) {
2331  case 'native':
2332  $res->free();
2333  break;
2334  case 'adodb':
2335  if (method_exists($res, 'Close')) {
2336  $res->Close();
2337  unset($res);
2338  $output = TRUE;
2339  } else {
2340  $output = FALSE;
2341  }
2342  break;
2343  case 'userdefined':
2344  unset($res);
2345  break;
2346  }
2347  return $output;
2348  }
2349 
2356  protected function determineHandlerType($res) {
2357  if (is_object($res) && !$res instanceof \mysqli_result) {
2358  $handlerType = $res->TYPO3_DBAL_handlerType;
2359  } elseif ($res instanceof \mysqli_result) {
2360  $handlerType = 'native';
2361  } else {
2362  $handlerType = FALSE;
2363  }
2364  return $handlerType;
2365  }
2366 
2372  public function sql_insert_id() {
2373  $output = 0;
2374  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2375  case 'native':
2376  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->insert_id;
2377  break;
2378  case 'adodb':
2379  $output = $this->handlerInstance[$this->lastHandlerKey]->last_insert_id;
2380  break;
2381  case 'userdefined':
2382  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_insert_id();
2383  break;
2384  }
2385  return $output;
2386  }
2387 
2393  public function sql_affected_rows() {
2394  $output = 0;
2395  switch ($this->handlerCfg[$this->lastHandlerKey]['type']) {
2396  case 'native':
2397  $output = $this->handlerInstance[$this->lastHandlerKey]['link']->affected_rows;
2398  break;
2399  case 'adodb':
2400  $output = $this->handlerInstance[$this->lastHandlerKey]->Affected_Rows();
2401  break;
2402  case 'userdefined':
2403  $output = $this->handlerInstance[$this->lastHandlerKey]->sql_affected_rows();
2404  break;
2405  }
2406  return $output;
2407  }
2408 
2416  public function sql_data_seek($res, $seek) {
2417  $output = TRUE;
2418  switch ($this->determineHandlerType($res)) {
2419  case 'native':
2420  $output = $res->data_seek($seek);
2421  break;
2422  case 'adodb':
2423  $output = $res->Move($seek);
2424  break;
2425  case 'userdefined':
2426  $output = $res->sql_data_seek($seek);
2427  break;
2428  }
2429  return $output;
2430  }
2431 
2441  public function sql_field_metatype($table, $field) {
2442  // If $table and/or $field are mapped, use the original names instead
2443  foreach ($this->mapping as $tableName => $tableMapInfo) {
2444  if (isset($tableMapInfo['mapFieldNames'])) {
2445  foreach ($tableMapInfo['mapFieldNames'] as $fieldName => $fieldMapInfo) {
2446  if ($fieldMapInfo === $field) {
2447  // Field name is mapped => use original name
2448  $field = $fieldName;
2449  }
2450  }
2451  }
2452  }
2453  return $this->cache_fieldType[$table][$field]['metaType'];
2454  }
2455 
2464  public function sql_field_type($res, $pointer) {
2465  if ($res === NULL) {
2466  debug(array('no res in sql_field_type!'));
2467  return 'text';
2468  } elseif (is_string($res)) {
2469  if ($res === 'tx_dbal_debuglog') {
2470  return 'text';
2471  }
2472  $handlerType = 'adodb';
2473  } else {
2474  $handlerType = $this->determineHandlerType($res);
2475  }
2476  $output = '';
2477  switch ($handlerType) {
2478  case 'native':
2479  $metaInfo = $res->fetch_field_direct($pointer);
2480  if ($metaInfo) {
2481  $output = $this->mysqlDataTypeMapping[$metaInfo->type];
2482  } else {
2483  $output = '';
2484  }
2485  break;
2486  case 'adodb':
2487  if (is_string($pointer)) {
2488  $output = $this->cache_fieldType[$res][$pointer]['type'];
2489  }
2490  break;
2491  case 'userdefined':
2492  $output = $res->sql_field_type($pointer);
2493  break;
2494  }
2495  return $output;
2496  }
2497 
2498  /**********
2499  *
2500  * Legacy functions, bound to _DEFAULT handler. (Overriding parent methods)
2501  * Deprecated or still experimental.
2502  *
2503  **********/
2520  public function sql_query($query) {
2521  $globalConfig = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['dbal']);
2522  if ($globalConfig['sql_query.']['passthrough']) {
2523  return parent::sql_query($query);
2524  }
2525  // This method is heavily used by Extbase, try to handle it with DBAL-native methods
2526  $queryParts = $this->SQLparser->parseSQL($query);
2527  if (is_array($queryParts) && GeneralUtility::inList('SELECT,UPDATE,INSERT,DELETE', $queryParts['type'])) {
2528  return $this->exec_query($queryParts);
2529  }
2530  $sqlResult = NULL;
2531  switch ($this->handlerCfg['_DEFAULT']['type']) {
2532  case 'native':
2533  if (!$this->isConnected()) {
2534  $this->connectDB();
2535  }
2536  $sqlResult = $this->handlerInstance['_DEFAULT']['link']->query($query);
2537  break;
2538  case 'adodb':
2539  $sqlResult = $this->handlerInstance['_DEFAULT']->Execute($query);
2540  $sqlResult->TYPO3_DBAL_handlerType = 'adodb';
2541  break;
2542  case 'userdefined':
2543  $sqlResult = $this->handlerInstance['_DEFAULT']->sql_query($query);
2544  $sqlResult->TYPO3_DBAL_handlerType = 'userdefined';
2545  break;
2546  }
2547  $this->lastHandlerKey = '_DEFAULT';
2548  if ($this->printErrors && $this->sql_error()) {
2549  debug(array($this->lastQuery, $this->sql_error()));
2550  }
2551  return $sqlResult;
2552  }
2553 
2563  public function sql_pconnect($host = NULL, $username = NULL, $password = NULL) {
2564  if ($host || $username || $password) {
2565  $this->handleDeprecatedConnectArguments($host, $username, $password);
2566  }
2567 
2568  // Initializing and output value:
2569  $sqlResult = $this->handler_init('_DEFAULT');
2570  return $sqlResult;
2571  }
2572 
2579  public function sql_select_db($TYPO3_db = NULL) {
2580  if (!$TYPO3_db) {
2581  $TYPO3_db = $this->handlerCfg[$this->lastHandlerKey]['config']['database'];
2582  }
2583  $ret = TRUE;
2584  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
2585  $ret = $this->handlerInstance[$this->lastHandlerKey]['link']->select_db($TYPO3_db);
2586  }
2587  if (!$ret) {
2588  GeneralUtility::sysLog(
2589  'Could not select MySQL database ' . $TYPO3_db . ': ' . $this->sql_error(),
2590  'Core',
2592  );
2593  }
2594  return $ret;
2595  }
2596 
2597  /**************************************
2598  *
2599  * SQL admin functions
2600  * (For use in the Install Tool and Extension Manager)
2601  *
2602  **************************************/
2612  public function admin_get_dbs() {
2613  $dbArr = array();
2614  $this->lastHandlerKey = '_DEFAULT';
2615  switch ($this->handlerCfg['_DEFAULT']['type']) {
2616  case 'native':
2618  $db_list = $this->query("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA");
2619  while ($row = $db_list->fetch_object()) {
2620  if ($this->sql_select_db($row->SCHEMA_NAME)) {
2621  $dbArr[] = $row->SCHEMA_NAME;
2622  }
2623  }
2624  $db_list->free();
2625  break;
2626  case 'adodb':
2627  // check needed for install tool - otherwise it will just die because the call to
2628  // MetaDatabases is done on a stdClass instance
2629  if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaDatabases')) {
2630  $sqlDBs = $this->handlerInstance['_DEFAULT']->MetaDatabases();
2631  if (is_array($sqlDBs)) {
2632  foreach ($sqlDBs as $k => $theDB) {
2633  $dbArr[] = $theDB;
2634  }
2635  }
2636  }
2637  break;
2638  case 'userdefined':
2639  $dbArr = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2640  break;
2641  }
2642  return $dbArr;
2643  }
2644 
2652  public function admin_get_tables() {
2653  $whichTables = array();
2654  // Getting real list of tables:
2655  switch ($this->handlerCfg['_DEFAULT']['type']) {
2656  case 'native':
2657  $tables_result = $this->query('SHOW TABLE STATUS FROM `' . TYPO3_db . '`');
2658  if (!$this->sql_error()) {
2659  while ($theTable = $this->sql_fetch_assoc($tables_result)) {
2660  $whichTables[$theTable['Name']] = $theTable;
2661  }
2662  }
2663  $tables_result->free();
2664  break;
2665  case 'adodb':
2666  // check needed for install tool - otherwise it will just die because the call to
2667  // MetaTables is done on a stdClass instance
2668  if (method_exists($this->handlerInstance['_DEFAULT'], 'MetaTables')) {
2669  $sqlTables = $this->handlerInstance['_DEFAULT']->MetaTables('TABLES');
2670  foreach ($sqlTables as $k => $theTable) {
2671  if (preg_match('/BIN\\$/', $theTable)) {
2672  // Skip tables from the Oracle 10 Recycle Bin
2673  continue;
2674  }
2675  $whichTables[$theTable] = array('Name' => $theTable);
2676  }
2677  }
2678  break;
2679  case 'userdefined':
2680  $whichTables = $this->handlerInstance['_DEFAULT']->admin_get_tables();
2681  break;
2682  }
2683  // Check mapping:
2684  if (is_array($this->mapping) && count($this->mapping)) {
2685  // Mapping table names in reverse, first getting list of real table names:
2686  $tMap = array();
2687  foreach ($this->mapping as $tN => $tMapInfo) {
2688  if (isset($tMapInfo['mapTableName'])) {
2689  $tMap[$tMapInfo['mapTableName']] = $tN;
2690  }
2691  }
2692  // Do mapping:
2693  $newList = array();
2694  foreach ($whichTables as $tN => $tDefinition) {
2695  if (isset($tMap[$tN])) {
2696  $tN = $tMap[$tN];
2697  $tDefinition = array('Name' => $tN);
2698  }
2699  $newList[$tN] = $tDefinition;
2700  }
2701  $whichTables = $newList;
2702  }
2703  // Adding tables configured to reside in other DBMS (handler by other handlers than the default):
2704  if (is_array($this->table2handlerKeys)) {
2705  foreach ($this->table2handlerKeys as $key => $handlerKey) {
2706  $whichTables[$key] = array('Name' => $key);
2707  }
2708  }
2709  return $whichTables;
2710  }
2711 
2723  public function admin_get_fields($tableName) {
2724  $output = array();
2725  // Do field mapping if needed:
2726  $ORIG_tableName = $tableName;
2727  if ($tableArray = $this->map_needMapping($tableName)) {
2728  // Table name:
2729  if ($this->mapping[$tableName]['mapTableName']) {
2730  $tableName = $this->mapping[$tableName]['mapTableName'];
2731  }
2732  }
2733  // Find columns
2734  $this->lastHandlerKey = $this->handler_getFromTableList($tableName);
2735  switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) {
2736  case 'native':
2738  $columns_res = $this->query('SHOW columns FROM ' . $tableName);
2739  while ($fieldRow = $columns_res->fetch_assoc()) {
2740  $output[$fieldRow['Field']] = $fieldRow;
2741  }
2742  $columns_res->free();
2743  break;
2744  case 'adodb':
2745  $fieldRows = $this->handlerInstance[$this->lastHandlerKey]->MetaColumns($tableName, FALSE);
2746  if (is_array($fieldRows)) {
2747  foreach ($fieldRows as $k => $fieldRow) {
2748  settype($fieldRow, 'array');
2749  $fieldRow['Field'] = $fieldRow['name'];
2750  $ntype = $this->MySQLActualType($this->MetaType($fieldRow['type'], $tableName));
2751  $ntype .= $fieldRow['max_length'] != -1 ? ($ntype == 'INT' ? '(11)' : '(' . $fieldRow['max_length'] . ')') : '';
2752  $fieldRow['Type'] = strtolower($ntype);
2753  $fieldRow['Null'] = '';
2754  $fieldRow['Key'] = '';
2755  $fieldRow['Default'] = $fieldRow['default_value'];
2756  $fieldRow['Extra'] = '';
2757  $output[$fieldRow['name']] = $fieldRow;
2758  }
2759  }
2760  break;
2761  case 'userdefined':
2762  $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_fields($tableName);
2763  break;
2764  }
2765  // mapping should be done:
2766  if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2767  $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2768  $newOutput = array();
2769  foreach ($output as $fN => $fInfo) {
2770  if (isset($revFields[$fN])) {
2771  $fN = $revFields[$fN];
2772  $fInfo['Field'] = $fN;
2773  }
2774  $newOutput[$fN] = $fInfo;
2775  }
2776  $output = $newOutput;
2777  }
2778  return $output;
2779  }
2780 
2788  public function admin_get_keys($tableName) {
2789  $output = array();
2790  // Do field mapping if needed:
2791  $ORIG_tableName = $tableName;
2792  if ($tableArray = $this->map_needMapping($tableName)) {
2793  // Table name:
2794  if ($this->mapping[$tableName]['mapTableName']) {
2795  $tableName = $this->mapping[$tableName]['mapTableName'];
2796  }
2797  }
2798  // Find columns
2799  $this->lastHandlerKey = $this->handler_getFromTableList($tableName);
2800  switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) {
2801  case 'native':
2803  $keyRes = $this->query('SHOW keys FROM ' . $tableName);
2804  while ($keyRow = $keyRes->fetch_assoc()) {
2805  $output[] = $keyRow;
2806  }
2807  $keyRes->free();
2808  break;
2809  case 'adodb':
2810  $keyRows = $this->handlerInstance[$this->lastHandlerKey]->MetaIndexes($tableName);
2811  if ($keyRows !== FALSE) {
2812  foreach ($keyRows as $k => $theKey) {
2813  $theKey['Table'] = $tableName;
2814  $theKey['Non_unique'] = (int)(!$theKey['unique']);
2815  $theKey['Key_name'] = str_replace($tableName . '_', '', $k);
2816  // the following are probably not needed anyway...
2817  $theKey['Collation'] = '';
2818  $theKey['Cardinality'] = '';
2819  $theKey['Sub_part'] = '';
2820  $theKey['Packed'] = '';
2821  $theKey['Null'] = '';
2822  $theKey['Index_type'] = '';
2823  $theKey['Comment'] = '';
2824  // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2825  $keycols = $theKey['columns'];
2826  foreach ($keycols as $c => $theCol) {
2827  $theKey['Seq_in_index'] = $c + 1;
2828  $theKey['Column_name'] = $theCol;
2829  $output[] = $theKey;
2830  }
2831  }
2832  }
2833  $priKeyRow = $this->handlerInstance[$this->lastHandlerKey]->MetaPrimaryKeys($tableName);
2834  $theKey = array();
2835  $theKey['Table'] = $tableName;
2836  $theKey['Non_unique'] = 0;
2837  $theKey['Key_name'] = 'PRIMARY';
2838  // the following are probably not needed anyway...
2839  $theKey['Collation'] = '';
2840  $theKey['Cardinality'] = '';
2841  $theKey['Sub_part'] = '';
2842  $theKey['Packed'] = '';
2843  $theKey['Null'] = '';
2844  $theKey['Index_type'] = '';
2845  $theKey['Comment'] = '';
2846  // now map multiple fields into multiple rows (we mimic MySQL, remember...)
2847  if ($priKeyRow !== FALSE) {
2848  foreach ($priKeyRow as $c => $theCol) {
2849  $theKey['Seq_in_index'] = $c + 1;
2850  $theKey['Column_name'] = $theCol;
2851  $output[] = $theKey;
2852  }
2853  }
2854  break;
2855  case 'userdefined':
2856  $output = $this->handlerInstance[$this->lastHandlerKey]->admin_get_keys($tableName);
2857  break;
2858  }
2859  // mapping should be done:
2860  if (is_array($tableArray) && is_array($this->mapping[$ORIG_tableName]['mapFieldNames'])) {
2861  $revFields = array_flip($this->mapping[$ORIG_tableName]['mapFieldNames']);
2862  $newOutput = array();
2863  foreach ($output as $kN => $kInfo) {
2864  // Table:
2865  $kInfo['Table'] = $ORIG_tableName;
2866  // Column
2867  if (isset($revFields[$kInfo['Column_name']])) {
2868  $kInfo['Column_name'] = $revFields[$kInfo['Column_name']];
2869  }
2870  // Write it back:
2871  $newOutput[$kN] = $kInfo;
2872  }
2873  $output = $newOutput;
2874  }
2875  return $output;
2876  }
2877 
2890  public function admin_get_charsets() {
2891  $output = array();
2892  if ((string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native') {
2894  $columns_res = $this->query('SHOW CHARACTER SET');
2895  if ($columns_res !== FALSE) {
2896  while ($row = $columns_res->fetch_assoc()) {
2897  $output[$row['Charset']] = $row;
2898  }
2899  $columns_res->free();
2900  }
2901  }
2902  return $output;
2903  }
2904 
2912  public function admin_query($query) {
2913  $parsedQuery = $this->SQLparser->parseSQL($query);
2914  if (!is_array($parsedQuery)) {
2915  throw new \InvalidArgumentException('ERROR: Query could not be parsed: "' . htmlspecialchars($parsedQuery) . '". Query: "' . htmlspecialchars($query) . '"', 1310027793);
2916  }
2917  $ORIG_table = $parsedQuery['TABLE'];
2918  // Process query based on type:
2919  switch ($parsedQuery['type']) {
2920  case 'CREATETABLE':
2921 
2922  case 'ALTERTABLE':
2923 
2924  case 'DROPTABLE':
2925  $this->clearCachedFieldInfo();
2926  $this->map_genericQueryParsed($parsedQuery);
2927  break;
2928  case 'INSERT':
2929 
2930  case 'TRUNCATETABLE':
2931  $this->map_genericQueryParsed($parsedQuery);
2932  break;
2933  case 'CREATEDATABASE':
2934  throw new \InvalidArgumentException('Creating a database with DBAL is not supported. Did you really read the manual?', 1310027716);
2935  break;
2936  default:
2937  throw new \InvalidArgumentException('ERROR: Invalid Query type (' . $parsedQuery['type'] . ') for ->admin_query() function!: "' . htmlspecialchars($query) . '"', 1310027740);
2938  }
2939  // Setting query array (for other applications to access if needed)
2940  $this->lastParsedAndMappedQueryArray = $parsedQuery;
2941  // Execute query (based on handler derived from the TABLE name which we actually know for once!)
2942  $result = NULL;
2943  $this->lastHandlerKey = $this->handler_getFromTableList($ORIG_table);
2944  switch ((string)$this->handlerCfg[$this->lastHandlerKey]['type']) {
2945  case 'native':
2946  // Compiling query:
2947  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2948  if (in_array($this->lastParsedAndMappedQueryArray['type'], array('INSERT', 'DROPTABLE', 'ALTERTABLE'))) {
2949  $result = $this->query($compiledQuery);
2950  } else {
2951  $result = $this->query($compiledQuery[0]);
2952  }
2953  break;
2954  case 'adodb':
2955  // Compiling query:
2956  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2957  switch ($this->lastParsedAndMappedQueryArray['type']) {
2958  case 'INSERT':
2959  $result = $this->exec_INSERTquery($this->lastParsedAndMappedQueryArray['TABLE'], $compiledQuery);
2960  break;
2961  case 'TRUNCATETABLE':
2962  $result = $this->exec_TRUNCATEquery($this->lastParsedAndMappedQueryArray['TABLE']);
2963  break;
2964  default:
2965  $result = $this->handlerInstance[$this->lastHandlerKey]->DataDictionary->ExecuteSQLArray($compiledQuery);
2966  }
2967  break;
2968  case 'userdefined':
2969  // Compiling query:
2970  $compiledQuery = $this->SQLparser->compileSQL($this->lastParsedAndMappedQueryArray);
2971  $result = $this->handlerInstance[$this->lastHandlerKey]->admin_query($compiledQuery);
2972  default:
2973  }
2974  return $result;
2975  }
2976 
2977  /************************************
2978  *
2979  * Handler management
2980  *
2981  **************************************/
2991  public function handler_getFromTableList($tableList) {
2992  $key = $tableList;
2993  if (!isset($this->cache_handlerKeyFromTableList[$key])) {
2994  // Get tables separated:
2995  $_tableList = $tableList;
2996  $tableArray = $this->SQLparser->parseFromTables($_tableList);
2997  // If success, traverse the tables:
2998  if (is_array($tableArray) && count($tableArray)) {
2999  $outputHandlerKey = '';
3000  foreach ($tableArray as $vArray) {
3001  // Find handler key, select "_DEFAULT" if none is specifically configured:
3002  $handlerKey = $this->table2handlerKeys[$vArray['table']] ? $this->table2handlerKeys[$vArray['table']] : '_DEFAULT';
3003  // In case of separate handler keys for joined tables:
3004  if ($outputHandlerKey && $handlerKey != $outputHandlerKey) {
3005  throw new \RuntimeException('DBAL fatal error: Tables in this list "' . $tableList . '" didn\'t use the same DB handler!', 1310027833);
3006  }
3007  $outputHandlerKey = $handlerKey;
3008  }
3009  // Check initialized state; if handler is NOT initialized (connected) then we will connect it!
3010  if (!isset($this->handlerInstance[$outputHandlerKey])) {
3011  $this->handler_init($outputHandlerKey);
3012  }
3013  // Return handler key:
3014  $this->cache_handlerKeyFromTableList[$key] = $outputHandlerKey;
3015  } else {
3016  throw new \RuntimeException('DBAL fatal error: No handler found in handler_getFromTableList() for: "' . $tableList . '" (' . $tableArray . ')', 1310027933);
3017  }
3018  }
3019  return $this->cache_handlerKeyFromTableList[$key];
3020  }
3021 
3030  public function handler_init($handlerKey) {
3031  if (!isset($this->handlerCfg[$handlerKey]) || !is_array($this->handlerCfg[$handlerKey])) {
3032  throw new \RuntimeException('ERROR: No handler for key "' . $handlerKey . '"', 1310028018);
3033  }
3034  if ($handlerKey === '_DEFAULT') {
3035  // Overriding the _DEFAULT handler configuration of username, password, localhost and database name:
3036  $this->handlerCfg[$handlerKey]['config']['username'] = $this->databaseUsername;
3037  $this->handlerCfg[$handlerKey]['config']['password'] = $this->databaseUserPassword;
3038  $this->handlerCfg[$handlerKey]['config']['host'] = $this->databaseHost;
3039  $this->handlerCfg[$handlerKey]['config']['port'] = (int)$this->databasePort;
3040  $this->handlerCfg[$handlerKey]['config']['database'] = $this->databaseName;
3041  }
3042  $cfgArray = $this->handlerCfg[$handlerKey];
3043  if (!$cfgArray['config']['database']) {
3044  // Configuration is incomplete
3045  return FALSE;
3046  }
3047 
3048  $output = FALSE;
3049  switch ((string)$cfgArray['type']) {
3050  case 'native':
3051  $host = $cfgArray['config']['host'];
3052  if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
3053  $host = 'p:' . $host;
3054  }
3055  $link = mysqli_init();
3056  $connected = $link->real_connect(
3057  $host,
3058  $cfgArray['config']['username'],
3059  $cfgArray['config']['password'],
3060  $cfgArray['config']['database'],
3061  isset($cfgArray['config']['port']) ? $cfgArray['config']['port'] : null
3062  );
3063  if ($connected) {
3064  // Set handler instance:
3065  $this->handlerInstance[$handlerKey] = array('handlerType' => 'native', 'link' => $link);
3066 
3067  if ($link->set_charset($this->connectionCharset) === FALSE) {
3068  GeneralUtility::sysLog(
3069  'Error setting connection charset to "' . $this->connectionCharset . '"',
3070  'Core',
3072  );
3073  }
3074 
3075  // For default, set ->link (see \TYPO3\CMS\Core\Database\DatabaseConnection)
3076  if ($handlerKey === '_DEFAULT') {
3077  $this->link = $link;
3078  $this->isConnected = TRUE;
3079  $this->lastHandlerKey = $handlerKey;
3080  foreach ($this->initializeCommandsAfterConnect as $command) {
3081  if ($this->query($command) === FALSE) {
3082  GeneralUtility::sysLog(
3083  'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
3084  'Core',
3086  );
3087  }
3088  }
3089  $this->setSqlMode();
3090  $this->checkConnectionCharset();
3091  }
3092 
3093  // TODO:
3094 
3095  $output = TRUE;
3096  } else {
3097  GeneralUtility::sysLog('Could not connect to MySQL server ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4);
3098  }
3099  break;
3100  case 'adodb':
3101  $output = TRUE;
3102  require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('adodb') . 'adodb/adodb.inc.php';
3103  if (!defined('ADODB_FORCE_NULLS')) {
3104  define('ADODB_FORCE_NULLS', 1);
3105  }
3106  $GLOBALS['ADODB_FORCE_TYPE'] = ADODB_FORCE_VALUE;
3107  $GLOBALS['ADODB_FETCH_MODE'] = ADODB_FETCH_BOTH;
3108  $this->handlerInstance[$handlerKey] = ADONewConnection($cfgArray['config']['driver']);
3109  // Set driver-specific options
3110  if (isset($cfgArray['config']['driverOptions'])) {
3111  foreach ($cfgArray['config']['driverOptions'] as $optionName => $optionValue) {
3112  $optionSetterName = 'set' . ucfirst($optionName);
3113  if (method_exists($this->handlerInstance[$handlerKey], $optionSetterName)) {
3114  $this->handlerInstance[$handlerKey]->{$optionSetterName}($optionValue);
3115  } else {
3116  $this->handlerInstance[$handlerKey]->{$optionName} = $optionValue;
3117  }
3118  }
3119  }
3120  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect']) {
3121  $this->handlerInstance[$handlerKey]->Connect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']);
3122  } else {
3123  $this->handlerInstance[$handlerKey]->PConnect($cfgArray['config']['host'] . (isset($cfgArray['config']['port']) ? ':' . $cfgArray['config']['port'] : ''), $cfgArray['config']['username'], $cfgArray['config']['password'], $cfgArray['config']['database']);
3124  }
3125  if (!$this->handlerInstance[$handlerKey]->isConnected()) {
3126  $dsn = $cfgArray['config']['driver'] . '://' . $cfgArray['config']['username'] . (strlen($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');
3127  GeneralUtility::sysLog('Could not connect to DB server using ADOdb on ' . $cfgArray['config']['host'] . ' with user ' . $cfgArray['config']['username'] . '.', 'Core', 4);
3128  error_log('DBAL error: Connection to ' . $dsn . ' failed. Maybe PHP doesn\'t support the database?');
3129  $output = FALSE;
3130  } else {
3131  $this->handlerInstance[$handlerKey]->DataDictionary = NewDataDictionary($this->handlerInstance[$handlerKey]);
3132  $this->handlerInstance[$handlerKey]->last_insert_id = 0;
3133  if (isset($cfgArray['config']['sequenceStart'])) {
3134  $this->handlerInstance[$handlerKey]->sequenceStart = $cfgArray['config']['sequenceStart'];
3135  } else {
3136  $this->handlerInstance[$handlerKey]->sequenceStart = 1;
3137  }
3138  }
3139  break;
3140  case 'userdefined':
3141  // Find class file:
3142  $fileName = GeneralUtility::getFileAbsFileName($cfgArray['config']['classFile']);
3143  if (@is_file($fileName)) {
3144  require_once $fileName;
3145  } else {
3146  throw new \RuntimeException('DBAL error: "' . $fileName . '" was not a file to include.', 1310027975);
3147  }
3148  // Initialize:
3149  $this->handlerInstance[$handlerKey] = GeneralUtility::makeInstance($cfgArray['config']['class']);
3150  $this->handlerInstance[$handlerKey]->init($cfgArray, $this);
3151  if (is_object($this->handlerInstance[$handlerKey])) {
3152  $output = TRUE;
3153  }
3154  break;
3155  default:
3156  throw new \RuntimeException('ERROR: Invalid handler type: "' . $cfgArray['type'] . '"', 1310027995);
3157  }
3158  return $output;
3159  }
3160 
3166  public function isConnected() {
3167  $result = FALSE;
3168  switch ((string) $this->handlerCfg[$this->lastHandlerKey]['type']) {
3169  case 'native':
3170  $result = isset($this->handlerCfg[$this->lastHandlerKey]['link']);
3171  break;
3172  case 'adodb':
3173 
3174  case 'userdefined':
3175  $result = is_object($this->handlerInstance[$this->lastHandlerKey]) && $this->handlerInstance[$this->lastHandlerKey]->isConnected();
3176  break;
3177  }
3178  return $result;
3179  }
3180 
3186  public function runningNative() {
3187  return (string)$this->handlerCfg[$this->lastHandlerKey]['type'] === 'native';
3188  }
3189 
3196  public function runningADOdbDriver($driver) {
3197  return strpos($this->handlerCfg[$this->lastHandlerKey]['config']['driver'], $driver) !== FALSE;
3198  }
3199 
3200  /************************************
3201  *
3202  * Table/Field mapping
3203  *
3204  **************************************/
3213  protected function map_needMapping($tableList, $fieldMappingOnly = FALSE, array &$parsedTableList = array()) {
3214  $key = $tableList . '|' . $fieldMappingOnly;
3215  if (!isset($this->cache_mappingFromTableList[$key])) {
3216  $this->cache_mappingFromTableList[$key] = FALSE;
3217  // Default:
3218  $tables = $this->SQLparser->parseFromTables($tableList);
3219  if (is_array($tables)) {
3220  $parsedTableList = $tables;
3221  foreach ($tables as $tableCfg) {
3222  if ($fieldMappingOnly) {
3223  if (is_array($this->mapping[$tableCfg['table']]['mapFieldNames'])) {
3224  $this->cache_mappingFromTableList[$key] = $tables;
3225  } elseif (is_array($tableCfg['JOIN'])) {
3226  foreach ($tableCfg['JOIN'] as $join) {
3227  if (is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
3228  $this->cache_mappingFromTableList[$key] = $tables;
3229  break;
3230  }
3231  }
3232  }
3233  } else {
3234  if (is_array($this->mapping[$tableCfg['table']])) {
3235  $this->cache_mappingFromTableList[$key] = $tables;
3236  } elseif (is_array($tableCfg['JOIN'])) {
3237  foreach ($tableCfg['JOIN'] as $join) {
3238  if (is_array($this->mapping[$join['withTable']])) {
3239  $this->cache_mappingFromTableList[$key] = $tables;
3240  break;
3241  }
3242  }
3243  }
3244  }
3245  }
3246  }
3247  }
3248  return $this->cache_mappingFromTableList[$key];
3249  }
3250 
3262  protected function map_assocArray($input, $tables, $rev = FALSE) {
3263  // Traverse tables from query (hopefully only one table):
3264  foreach ($tables as $tableCfg) {
3265  $tableKey = $this->getMappingKey($tableCfg['table']);
3266  if (is_array($this->mapping[$tableKey]['mapFieldNames'])) {
3267  // Get the map (reversed if needed):
3268  if ($rev) {
3269  $theMap = array_flip($this->mapping[$tableKey]['mapFieldNames']);
3270  } else {
3271  $theMap = $this->mapping[$tableKey]['mapFieldNames'];
3272  }
3273  // Traverse selected record, map fieldnames:
3274  $output = array();
3275  foreach ($input as $fN => $value) {
3276  // Set the field name, change it if found in mapping array:
3277  if ($theMap[$fN]) {
3278  $newKey = $theMap[$fN];
3279  } else {
3280  $newKey = $fN;
3281  }
3282  // Set value to fieldname:
3283  $output[$newKey] = $value;
3284  }
3285  // When done, override the $input array with the result:
3286  $input = $output;
3287  }
3288  }
3289  // Return input array (which might have been altered in the mean time)
3290  return $input;
3291  }
3292 
3304  protected function map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy) {
3305  // Backup current mapping as it may be altered if aliases on mapped tables are found
3306  $backupMapping = $this->mapping;
3307  // Tables:
3308  $tables = is_array($from_table) ? $from_table : $this->SQLparser->parseFromTables($from_table);
3309  $defaultTable = $tables[0]['table'];
3310  // Prepare mapping for aliased tables. This will copy the definition of the original table name.
3311  // The alias is prefixed with a database-incompatible character to prevent naming clash with real table name
3312  // Further access to $this->mapping should be made through $this->getMappingKey() method
3313  foreach ($tables as $k => $v) {
3314  if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
3315  $mappingKey = $this->getFreeMappingKey($v['as']);
3316  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
3317  }
3318  if (is_array($v['JOIN'])) {
3319  foreach ($v['JOIN'] as $joinCnt => $join) {
3320  if ($join['as'] && is_array($this->mapping[$join['withTable']]['mapFieldNames'])) {
3321  $mappingKey = $this->getFreeMappingKey($join['as']);
3322  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$join['withTable']]['mapFieldNames'];
3323  }
3324  }
3325  }
3326  }
3327  foreach ($tables as $k => $v) {
3328  $tableKey = $this->getMappingKey($v['table']);
3329  if ($this->mapping[$tableKey]['mapTableName']) {
3330  $tables[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
3331  }
3332  // Mapping JOINS
3333  if (is_array($v['JOIN'])) {
3334  foreach ($v['JOIN'] as $joinCnt => $join) {
3335  // Mapping withTable of the JOIN
3336  $withTableKey = $this->getMappingKey($join['withTable']);
3337  if ($this->mapping[$withTableKey]['mapTableName']) {
3338  $tables[$k]['JOIN'][$joinCnt]['withTable'] = $this->mapping[$withTableKey]['mapTableName'];
3339  }
3340  $onPartsArray = array();
3341  // Mapping ON parts of the JOIN
3342  if (is_array($tables[$k]['JOIN'][$joinCnt]['ON'])) {
3343  foreach ($tables[$k]['JOIN'][$joinCnt]['ON'] as &$condition) {
3344  // Left side of the comparator
3345  $leftTableKey = $this->getMappingKey($condition['left']['table']);
3346  if (isset($this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']])) {
3347  $condition['left']['field'] = $this->mapping[$leftTableKey]['mapFieldNames'][$condition['left']['field']];
3348  }
3349  if (isset($this->mapping[$leftTableKey]['mapTableName'])) {
3350  $condition['left']['table'] = $this->mapping[$leftTableKey]['mapTableName'];
3351  }
3352  // Right side of the comparator
3353  $rightTableKey = $this->getMappingKey($condition['right']['table']);
3354  if (isset($this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']])) {
3355  $condition['right']['field'] = $this->mapping[$rightTableKey]['mapFieldNames'][$condition['right']['field']];
3356  }
3357  if (isset($this->mapping[$rightTableKey]['mapTableName'])) {
3358  $condition['right']['table'] = $this->mapping[$rightTableKey]['mapTableName'];
3359  }
3360  }
3361  }
3362  }
3363  }
3364  }
3365  $fromParts = $tables;
3366  // Where clause:
3367  $parameterReferences = array();
3368  $whereParts = $this->SQLparser->parseWhereClause($where_clause, '', $parameterReferences);
3369  $this->map_sqlParts($whereParts, $defaultTable);
3370  // Select fields:
3371  $selectParts = $this->SQLparser->parseFieldList($select_fields);
3372  $this->map_sqlParts($selectParts, $defaultTable);
3373  // Group By fields
3374  $groupByParts = $this->SQLparser->parseFieldList($groupBy);
3375  $this->map_sqlParts($groupByParts, $defaultTable);
3376  // Order By fields
3377  $orderByParts = $this->SQLparser->parseFieldList($orderBy);
3378  $this->map_sqlParts($orderByParts, $defaultTable);
3379  // Restore the original mapping
3380  $this->mapping = $backupMapping;
3381  return array($selectParts, $fromParts, $whereParts, $groupByParts, $orderByParts, $parameterReferences);
3382  }
3383 
3391  protected function getMappingKey($tableName) {
3392  // Search deepest alias mapping
3393  while (isset($this->mapping['*' . $tableName])) {
3394  $tableName = '*' . $tableName;
3395  }
3396  return $tableName;
3397  }
3398 
3405  protected function getFreeMappingKey($tableName) {
3406  while (isset($this->mapping[$tableName])) {
3407  $tableName = '*' . $tableName;
3408  }
3409  return $tableName;
3410  }
3411 
3420  protected function map_sqlParts(&$sqlPartArray, $defaultTable) {
3421  $defaultTableKey = $this->getMappingKey($defaultTable);
3422  // Traverse sql Part array:
3423  if (is_array($sqlPartArray)) {
3424  foreach ($sqlPartArray as $k => $v) {
3425  if (isset($sqlPartArray[$k]['type'])) {
3426  switch ($sqlPartArray[$k]['type']) {
3427  case 'flow-control':
3428  $temp = array($sqlPartArray[$k]['flow-control']);
3429  $this->map_sqlParts($temp, $defaultTable);
3430  // Call recursively!
3431  $sqlPartArray[$k]['flow-control'] = $temp[0];
3432  break;
3433  case 'CASE':
3434  if (isset($sqlPartArray[$k]['case_field'])) {
3435  $fieldArray = explode('.', $sqlPartArray[$k]['case_field']);
3436  if (count($fieldArray) == 1 && is_array($this->mapping[$defaultTableKey]['mapFieldNames']) && isset($this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]])) {
3437  $sqlPartArray[$k]['case_field'] = $this->mapping[$defaultTableKey]['mapFieldNames'][$fieldArray[0]];
3438  } elseif (count($fieldArray) == 2) {
3439  // Map the external table
3440  $table = $fieldArray[0];
3441  $tableKey = $this->getMappingKey($table);
3442  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3443  $table = $this->mapping[$tableKey]['mapTableName'];
3444  }
3445  // Map the field itself
3446  $field = $fieldArray[1];
3447  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3448  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3449  }
3450  $sqlPartArray[$k]['case_field'] = $table . '.' . $field;
3451  }
3452  }
3453  foreach ($sqlPartArray[$k]['when'] as $key => $when) {
3454  $this->map_sqlParts($sqlPartArray[$k]['when'][$key]['when_value'], $defaultTable);
3455  }
3456  break;
3457  }
3458  }
3459  // Look for sublevel (WHERE parts only)
3460  if (is_array($sqlPartArray[$k]['sub'])) {
3461  $this->map_sqlParts($sqlPartArray[$k]['sub'], $defaultTable);
3462  } elseif (isset($sqlPartArray[$k]['func'])) {
3463  switch ($sqlPartArray[$k]['func']['type']) {
3464  case 'EXISTS':
3465  $this->map_subquery($sqlPartArray[$k]['func']['subquery']);
3466  break;
3467  case 'FIND_IN_SET':
3468 
3469  case 'IFNULL':
3470 
3471  case 'LOCATE':
3472  // For the field, look for table mapping (generic):
3473  $t = $sqlPartArray[$k]['func']['table'] ? $sqlPartArray[$k]['func']['table'] : $defaultTable;
3474  $t = $this->getMappingKey($t);
3475  if (is_array($this->mapping[$t]['mapFieldNames']) && $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']]) {
3476  $sqlPartArray[$k]['func']['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['func']['field']];
3477  }
3478  if ($this->mapping[$t]['mapTableName']) {
3479  $sqlPartArray[$k]['func']['table'] = $this->mapping[$t]['mapTableName'];
3480  }
3481  break;
3482  }
3483  } else {
3484  // For the field, look for table mapping (generic):
3485  $t = $sqlPartArray[$k]['table'] ? $sqlPartArray[$k]['table'] : $defaultTable;
3486  $t = $this->getMappingKey($t);
3487  // Mapping field name, if set:
3488  if (is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']])) {
3489  $sqlPartArray[$k]['field'] = $this->mapping[$t]['mapFieldNames'][$sqlPartArray[$k]['field']];
3490  }
3491  // Mapping field name in SQL-functions like MIN(), MAX() or SUM()
3492  if ($this->mapping[$t]['mapFieldNames']) {
3493  $fieldArray = explode('.', $sqlPartArray[$k]['func_content']);
3494  if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
3495  $sqlPartArray[$k]['func_content.'][0]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3496  $sqlPartArray[$k]['func_content'] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3497  } elseif (count($fieldArray) == 2) {
3498  // Map the external table
3499  $table = $fieldArray[0];
3500  $tableKey = $this->getMappingKey($table);
3501  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3502  $table = $this->mapping[$tableKey]['mapTableName'];
3503  }
3504  // Map the field itself
3505  $field = $fieldArray[1];
3506  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3507  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3508  }
3509  $sqlPartArray[$k]['func_content.'][0]['func_content'] = $table . '.' . $field;
3510  $sqlPartArray[$k]['func_content'] = $table . '.' . $field;
3511  }
3512  // Mapping flow-control statements
3513  if (isset($sqlPartArray[$k]['flow-control'])) {
3514  if (isset($sqlPartArray[$k]['flow-control']['type'])) {
3515  $temp = array($sqlPartArray[$k]['flow-control']);
3516  $this->map_sqlParts($temp, $t);
3517  // Call recursively!
3518  $sqlPartArray[$k]['flow-control'] = $temp[0];
3519  }
3520  }
3521  }
3522  // Do we have a function (e.g., CONCAT)
3523  if (isset($v['value']['operator'])) {
3524  foreach ($sqlPartArray[$k]['value']['args'] as $argK => $fieldDef) {
3525  $tableKey = $this->getMappingKey($fieldDef['table']);
3526  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3527  $sqlPartArray[$k]['value']['args'][$argK]['table'] = $this->mapping[$tableKey]['mapTableName'];
3528  }
3529  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']])) {
3530  $sqlPartArray[$k]['value']['args'][$argK]['field'] = $this->mapping[$tableKey]['mapFieldNames'][$fieldDef['field']];
3531  }
3532  }
3533  }
3534  // Do we have a subquery (WHERE parts only)?
3535  if (isset($sqlPartArray[$k]['subquery'])) {
3536  $this->map_subquery($sqlPartArray[$k]['subquery']);
3537  }
3538  // do we have a field name in the value?
3539  // this is a very simplistic check, beware
3540  if (!is_numeric($sqlPartArray[$k]['value'][0]) && !isset($sqlPartArray[$k]['value'][1])) {
3541  $fieldArray = explode('.', $sqlPartArray[$k]['value'][0]);
3542  if (count($fieldArray) == 1 && is_array($this->mapping[$t]['mapFieldNames']) && isset($this->mapping[$t]['mapFieldNames'][$fieldArray[0]])) {
3543  $sqlPartArray[$k]['value'][0] = $this->mapping[$t]['mapFieldNames'][$fieldArray[0]];
3544  } elseif (count($fieldArray) == 2) {
3545  // Map the external table
3546  $table = $fieldArray[0];
3547  $tableKey = $this->getMappingKey($table);
3548  if (isset($this->mapping[$tableKey]['mapTableName'])) {
3549  $table = $this->mapping[$tableKey]['mapTableName'];
3550  }
3551  // Map the field itself
3552  $field = $fieldArray[1];
3553  if (is_array($this->mapping[$tableKey]['mapFieldNames']) && isset($this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]])) {
3554  $field = $this->mapping[$tableKey]['mapFieldNames'][$fieldArray[1]];
3555  }
3556  $sqlPartArray[$k]['value'][0] = $table . '.' . $field;
3557  }
3558  }
3559  // Map table?
3560  $tableKey = $this->getMappingKey($sqlPartArray[$k]['table']);
3561  if ($sqlPartArray[$k]['table'] && $this->mapping[$tableKey]['mapTableName']) {
3562  $sqlPartArray[$k]['table'] = $this->mapping[$tableKey]['mapTableName'];
3563  }
3564  }
3565  }
3566  }
3567  }
3568 
3575  protected function map_subquery(&$parsedQuery) {
3576  // Backup current mapping as it may be altered
3577  $backupMapping = $this->mapping;
3578  foreach ($parsedQuery['FROM'] as $k => $v) {
3579  $mappingKey = $v['table'];
3580  if ($v['as'] && is_array($this->mapping[$v['table']]['mapFieldNames'])) {
3581  $mappingKey = $this->getFreeMappingKey($v['as']);
3582  } else {
3583  // Should ensure that no alias is defined in the external query
3584  // which would correspond to a real table name in the subquery
3585  if ($this->getMappingKey($v['table']) !== $v['table']) {
3586  $mappingKey = $this->getFreeMappingKey($v['table']);
3587  // This is the only case when 'mapTableName' should be copied
3588  $this->mapping[$mappingKey]['mapTableName'] =& $this->mapping[$v['table']]['mapTableName'];
3589  }
3590  }
3591  if ($mappingKey !== $v['table']) {
3592  $this->mapping[$mappingKey]['mapFieldNames'] =& $this->mapping[$v['table']]['mapFieldNames'];
3593  }
3594  }
3595  // Perform subquery's remapping
3596  $defaultTable = $parsedQuery['FROM'][0]['table'];
3597  $this->map_sqlParts($parsedQuery['SELECT'], $defaultTable);
3598  $this->map_sqlParts($parsedQuery['FROM'], $defaultTable);
3599  $this->map_sqlParts($parsedQuery['WHERE'], $defaultTable);
3600  // Restore the mapping
3601  $this->mapping = $backupMapping;
3602  }
3603 
3613  protected function map_genericQueryParsed(&$parsedQuery) {
3614  // Getting table - same for all:
3615  $table = $parsedQuery['TABLE'];
3616  if (!$table) {
3617  throw new \InvalidArgumentException('ERROR, mapping: No table found in parsed Query array...', 1310028048);
3618  }
3619  // Do field mapping if needed:
3620  if ($tableArray = $this->map_needMapping($table)) {
3621  // Table name:
3622  if ($this->mapping[$table]['mapTableName']) {
3623  $parsedQuery['TABLE'] = $this->mapping[$table]['mapTableName'];
3624  }
3625  // Based on type, do additional changes:
3626  switch ($parsedQuery['type']) {
3627  case 'ALTERTABLE':
3628  // Changing field name:
3629  $newFieldName = $this->mapping[$table]['mapFieldNames'][$parsedQuery['FIELD']];
3630  if ($newFieldName) {
3631  if ($parsedQuery['FIELD'] == $parsedQuery['newField']) {
3632  $parsedQuery['FIELD'] = ($parsedQuery['newField'] = $newFieldName);
3633  } else {
3634  $parsedQuery['FIELD'] = $newFieldName;
3635  }
3636  }
3637  // Changing key field names:
3638  if (is_array($parsedQuery['fields'])) {
3639  $this->map_fieldNamesInArray($table, $parsedQuery['fields']);
3640  }
3641  break;
3642  case 'CREATETABLE':
3643  // Remapping fields:
3644  if (is_array($parsedQuery['FIELDS'])) {
3645  $newFieldsArray = array();
3646  foreach ($parsedQuery['FIELDS'] as $fN => $fInfo) {
3647  if ($this->mapping[$table]['mapFieldNames'][$fN]) {
3648  $fN = $this->mapping[$table]['mapFieldNames'][$fN];
3649  }
3650  $newFieldsArray[$fN] = $fInfo;
3651  }
3652  $parsedQuery['FIELDS'] = $newFieldsArray;
3653  }
3654  // Remapping keys:
3655  if (is_array($parsedQuery['KEYS'])) {
3656  foreach ($parsedQuery['KEYS'] as $kN => $kInfo) {
3657  $this->map_fieldNamesInArray($table, $parsedQuery['KEYS'][$kN]);
3658  }
3659  }
3660  break;
3661  }
3662  }
3663  }
3664 
3672  protected function map_fieldNamesInArray($table, &$fieldArray) {
3673  if (is_array($this->mapping[$table]['mapFieldNames'])) {
3674  foreach ($fieldArray as $k => $v) {
3675  if ($this->mapping[$table]['mapFieldNames'][$v]) {
3676  $fieldArray[$k] = $this->mapping[$table]['mapFieldNames'][$v];
3677  }
3678  }
3679  }
3680  }
3681 
3682  /**************************************
3683  *
3684  * Debugging
3685  *
3686  **************************************/
3696  public function debugHandler($function, $execTime, $inData) {
3697  // we don't want to log our own log/debug SQL
3699  if (substr($script, -strlen('dbal/mod1/index.php')) != 'dbal/mod1/index.php' && !strstr($inData['args'][0], 'tx_dbal_debuglog')) {
3700  $data = array();
3701  $errorFlag = 0;
3702  $joinTable = '';
3703  if ($this->sql_error()) {
3704  $data['sqlError'] = $this->sql_error();
3705  $errorFlag |= 1;
3706  }
3707  // if lastQuery is empty (for whatever reason) at least log inData.args
3708  if (empty($this->lastQuery)) {
3709  $query = implode(' ', $inData['args']);
3710  } else {
3711  $query = $this->lastQuery;
3712  }
3713  if ($this->conf['debugOptions']['numberRows']) {
3714  switch ($function) {
3715  case 'exec_INSERTquery':
3716 
3717  case 'exec_UPDATEquery':
3718 
3719  case 'exec_DELETEquery':
3720  $data['numberRows'] = $this->sql_affected_rows();
3721  break;
3722  case 'exec_SELECTquery':
3723  $data['numberRows'] = $inData['numberRows'];
3724  break;
3725  }
3726  }
3727  if ($this->conf['debugOptions']['backtrace']) {
3728  $backtrace = debug_backtrace();
3729  unset($backtrace[0]);
3730  // skip this very method :)
3731  $data['backtrace'] = array_slice($backtrace, 0, $this->conf['debugOptions']['backtrace']);
3732  }
3733  switch ($function) {
3734  case 'exec_INSERTquery':
3735 
3736  case 'exec_UPDATEquery':
3737 
3738  case 'exec_DELETEquery':
3739  $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script);
3740  break;
3741  case 'exec_SELECTquery':
3742  // Get explain data:
3743  if ($this->conf['debugOptions']['EXPLAIN'] && GeneralUtility::inList('adodb,native', $inData['handlerType'])) {
3744  $data['EXPLAIN'] = $this->debug_explain($this->lastQuery);
3745  }
3746  // Check parsing of Query:
3747  if ($this->conf['debugOptions']['parseQuery']) {
3748  $parseResults = array();
3749  $parseResults['SELECT'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][1]);
3750  $parseResults['FROM'] = $this->SQLparser->debug_parseSQLpart('FROM', $inData['args'][0]);
3751  $parseResults['WHERE'] = $this->SQLparser->debug_parseSQLpart('WHERE', $inData['args'][2]);
3752  $parseResults['GROUPBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][3]);
3753  // Using select field list syntax
3754  $parseResults['ORDERBY'] = $this->SQLparser->debug_parseSQLpart('SELECT', $inData['args'][4]);
3755  // Using select field list syntax
3756  foreach ($parseResults as $k => $v) {
3757  if (!strlen($parseResults[$k])) {
3758  unset($parseResults[$k]);
3759  }
3760  }
3761  if (count($parseResults)) {
3762  $data['parseError'] = $parseResults;
3763  $errorFlag |= 2;
3764  }
3765  }
3766  // Checking joinTables:
3767  if ($this->conf['debugOptions']['joinTables']) {
3768  if (count(explode(',', $inData['ORIG_from_table'])) > 1) {
3769  $joinTable = $inData['args'][0];
3770  }
3771  }
3772  // Logging it:
3773  $this->debug_log($query, $execTime, $data, $joinTable, $errorFlag, $script);
3774  if (!empty($inData['args'][2])) {
3775  $this->debug_WHERE($inData['args'][0], $inData['args'][2], $script);
3776  }
3777  break;
3778  }
3779  }
3780  }
3781 
3790  public function debug_WHERE($table, $where, $script = '') {
3791  $insertArray = array(
3792  'tstamp' => $GLOBALS['EXEC_TIME'],
3793  'beuser_id' => (int)$GLOBALS['BE_USER']->user['uid'],
3794  'script' => $script,
3795  'tablename' => $table,
3796  'whereclause' => $where
3797  );
3798  $this->exec_INSERTquery('tx_dbal_debuglog_where', $insertArray);
3799  }
3800 
3812  public function debug_log($query, $ms, $data, $join, $errorFlag, $script = '') {
3813  if (is_array($query)) {
3814  $queryToLog = $query[0] . ' -- ';
3815  if (count($query[1])) {
3816  $queryToLog .= count($query[1]) . ' BLOB FIELDS: ' . implode(', ', array_keys($query[1]));
3817  }
3818  if (count($query[2])) {
3819  $queryToLog .= count($query[2]) . ' CLOB FIELDS: ' . implode(', ', array_keys($query[2]));
3820  }
3821  } else {
3822  $queryToLog = $query;
3823  }
3824  $insertArray = array(
3825  'tstamp' => $GLOBALS['EXEC_TIME'],
3826  'beuser_id' => (int)$GLOBALS['BE_USER']->user['uid'],
3827  'script' => $script,
3828  'exec_time' => $ms,
3829  'table_join' => $join,
3830  'serdata' => serialize($data),
3831  'query' => $queryToLog,
3832  'errorFlag' => $errorFlag
3833  );
3834  $this->exec_INSERTquery('tx_dbal_debuglog', $insertArray);
3835  }
3836 
3843  public function debug_explain($query) {
3844  $output = array();
3845  $hType = (string)$this->handlerCfg[$this->lastHandlerKey]['type'];
3846  switch ($hType) {
3847  case 'native':
3848  $res = $this->sql_query('EXPLAIN ' . $query);
3849  while ($row = $this->sql_fetch_assoc($res)) {
3850  $output[] = $row;
3851  }
3852  break;
3853  case 'adodb':
3854  switch ($this->handlerCfg['_DEFAULT']['config']['driver']) {
3855  case 'oci8':
3856  $this->sql_query('EXPLAIN PLAN ' . $query);
3857  $output[] = 'EXPLAIN PLAN data logged to default PLAN_TABLE';
3858  break;
3859  default:
3860  $res = $this->sql_query('EXPLAIN ' . $query);
3861  while ($row = $this->sql_fetch_assoc($res)) {
3862  $output[] = $row;
3863  }
3864  }
3865  break;
3866  }
3867  return $output;
3868  }
3869 
3870 }
quoteName($name, $handlerKey=NULL, $useBackticks=FALSE)
$driver
Definition: server.php:34
static getCacheKey($config)
Definition: QueryCache.php:29
prepare_PREPAREDquery($query, array $queryComponents)
debugHandler($function, $execTime, $inData)
$sql
Definition: server.php:82
exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields=FALSE)
static devLog($msg, $extKey, $severity=0, $dataVar=FALSE)
if(isset($_REQUEST['nrows'])) else $rs
Definition: server.php:92
map_needMapping($tableList, $fieldMappingOnly=FALSE, array &$parsedTableList=array())
fullQuoteStr($str, $table, $allowNull=FALSE)
static intExplode($delimiter, $string, $removeEmptyValues=FALSE, $limit=0)
sql_pconnect($host=NULL, $username=NULL, $password=NULL)
INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields=FALSE)
debug_log($query, $ms, $data, $join, $errorFlag, $script='')
getQueryComponents($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit)
INSERTquery($table, $fields_values, $no_quote_fields=FALSE)
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
exec_INSERTquery($table, $fields_values, $no_quote_fields=FALSE)
handleDeprecatedConnectArguments($host=NULL, $username=NULL, $password=NULL, $db=NULL)
$host
Definition: server.php:35
map_remapSELECTQueryParts($select_fields, $from_table, $where_clause, $groupBy, $orderBy)
UPDATEquery($table, $where, $fields_values, $no_quote_fields=FALSE)
prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='', array $input_parameters=array())
exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='')
connectDB($host=NULL, $username=NULL, $password=NULL, $db=NULL)
SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='')
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields=FALSE)
static getFileAbsFileName($filename, $onlyRelative=TRUE, $relToTYPO3_mainDir=FALSE)
map_sqlParts(&$sqlPartArray, $defaultTable)