[CLEANUP] Add missing signature to DatabaseConnection phpDoc updateQuery
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / DatabaseConnection.php
1 <?php
2 namespace TYPO3\CMS\Core\Database;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Contains the class "DatabaseConnection" containing functions for building SQL queries
21 * and mysqli wrappers, thus providing a foundational API to all database
22 * interaction.
23 * This class is instantiated globally as $TYPO3_DB in TYPO3 scripts.
24 *
25 * TYPO3 "database wrapper" class (new in 3.6.0)
26 * This class contains
27 * - abstraction functions for executing INSERT/UPDATE/DELETE/SELECT queries ("Query execution"; These are REQUIRED for all future connectivity to the database, thus ensuring DBAL compliance!)
28 * - functions for building SQL queries (INSERT/UPDATE/DELETE/SELECT) ("Query building"); These are transitional functions for building SQL queries in a more automated way. Use these to build queries instead of doing it manually in your code!
29 * - mysqli wrapper functions; These are transitional functions. By a simple search/replace you should be able to substitute all mysql*() calls with $GLOBALS['TYPO3_DB']->sql*() and your application will work out of the box. YOU CANNOT (legally) use any mysqli functions not found as wrapper functions in this class!
30 * See the Project Coding Guidelines (doc_core_cgl) for more instructions on best-practise
31 *
32 * This class is not in itself a complete database abstraction layer but can be extended to be a DBAL (by extensions, see "dbal" for example)
33 * ALL connectivity to the database in TYPO3 must be done through this class!
34 * The points of this class are:
35 * - To direct all database calls through this class so it becomes possible to implement DBAL with extensions.
36 * - To keep it very easy to use for developers used to MySQL in PHP - and preserve as much performance as possible when TYPO3 is used with MySQL directly...
37 * - To create an interface for DBAL implemented by extensions; (Eg. making possible escaping characters, clob/blob handling, reserved words handling)
38 * - Benchmarking the DB bottleneck queries will become much easier; Will make it easier to find optimization possibilities.
39 *
40 * USE:
41 * In all TYPO3 scripts the global variable $TYPO3_DB is an instance of this class. Use that.
42 * Eg. $GLOBALS['TYPO3_DB']->sql_fetch_assoc()
43 *
44 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
45 */
46 class DatabaseConnection {
47
48 /**
49 * The AND constraint in where clause
50 *
51 * @var string
52 */
53 const AND_Constraint = 'AND';
54
55 /**
56 * The OR constraint in where clause
57 *
58 * @var string
59 */
60 const OR_Constraint = 'OR';
61
62 /**
63 * Set "TRUE" or "1" if you want database errors outputted. Set to "2" if you also want successful database actions outputted.
64 *
65 * @var bool|int
66 */
67 public $debugOutput = FALSE;
68
69 /**
70 * Internally: Set to last built query (not necessarily executed...)
71 *
72 * @var string
73 */
74 public $debug_lastBuiltQuery = '';
75
76 /**
77 * Set "TRUE" if you want the last built query to be stored in $debug_lastBuiltQuery independent of $this->debugOutput
78 *
79 * @var bool
80 */
81 public $store_lastBuiltQuery = FALSE;
82
83 /**
84 * Set this to 1 to get queries explained (devIPmask must match). Set the value to 2 to the same but disregarding the devIPmask.
85 * There is an alternative option to enable explain output in the admin panel under "TypoScript", which will produce much nicer output, but only works in FE.
86 *
87 * @var bool
88 */
89 public $explainOutput = 0;
90
91 /**
92 * @var string Database host to connect to
93 */
94 protected $databaseHost = '';
95
96 /**
97 * @var int Database port to connect to
98 */
99 protected $databasePort = 3306;
100
101 /**
102 * @var string|NULL Database socket to connect to
103 */
104 protected $databaseSocket = NULL;
105
106 /**
107 * @var string Database name to connect to
108 */
109 protected $databaseName = '';
110
111 /**
112 * @var string Database user to connect with
113 */
114 protected $databaseUsername = '';
115
116 /**
117 * @var string Database password to connect with
118 */
119 protected $databaseUserPassword = '';
120
121 /**
122 * @var bool TRUE if database connection should be persistent
123 * @see http://php.net/manual/de/mysqli.persistconns.php
124 */
125 protected $persistentDatabaseConnection = FALSE;
126
127 /**
128 * @var bool TRUE if connection between client and sql server is compressed
129 */
130 protected $connectionCompression = FALSE;
131
132 /**
133 * The charset for the connection; will be passed on to
134 * mysqli_set_charset during connection initialization.
135 *
136 * @var string
137 */
138 protected $connectionCharset = 'utf8';
139
140 /**
141 * @var array List of commands executed after connection was established
142 */
143 protected $initializeCommandsAfterConnect = array();
144
145 /**
146 * @var bool TRUE if database connection is established
147 */
148 protected $isConnected = FALSE;
149
150 /**
151 * @var \mysqli $link Default database link object
152 */
153 protected $link = NULL;
154
155 /**
156 * Default character set, applies unless character set or collation are explicitly set
157 *
158 * @var string
159 */
160 public $default_charset = 'utf8';
161
162 /**
163 * @var array<PostProcessQueryHookInterface>
164 */
165 protected $preProcessHookObjects = array();
166
167 /**
168 * @var array<PreProcessQueryHookInterface>
169 */
170 protected $postProcessHookObjects = array();
171
172 /**
173 * the date and time formats compatible with the database in general
174 *
175 * @var array
176 */
177 static protected $dateTimeFormats = array(
178 'date' => array(
179 'empty' => '0000-00-00',
180 'format' => 'Y-m-d'
181 ),
182 'datetime' => array(
183 'empty' => '0000-00-00 00:00:00',
184 'format' => 'Y-m-d H:i:s'
185 )
186 );
187
188 /**
189 * Initialize the database connection
190 *
191 * @return void
192 */
193 public function initialize() {
194 // Intentionally blank as this will be overloaded by DBAL
195 }
196
197 /************************************
198 *
199 * Query execution
200 *
201 * These functions are the RECOMMENDED DBAL functions for use in your applications
202 * Using these functions will allow the DBAL to use alternative ways of accessing data (contrary to if a query is returned!)
203 * They compile a query AND execute it immediately and then return the result
204 * This principle heightens our ability to create various forms of DBAL of the functions.
205 * Generally: We want to return a result pointer/object, never queries.
206 * Also, having the table name together with the actual query execution allows us to direct the request to other databases.
207 *
208 **************************************/
209
210 /**
211 * Creates and executes an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
212 * Using this function specifically allows us to handle BLOB and CLOB fields depending on DB
213 *
214 * @param string $table Table name
215 * @param array $fields_values Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$insertFields" with 'fieldname'=>'value' and pass it to this function as argument.
216 * @param bool|array|string $no_quote_fields See fullQuoteArray()
217 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
218 */
219 public function exec_INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
220 $res = $this->query($this->INSERTquery($table, $fields_values, $no_quote_fields));
221 if ($this->debugOutput) {
222 $this->debug('exec_INSERTquery');
223 }
224 foreach ($this->postProcessHookObjects as $hookObject) {
225 /** @var $hookObject PostProcessQueryHookInterface */
226 $hookObject->exec_INSERTquery_postProcessAction($table, $fields_values, $no_quote_fields, $this);
227 }
228 return $res;
229 }
230
231 /**
232 * Creates and executes an INSERT SQL-statement for $table with multiple rows.
233 *
234 * @param string $table Table name
235 * @param array $fields Field names
236 * @param array $rows Table rows. Each row should be an array with field values mapping to $fields
237 * @param bool|array|string $no_quote_fields See fullQuoteArray()
238 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
239 */
240 public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
241 $res = $this->query($this->INSERTmultipleRows($table, $fields, $rows, $no_quote_fields));
242 if ($this->debugOutput) {
243 $this->debug('exec_INSERTmultipleRows');
244 }
245 foreach ($this->postProcessHookObjects as $hookObject) {
246 /** @var $hookObject PostProcessQueryHookInterface */
247 $hookObject->exec_INSERTmultipleRows_postProcessAction($table, $fields, $rows, $no_quote_fields, $this);
248 }
249 return $res;
250 }
251
252 /**
253 * Creates and executes an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values.
254 * Using this function specifically allow us to handle BLOB and CLOB fields depending on DB
255 *
256 * @param string $table Database tablename
257 * @param string $where WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
258 * @param array $fields_values Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$updateFields" with 'fieldname'=>'value' and pass it to this function as argument.
259 * @param bool|array|string $no_quote_fields See fullQuoteArray()
260 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
261 */
262 public function exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
263 $res = $this->query($this->UPDATEquery($table, $where, $fields_values, $no_quote_fields));
264 if ($this->debugOutput) {
265 $this->debug('exec_UPDATEquery');
266 }
267 foreach ($this->postProcessHookObjects as $hookObject) {
268 /** @var $hookObject PostProcessQueryHookInterface */
269 $hookObject->exec_UPDATEquery_postProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
270 }
271 return $res;
272 }
273
274 /**
275 * Creates and executes a DELETE SQL-statement for $table where $where-clause
276 *
277 * @param string $table Database tablename
278 * @param string $where WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
279 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
280 */
281 public function exec_DELETEquery($table, $where) {
282 $res = $this->query($this->DELETEquery($table, $where));
283 if ($this->debugOutput) {
284 $this->debug('exec_DELETEquery');
285 }
286 foreach ($this->postProcessHookObjects as $hookObject) {
287 /** @var $hookObject PostProcessQueryHookInterface */
288 $hookObject->exec_DELETEquery_postProcessAction($table, $where, $this);
289 }
290 return $res;
291 }
292
293 /**
294 * Creates and executes a SELECT SQL-statement
295 * Using this function specifically allow us to handle the LIMIT feature independently of DB.
296 *
297 * @param string $select_fields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
298 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
299 * @param string $where_clause Additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT!
300 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
301 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
302 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
303 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
304 */
305 public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
306 $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
307 $res = $this->query($query);
308 if ($this->debugOutput) {
309 $this->debug('exec_SELECTquery');
310 }
311 if ($this->explainOutput) {
312 $this->explain($query, $from_table, $res->num_rows);
313 }
314 foreach ($this->postProcessHookObjects as $hookObject) {
315 /** @var $hookObject PostProcessQueryHookInterface */
316 $hookObject->exec_SELECTquery_postProcessAction($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $this);
317 }
318 return $res;
319 }
320
321 /**
322 * Creates and executes a SELECT query, selecting fields ($select) from two/three tables joined
323 * Use $mm_table together with $local_table or $foreign_table to select over two tables. Or use all three tables to select the full MM-relation.
324 * The JOIN is done with [$local_table].uid <--> [$mm_table].uid_local / [$mm_table].uid_foreign <--> [$foreign_table].uid
325 * The function is very useful for selecting MM-relations between tables adhering to the MM-format used by TCE (TYPO3 Core Engine). See the section on $GLOBALS['TCA'] in Inside TYPO3 for more details.
326 *
327 * @param string $select Field list for SELECT
328 * @param string $local_table Tablename, local table
329 * @param string $mm_table Tablename, relation table
330 * @param string $foreign_table Tablename, foreign table
331 * @param string $whereClause Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT! You have to prepend 'AND ' to this parameter yourself!
332 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
333 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
334 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
335 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
336 * @see exec_SELECTquery()
337 */
338 public function exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') {
339 $foreign_table_as = $foreign_table == $local_table ? $foreign_table . str_replace('.', '', uniqid('_join', TRUE)) : '';
340 $mmWhere = $local_table ? $local_table . '.uid=' . $mm_table . '.uid_local' : '';
341 $mmWhere .= ($local_table and $foreign_table) ? ' AND ' : '';
342 $tables = ($local_table ? $local_table . ',' : '') . $mm_table;
343 if ($foreign_table) {
344 $mmWhere .= ($foreign_table_as ?: $foreign_table) . '.uid=' . $mm_table . '.uid_foreign';
345 $tables .= ',' . $foreign_table . ($foreign_table_as ? ' AS ' . $foreign_table_as : '');
346 }
347 return $this->exec_SELECTquery($select, $tables, $mmWhere . ' ' . $whereClause, $groupBy, $orderBy, $limit);
348 }
349
350 /**
351 * Executes a select based on input query parts array
352 *
353 * @param array $queryParts Query parts array
354 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
355 * @see exec_SELECTquery()
356 */
357 public function exec_SELECT_queryArray($queryParts) {
358 return $this->exec_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
359 }
360
361 /**
362 * Creates and executes a SELECT SQL-statement AND traverse result set and returns array with records in.
363 *
364 * @param string $select_fields See exec_SELECTquery()
365 * @param string $from_table See exec_SELECTquery()
366 * @param string $where_clause See exec_SELECTquery()
367 * @param string $groupBy See exec_SELECTquery()
368 * @param string $orderBy See exec_SELECTquery()
369 * @param string $limit See exec_SELECTquery()
370 * @param string $uidIndexField If set, the result array will carry this field names value as index. Requires that field to be selected of course!
371 * @return array|NULL Array of rows, or NULL in case of SQL error
372 */
373 public function exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $uidIndexField = '') {
374 $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
375 if ($this->debugOutput) {
376 $this->debug('exec_SELECTquery');
377 }
378 if (!$this->sql_error()) {
379 $output = array();
380 if ($uidIndexField) {
381 while ($tempRow = $this->sql_fetch_assoc($res)) {
382 $output[$tempRow[$uidIndexField]] = $tempRow;
383 }
384 } else {
385 while ($output[] = $this->sql_fetch_assoc($res)) {
386
387 }
388 array_pop($output);
389 }
390 $this->sql_free_result($res);
391 } else {
392 $output = NULL;
393 }
394 return $output;
395 }
396
397 /**
398 * Creates and executes a SELECT SQL-statement AND gets a result set and returns an array with a single record in.
399 * LIMIT is automatically set to 1 and can not be overridden.
400 *
401 * @param string $select_fields List of fields to select from the table.
402 * @param string $from_table Table(s) from which to select.
403 * @param string $where_clause Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
404 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
405 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
406 * @param bool $numIndex If set, the result will be fetched with sql_fetch_row, otherwise sql_fetch_assoc will be used.
407 * @return array|FALSE|NULL Single row, FALSE on empty result, NULL on error
408 */
409 public function exec_SELECTgetSingleRow($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $numIndex = FALSE) {
410 $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, '1');
411 if ($this->debugOutput) {
412 $this->debug('exec_SELECTquery');
413 }
414 $output = NULL;
415 if ($res !== FALSE) {
416 if ($numIndex) {
417 $output = $this->sql_fetch_row($res);
418 } else {
419 $output = $this->sql_fetch_assoc($res);
420 }
421 $this->sql_free_result($res);
422 }
423 return $output;
424 }
425
426 /**
427 * Counts the number of rows in a table.
428 *
429 * @param string $field Name of the field to use in the COUNT() expression (e.g. '*')
430 * @param string $table Name of the table to count rows for
431 * @param string $where (optional) WHERE statement of the query
432 * @return mixed Number of rows counter (int) or FALSE if something went wrong (bool)
433 */
434 public function exec_SELECTcountRows($field, $table, $where = '') {
435 $count = FALSE;
436 $resultSet = $this->exec_SELECTquery('COUNT(' . $field . ')', $table, $where);
437 if ($resultSet !== FALSE) {
438 list($count) = $this->sql_fetch_row($resultSet);
439 $count = (int)$count;
440 $this->sql_free_result($resultSet);
441 }
442 return $count;
443 }
444
445 /**
446 * Truncates a table.
447 *
448 * @param string $table Database tablename
449 * @return mixed Result from handler
450 */
451 public function exec_TRUNCATEquery($table) {
452 $res = $this->query($this->TRUNCATEquery($table));
453 if ($this->debugOutput) {
454 $this->debug('exec_TRUNCATEquery');
455 }
456 foreach ($this->postProcessHookObjects as $hookObject) {
457 /** @var $hookObject PostProcessQueryHookInterface */
458 $hookObject->exec_TRUNCATEquery_postProcessAction($table, $this);
459 }
460 return $res;
461 }
462
463 /**
464 * Central query method. Also checks if there is a database connection.
465 * Use this to execute database queries instead of directly calling $this->link->query()
466 *
467 * @param string $query The query to send to the database
468 * @return bool|\mysqli_result
469 */
470 protected function query($query) {
471 if (!$this->isConnected) {
472 $this->connectDB();
473 }
474 return $this->link->query($query);
475 }
476
477 /**************************************
478 *
479 * Query building
480 *
481 **************************************/
482 /**
483 * Creates an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
484 *
485 * @param string $table See exec_INSERTquery()
486 * @param array $fields_values See exec_INSERTquery()
487 * @param bool|array|string $no_quote_fields See fullQuoteArray()
488 * @return string|NULL Full SQL query for INSERT, NULL if $fields_values is empty
489 */
490 public function INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
491 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
492 // function (contrary to values in the arrays which may be insecure).
493 if (!is_array($fields_values) || count($fields_values) === 0) {
494 return NULL;
495 }
496 foreach ($this->preProcessHookObjects as $hookObject) {
497 $hookObject->INSERTquery_preProcessAction($table, $fields_values, $no_quote_fields, $this);
498 }
499 // Quote and escape values
500 $fields_values = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
501 // Build query
502 $query = 'INSERT INTO ' . $table . ' (' . implode(',', array_keys($fields_values)) . ') VALUES ' . '(' . implode(',', $fields_values) . ')';
503 // Return query
504 if ($this->debugOutput || $this->store_lastBuiltQuery) {
505 $this->debug_lastBuiltQuery = $query;
506 }
507 return $query;
508 }
509
510 /**
511 * Creates an INSERT SQL-statement for $table with multiple rows.
512 *
513 * @param string $table Table name
514 * @param array $fields Field names
515 * @param array $rows Table rows. Each row should be an array with field values mapping to $fields
516 * @param bool|array|string $no_quote_fields See fullQuoteArray()
517 * @return string|NULL Full SQL query for INSERT, NULL if $rows is empty
518 */
519 public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
520 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
521 // function (contrary to values in the arrays which may be insecure).
522 if (count($rows) === 0) {
523 return NULL;
524 }
525 foreach ($this->preProcessHookObjects as $hookObject) {
526 /** @var $hookObject PreProcessQueryHookInterface */
527 $hookObject->INSERTmultipleRows_preProcessAction($table, $fields, $rows, $no_quote_fields, $this);
528 }
529 // Build query
530 $query = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES ';
531 $rowSQL = array();
532 foreach ($rows as $row) {
533 // Quote and escape values
534 $row = $this->fullQuoteArray($row, $table, $no_quote_fields);
535 $rowSQL[] = '(' . implode(', ', $row) . ')';
536 }
537 $query .= implode(', ', $rowSQL);
538 // Return query
539 if ($this->debugOutput || $this->store_lastBuiltQuery) {
540 $this->debug_lastBuiltQuery = $query;
541 }
542 return $query;
543 }
544
545 /**
546 * Creates an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values.
547 *
548 *
549 * @param string $table See exec_UPDATEquery()
550 * @param string $where See exec_UPDATEquery()
551 * @param array $fields_values See exec_UPDATEquery()
552 * @param bool|array|string $no_quote_fields See fullQuoteArray()
553 * @throws \InvalidArgumentException
554 * @return string Full SQL query for UPDATE
555 */
556 public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
557 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
558 // function (contrary to values in the arrays which may be insecure).
559 if (is_string($where)) {
560 foreach ($this->preProcessHookObjects as $hookObject) {
561 /** @var $hookObject PreProcessQueryHookInterface */
562 $hookObject->UPDATEquery_preProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
563 }
564 $fields = array();
565 if (is_array($fields_values) && count($fields_values)) {
566 // Quote and escape values
567 $nArr = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
568 foreach ($nArr as $k => $v) {
569 $fields[] = $k . '=' . $v;
570 }
571 }
572 // Build query
573 $query = 'UPDATE ' . $table . ' SET ' . implode(',', $fields) . ((string)$where !== '' ? ' WHERE ' . $where : '');
574 if ($this->debugOutput || $this->store_lastBuiltQuery) {
575 $this->debug_lastBuiltQuery = $query;
576 }
577 return $query;
578 } else {
579 throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 1270853880);
580 }
581 }
582
583 /**
584 * Creates a DELETE SQL-statement for $table where $where-clause
585 *
586 * @param string $table See exec_DELETEquery()
587 * @param string $where See exec_DELETEquery()
588 * @return string Full SQL query for DELETE
589 * @throws \InvalidArgumentException
590 */
591 public function DELETEquery($table, $where) {
592 if (is_string($where)) {
593 foreach ($this->preProcessHookObjects as $hookObject) {
594 /** @var $hookObject PreProcessQueryHookInterface */
595 $hookObject->DELETEquery_preProcessAction($table, $where, $this);
596 }
597 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
598 $query = 'DELETE FROM ' . $table . ((string)$where !== '' ? ' WHERE ' . $where : '');
599 if ($this->debugOutput || $this->store_lastBuiltQuery) {
600 $this->debug_lastBuiltQuery = $query;
601 }
602 return $query;
603 } else {
604 throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !', 1270853881);
605 }
606 }
607
608 /**
609 * Creates a SELECT SQL-statement
610 *
611 * @param string $select_fields See exec_SELECTquery()
612 * @param string $from_table See exec_SELECTquery()
613 * @param string $where_clause See exec_SELECTquery()
614 * @param string $groupBy See exec_SELECTquery()
615 * @param string $orderBy See exec_SELECTquery()
616 * @param string $limit See exec_SELECTquery()
617 * @return string Full SQL query for SELECT
618 */
619 public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
620 foreach ($this->preProcessHookObjects as $hookObject) {
621 /** @var $hookObject PreProcessQueryHookInterface */
622 $hookObject->SELECTquery_preProcessAction($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit, $this);
623 }
624 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
625 // Build basic query
626 $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
627 // Group by
628 $query .= (string)$groupBy !== '' ? ' GROUP BY ' . $groupBy : '';
629 // Order by
630 $query .= (string)$orderBy !== '' ? ' ORDER BY ' . $orderBy : '';
631 // Group by
632 $query .= (string)$limit !== '' ? ' LIMIT ' . $limit : '';
633 // Return query
634 if ($this->debugOutput || $this->store_lastBuiltQuery) {
635 $this->debug_lastBuiltQuery = $query;
636 }
637 return $query;
638 }
639
640 /**
641 * Creates a SELECT SQL-statement to be used as subquery within another query.
642 * BEWARE: This method should not be overriden within DBAL to prevent quoting from happening.
643 *
644 * @param string $select_fields List of fields to select from the table.
645 * @param string $from_table Table from which to select.
646 * @param string $where_clause Conditional WHERE statement
647 * @return string Full SQL query for SELECT
648 */
649 public function SELECTsubquery($select_fields, $from_table, $where_clause) {
650 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
651 // Build basic query:
652 $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
653 // Return query
654 if ($this->debugOutput || $this->store_lastBuiltQuery) {
655 $this->debug_lastBuiltQuery = $query;
656 }
657 return $query;
658 }
659
660 /**
661 * Creates a TRUNCATE TABLE SQL-statement
662 *
663 * @param string $table See exec_TRUNCATEquery()
664 * @return string Full SQL query for TRUNCATE TABLE
665 */
666 public function TRUNCATEquery($table) {
667 foreach ($this->preProcessHookObjects as $hookObject) {
668 /** @var $hookObject PreProcessQueryHookInterface */
669 $hookObject->TRUNCATEquery_preProcessAction($table, $this);
670 }
671 // Table should be "SQL-injection-safe" when supplied to this function
672 // Build basic query:
673 $query = 'TRUNCATE TABLE ' . $table;
674 // Return query:
675 if ($this->debugOutput || $this->store_lastBuiltQuery) {
676 $this->debug_lastBuiltQuery = $query;
677 }
678 return $query;
679 }
680
681 /**
682 * Returns a WHERE clause that can find a value ($value) in a list field ($field)
683 * For instance a record in the database might contain a list of numbers,
684 * "34,234,5" (with no spaces between). This query would be able to select that
685 * record based on the value "34", "234" or "5" regardless of their position in
686 * the list (left, middle or right).
687 * The value must not contain a comma (,)
688 * Is nice to look up list-relations to records or files in TYPO3 database tables.
689 *
690 * @param string $field Field name
691 * @param string $value Value to find in list
692 * @param string $table Table in which we are searching (for DBAL detection of quoteStr() method)
693 * @return string WHERE clause for a query
694 * @throws \InvalidArgumentException
695 */
696 public function listQuery($field, $value, $table) {
697 $value = (string)$value;
698 if (strpos($value, ',') !== FALSE) {
699 throw new \InvalidArgumentException('$value must not contain a comma (,) in $this->listQuery() !', 1294585862);
700 }
701 $pattern = $this->quoteStr($value, $table);
702 $where = 'FIND_IN_SET(\'' . $pattern . '\',' . $field . ')';
703 return $where;
704 }
705
706 /**
707 * Returns a WHERE clause which will make an AND or OR search for the words in the $searchWords array in any of the fields in array $fields.
708 *
709 * @param array $searchWords Array of search words
710 * @param array $fields Array of fields
711 * @param string $table Table in which we are searching (for DBAL detection of quoteStr() method)
712 * @param string $constraint How multiple search words have to match ('AND' or 'OR')
713 * @return string WHERE clause for search
714 */
715 public function searchQuery($searchWords, $fields, $table, $constraint = self::AND_Constraint) {
716 switch ($constraint) {
717 case self::OR_Constraint:
718 $constraint = 'OR';
719 break;
720 default:
721 $constraint = 'AND';
722 }
723
724 $queryParts = array();
725 foreach ($searchWords as $sw) {
726 $like = ' LIKE \'%' . $this->quoteStr($sw, $table) . '%\'';
727 $queryParts[] = $table . '.' . implode(($like . ' OR ' . $table . '.'), $fields) . $like;
728 }
729 $query = '(' . implode(') ' . $constraint . ' (', $queryParts) . ')';
730
731 return $query;
732 }
733
734 /**************************************
735 *
736 * Prepared Query Support
737 *
738 **************************************/
739 /**
740 * Creates a SELECT prepared SQL statement.
741 *
742 * @param string $select_fields See exec_SELECTquery()
743 * @param string $from_table See exec_SELECTquery()
744 * @param string $where_clause See exec_SELECTquery()
745 * @param string $groupBy See exec_SELECTquery()
746 * @param string $orderBy See exec_SELECTquery()
747 * @param string $limit See exec_SELECTquery()
748 * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE.
749 * @return \TYPO3\CMS\Core\Database\PreparedStatement Prepared statement
750 */
751 public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) {
752 $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
753 /** @var $preparedStatement \TYPO3\CMS\Core\Database\PreparedStatement */
754 $preparedStatement = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\PreparedStatement::class, $query, $from_table, array());
755 // Bind values to parameters
756 foreach ($input_parameters as $key => $value) {
757 $preparedStatement->bindValue($key, $value, PreparedStatement::PARAM_AUTOTYPE);
758 }
759 // Return prepared statement
760 return $preparedStatement;
761 }
762
763 /**
764 * Creates a SELECT prepared SQL statement based on input query parts array
765 *
766 * @param array $queryParts Query parts array
767 * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE.
768 * @return \TYPO3\CMS\Core\Database\PreparedStatement Prepared statement
769 */
770 public function prepare_SELECTqueryArray(array $queryParts, array $input_parameters = array()) {
771 return $this->prepare_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT'], $input_parameters);
772 }
773
774 /**
775 * Prepares a prepared query.
776 *
777 * @param string $query The query to execute
778 * @param array $queryComponents The components of the query to execute
779 * @return \mysqli_stmt|object MySQLi statement / DBAL object
780 * @internal This method may only be called by \TYPO3\CMS\Core\Database\PreparedStatement
781 */
782 public function prepare_PREPAREDquery($query, array $queryComponents) {
783 if (!$this->isConnected) {
784 $this->connectDB();
785 }
786 $stmt = $this->link->stmt_init();
787 $success = $stmt->prepare($query);
788 if ($this->debugOutput) {
789 $this->debug('stmt_execute', $query);
790 }
791 return $success ? $stmt : NULL;
792 }
793
794 /**************************************
795 *
796 * Various helper functions
797 *
798 * Functions recommended to be used for
799 * - escaping values,
800 * - cleaning lists of values,
801 * - stripping of excess ORDER BY/GROUP BY keywords
802 *
803 **************************************/
804 /**
805 * Escaping and quoting values for SQL statements.
806 *
807 * @param string $str Input string
808 * @param string $table Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
809 * @param bool $allowNull Whether to allow NULL values
810 * @return string Output string; Wrapped in single quotes and quotes in the string (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
811 * @see quoteStr()
812 */
813 public function fullQuoteStr($str, $table, $allowNull = FALSE) {
814 if (!$this->isConnected) {
815 $this->connectDB();
816 }
817 if ($allowNull && $str === NULL) {
818 return 'NULL';
819 }
820
821 return '\'' . $this->link->real_escape_string($str) . '\'';
822 }
823
824 /**
825 * Will fullquote all values in the one-dimensional array so they are ready to "implode" for an sql query.
826 *
827 * @param array $arr Array with values (either associative or non-associative array)
828 * @param string $table Table name for which to quote
829 * @param bool|array $noQuote List/array of keys NOT to quote (eg. SQL functions) - ONLY for associative arrays
830 * @param bool $allowNull Whether to allow NULL values
831 * @return array The input array with the values quoted
832 * @see cleanIntArray()
833 */
834 public function fullQuoteArray($arr, $table, $noQuote = FALSE, $allowNull = FALSE) {
835 if (is_string($noQuote)) {
836 $noQuote = explode(',', $noQuote);
837 } elseif (!is_array($noQuote)) {
838 $noQuote = FALSE;
839 }
840 foreach ($arr as $k => $v) {
841 if ($noQuote === FALSE || !in_array($k, $noQuote)) {
842 $arr[$k] = $this->fullQuoteStr($v, $table, $allowNull);
843 }
844 }
845 return $arr;
846 }
847
848 /**
849 * Substitution for PHP function "addslashes()"
850 * Use this function instead of the PHP addslashes() function when you build queries - this will prepare your code for DBAL.
851 * NOTICE: You must wrap the output of this function in SINGLE QUOTES to be DBAL compatible. Unless you have to apply the single quotes yourself you should rather use ->fullQuoteStr()!
852 *
853 * @param string $str Input string
854 * @param string $table Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
855 * @return string Output string; Quotes (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
856 * @see quoteStr()
857 */
858 public function quoteStr($str, $table) {
859 if (!$this->isConnected) {
860 $this->connectDB();
861 }
862 return $this->link->real_escape_string($str);
863 }
864
865 /**
866 * Escaping values for SQL LIKE statements.
867 *
868 * @param string $str Input string
869 * @param string $table Table name for which to escape string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
870 * @return string Output string; % and _ will be escaped with \ (or otherwise based on DBAL handler)
871 * @see quoteStr()
872 */
873 public function escapeStrForLike($str, $table) {
874 return addcslashes($str, '_%');
875 }
876
877 /**
878 * Will convert all values in the one-dimensional array to integers.
879 * Useful when you want to make sure an array contains only integers before imploding them in a select-list.
880 *
881 * @param array $arr Array with values
882 * @return array The input array with all values cast to (int)
883 * @see cleanIntList()
884 */
885 public function cleanIntArray($arr) {
886 return array_map('intval', $arr);
887 }
888
889 /**
890 * Will force all entries in the input comma list to integers
891 * Useful when you want to make sure a commalist of supposed integers really contain only integers; You want to know that when you don't trust content that could go into an SQL statement.
892 *
893 * @param string $list List of comma-separated values which should be integers
894 * @return string The input list but with every value cast to (int)
895 * @see cleanIntArray()
896 */
897 public function cleanIntList($list) {
898 return implode(',', GeneralUtility::intExplode(',', $list));
899 }
900
901 /**
902 * Removes the prefix "ORDER BY" from the input string.
903 * This function is used when you call the exec_SELECTquery() function and want to pass the ORDER BY parameter by can't guarantee that "ORDER BY" is not prefixed.
904 * Generally; This function provides a work-around to the situation where you cannot pass only the fields by which to order the result.
905 *
906 * @param string $str eg. "ORDER BY title, uid
907 * @return string eg. "title, uid
908 * @see exec_SELECTquery(), stripGroupBy()
909 */
910 public function stripOrderBy($str) {
911 return preg_replace('/^(?:ORDER[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
912 }
913
914 /**
915 * Removes the prefix "GROUP BY" from the input string.
916 * This function is used when you call the SELECTquery() function and want to pass the GROUP BY parameter by can't guarantee that "GROUP BY" is not prefixed.
917 * Generally; This function provides a work-around to the situation where you cannot pass only the fields by which to order the result.
918 *
919 * @param string $str eg. "GROUP BY title, uid
920 * @return string eg. "title, uid
921 * @see exec_SELECTquery(), stripOrderBy()
922 */
923 public function stripGroupBy($str) {
924 return preg_replace('/^(?:GROUP[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
925 }
926
927 /**
928 * Takes the last part of a query, eg. "... uid=123 GROUP BY title ORDER BY title LIMIT 5,2" and splits each part into a table (WHERE, GROUPBY, ORDERBY, LIMIT)
929 * Work-around function for use where you know some userdefined end to an SQL clause is supplied and you need to separate these factors.
930 *
931 * @param string $str Input string
932 * @return array
933 */
934 public function splitGroupOrderLimit($str) {
935 // Prepending a space to make sure "[[:space:]]+" will find a space there
936 // for the first element.
937 $str = ' ' . $str;
938 // Init output array:
939 $wgolParts = array(
940 'WHERE' => '',
941 'GROUPBY' => '',
942 'ORDERBY' => '',
943 'LIMIT' => ''
944 );
945 // Find LIMIT
946 $reg = array();
947 if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
948 $wgolParts['LIMIT'] = trim($reg[2]);
949 $str = $reg[1];
950 }
951 // Find ORDER BY
952 $reg = array();
953 if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
954 $wgolParts['ORDERBY'] = trim($reg[2]);
955 $str = $reg[1];
956 }
957 // Find GROUP BY
958 $reg = array();
959 if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
960 $wgolParts['GROUPBY'] = trim($reg[2]);
961 $str = $reg[1];
962 }
963 // Rest is assumed to be "WHERE" clause
964 $wgolParts['WHERE'] = $str;
965 return $wgolParts;
966 }
967
968 /**
969 * Returns the date and time formats compatible with the given database table.
970 *
971 * @param string $table Table name for which to return an empty date. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how date and time should be formatted).
972 * @return array
973 */
974 public function getDateTimeFormats($table) {
975 return self::$dateTimeFormats;
976 }
977
978 /**************************************
979 *
980 * MySQL(i) wrapper functions
981 * (For use in your applications)
982 *
983 **************************************/
984 /**
985 * Executes query
986 * MySQLi query() wrapper function
987 * Beware: Use of this method should be avoided as it is experimentally supported by DBAL. You should consider
988 * using exec_SELECTquery() and similar methods instead.
989 *
990 * @param string $query Query to execute
991 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
992 */
993 public function sql_query($query) {
994 $res = $this->query($query);
995 if ($this->debugOutput) {
996 $this->debug('sql_query', $query);
997 }
998 return $res;
999 }
1000
1001 /**
1002 * Returns the error status on the last query() execution
1003 *
1004 * @return string MySQLi error string.
1005 */
1006 public function sql_error() {
1007 return $this->link->error;
1008 }
1009
1010 /**
1011 * Returns the error number on the last query() execution
1012 *
1013 * @return int MySQLi error number
1014 */
1015 public function sql_errno() {
1016 return $this->link->errno;
1017 }
1018
1019 /**
1020 * Returns the number of selected rows.
1021 *
1022 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1023 * @return int Number of resulting rows
1024 */
1025 public function sql_num_rows($res) {
1026 if ($this->debug_check_recordset($res)) {
1027 return $res->num_rows;
1028 } else {
1029 return FALSE;
1030 }
1031 }
1032
1033 /**
1034 * Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows.
1035 * MySQLi fetch_assoc() wrapper function
1036 *
1037 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1038 * @return array|boolean Associative array of result row.
1039 */
1040 public function sql_fetch_assoc($res) {
1041 if ($this->debug_check_recordset($res)) {
1042 $result = $res->fetch_assoc();
1043 if ($result === NULL) {
1044 // Needed for compatibility
1045 $result = FALSE;
1046 }
1047 return $result;
1048 } else {
1049 return FALSE;
1050 }
1051 }
1052
1053 /**
1054 * Returns an array that corresponds to the fetched row, or FALSE if there are no more rows.
1055 * The array contains the values in numerical indices.
1056 * MySQLi fetch_row() wrapper function
1057 *
1058 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1059 * @return array|boolean Array with result rows.
1060 */
1061 public function sql_fetch_row($res) {
1062 if ($this->debug_check_recordset($res)) {
1063 $result = $res->fetch_row();
1064 if ($result === NULL) {
1065 // Needed for compatibility
1066 $result = FALSE;
1067 }
1068 return $result;
1069 } else {
1070 return FALSE;
1071 }
1072 }
1073
1074 /**
1075 * Free result memory
1076 * free_result() wrapper function
1077 *
1078 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1079 * @return bool Returns TRUE on success or FALSE on failure.
1080 */
1081 public function sql_free_result($res) {
1082 if ($this->debug_check_recordset($res) && is_object($res)) {
1083 $res->free();
1084 return TRUE;
1085 } else {
1086 return FALSE;
1087 }
1088 }
1089
1090 /**
1091 * Get the ID generated from the previous INSERT operation
1092 *
1093 * @return int The uid of the last inserted record.
1094 */
1095 public function sql_insert_id() {
1096 return $this->link->insert_id;
1097 }
1098
1099 /**
1100 * Returns the number of rows affected by the last INSERT, UPDATE or DELETE query
1101 *
1102 * @return int Number of rows affected by last query
1103 */
1104 public function sql_affected_rows() {
1105 return $this->link->affected_rows;
1106 }
1107
1108 /**
1109 * Move internal result pointer
1110 *
1111 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1112 * @param int $seek Seek result number.
1113 * @return bool Returns TRUE on success or FALSE on failure.
1114 */
1115 public function sql_data_seek($res, $seek) {
1116 if ($this->debug_check_recordset($res)) {
1117 return $res->data_seek($seek);
1118 } else {
1119 return FALSE;
1120 }
1121 }
1122
1123 /**
1124 * Get the type of the specified field in a result
1125 * mysql_field_type() wrapper function
1126 *
1127 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1128 * @param int $pointer Field index.
1129 * @return string Returns the name of the specified field index, or FALSE on error
1130 */
1131 public function sql_field_type($res, $pointer) {
1132 // mysql_field_type compatibility map
1133 // taken from: http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php#89117
1134 // Constant numbers see http://php.net/manual/en/mysqli.constants.php
1135 $mysql_data_type_hash = array(
1136 1=>'tinyint',
1137 2=>'smallint',
1138 3=>'int',
1139 4=>'float',
1140 5=>'double',
1141 7=>'timestamp',
1142 8=>'bigint',
1143 9=>'mediumint',
1144 10=>'date',
1145 11=>'time',
1146 12=>'datetime',
1147 13=>'year',
1148 16=>'bit',
1149 //252 is currently mapped to all text and blob types (MySQL 5.0.51a)
1150 253=>'varchar',
1151 254=>'char',
1152 246=>'decimal'
1153 );
1154 if ($this->debug_check_recordset($res)) {
1155 $metaInfo = $res->fetch_field_direct($pointer);
1156 if ($metaInfo === FALSE) {
1157 return FALSE;
1158 }
1159 return $mysql_data_type_hash[$metaInfo->type];
1160 } else {
1161 return FALSE;
1162 }
1163 }
1164
1165 /**
1166 * Open a (persistent) connection to a MySQL server
1167 *
1168 * @return bool|void
1169 * @throws \RuntimeException
1170 */
1171 public function sql_pconnect() {
1172 if ($this->isConnected) {
1173 return $this->link;
1174 }
1175
1176 if (!extension_loaded('mysqli')) {
1177 throw new \RuntimeException(
1178 'Database Error: PHP mysqli extension not loaded. This is a must have for TYPO3 CMS!',
1179 1271492607
1180 );
1181 }
1182
1183 $host = $this->persistentDatabaseConnection
1184 ? 'p:' . $this->databaseHost
1185 : $this->databaseHost;
1186
1187 $this->link = mysqli_init();
1188 $connected = $this->link->real_connect(
1189 $host,
1190 $this->databaseUsername,
1191 $this->databaseUserPassword,
1192 NULL,
1193 (int)$this->databasePort,
1194 $this->databaseSocket,
1195 $this->connectionCompression ? MYSQLI_CLIENT_COMPRESS : 0
1196 );
1197
1198 if ($connected) {
1199 $this->isConnected = TRUE;
1200
1201 if ($this->link->set_charset($this->connectionCharset) === FALSE) {
1202 GeneralUtility::sysLog(
1203 'Error setting connection charset to "' . $this->connectionCharset . '"',
1204 'Core',
1205 GeneralUtility::SYSLOG_SEVERITY_ERROR
1206 );
1207 }
1208
1209 foreach ($this->initializeCommandsAfterConnect as $command) {
1210 if ($this->query($command) === FALSE) {
1211 GeneralUtility::sysLog(
1212 'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
1213 'Core',
1214 GeneralUtility::SYSLOG_SEVERITY_ERROR
1215 );
1216 }
1217 }
1218 $this->setSqlMode();
1219 $this->checkConnectionCharset();
1220 } else {
1221 // @todo This should raise an exception. Would be useful especially to work during installation.
1222 $error_msg = $this->link->connect_error;
1223 $this->link = NULL;
1224 GeneralUtility::sysLog(
1225 'Could not connect to MySQL server ' . $host . ' with user ' . $this->databaseUsername . ': ' . $error_msg,
1226 'Core',
1227 GeneralUtility::SYSLOG_SEVERITY_FATAL
1228 );
1229 }
1230 return $this->link;
1231 }
1232
1233 /**
1234 * Fixes the SQL mode by unsetting NO_BACKSLASH_ESCAPES if found.
1235 *
1236 * @return void
1237 */
1238 protected function setSqlMode() {
1239 $resource = $this->sql_query('SELECT @@SESSION.sql_mode;');
1240 if ($resource) {
1241 $result = $this->sql_fetch_row($resource);
1242 if (isset($result[0]) && $result[0] && strpos($result[0], 'NO_BACKSLASH_ESCAPES') !== FALSE) {
1243 $modes = array_diff(GeneralUtility::trimExplode(',', $result[0]), array('NO_BACKSLASH_ESCAPES'));
1244 $query = 'SET sql_mode=\'' . $this->link->real_escape_string(implode(',', $modes)) . '\';';
1245 $this->sql_query($query);
1246 GeneralUtility::sysLog(
1247 'NO_BACKSLASH_ESCAPES could not be removed from SQL mode: ' . $this->sql_error(),
1248 'Core',
1249 GeneralUtility::SYSLOG_SEVERITY_ERROR
1250 );
1251 }
1252 }
1253 }
1254
1255 /**
1256 * Select a SQL database
1257 *
1258 * @return bool Returns TRUE on success or FALSE on failure.
1259 */
1260 public function sql_select_db() {
1261 if (!$this->isConnected) {
1262 $this->connectDB();
1263 }
1264
1265 $ret = $this->link->select_db($this->databaseName);
1266 if (!$ret) {
1267 GeneralUtility::sysLog(
1268 'Could not select MySQL database ' . $this->databaseName . ': ' . $this->sql_error(),
1269 'Core',
1270 GeneralUtility::SYSLOG_SEVERITY_FATAL
1271 );
1272 }
1273 return $ret;
1274 }
1275
1276 /**************************************
1277 *
1278 * SQL admin functions
1279 * (For use in the Install Tool and Extension Manager)
1280 *
1281 **************************************/
1282 /**
1283 * Listing databases from current MySQL connection. NOTICE: It WILL try to select those databases and thus break selection of current database.
1284 * This is only used as a service function in the (1-2-3 process) of the Install Tool.
1285 * In any case a lookup should be done in the _DEFAULT handler DBMS then.
1286 * Use in Install Tool only!
1287 *
1288 * @return array Each entry represents a database name
1289 * @throws \RuntimeException
1290 */
1291 public function admin_get_dbs() {
1292 $dbArr = array();
1293 $db_list = $this->query("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA");
1294 if ($db_list === FALSE) {
1295 throw new \RuntimeException(
1296 'MySQL Error: Cannot get tablenames: "' . $this->sql_error() . '"!',
1297 1378457171
1298 );
1299 } else {
1300 while ($row = $db_list->fetch_object()) {
1301 try {
1302 $this->setDatabaseName($row->SCHEMA_NAME);
1303 if ($this->sql_select_db()) {
1304 $dbArr[] = $row->SCHEMA_NAME;
1305 }
1306 } catch (\RuntimeException $exception) {
1307 // The exception happens if we cannot connect to the database
1308 // (usually due to missing permissions). This is ok here.
1309 // We catch the exception, skip the database and continue.
1310 }
1311 }
1312 }
1313 return $dbArr;
1314 }
1315
1316 /**
1317 * Returns the list of tables from the default database, TYPO3_db (quering the DBMS)
1318 * In a DBAL this method should 1) look up all tables from the DBMS of
1319 * the _DEFAULT handler and then 2) add all tables *configured* to be managed by other handlers
1320 *
1321 * @return array Array with tablenames as key and arrays with status information as value
1322 */
1323 public function admin_get_tables() {
1324 $whichTables = array();
1325 $tables_result = $this->query('SHOW TABLE STATUS FROM `' . $this->databaseName . '`');
1326 if ($tables_result !== FALSE) {
1327 while ($theTable = $tables_result->fetch_assoc()) {
1328 $whichTables[$theTable['Name']] = $theTable;
1329 }
1330 $tables_result->free();
1331 }
1332 return $whichTables;
1333 }
1334
1335 /**
1336 * Returns information about each field in the $table (quering the DBMS)
1337 * In a DBAL this should look up the right handler for the table and return compatible information
1338 * This function is important not only for the Install Tool but probably for
1339 * DBALs as well since they might need to look up table specific information
1340 * in order to construct correct queries. In such cases this information should
1341 * probably be cached for quick delivery.
1342 *
1343 * @param string $tableName Table name
1344 * @return array Field information in an associative array with fieldname => field row
1345 */
1346 public function admin_get_fields($tableName) {
1347 $output = array();
1348 $columns_res = $this->query('SHOW FULL COLUMNS FROM `' . $tableName . '`');
1349 if ($columns_res !== FALSE) {
1350 while ($fieldRow = $columns_res->fetch_assoc()) {
1351 $output[$fieldRow['Field']] = $fieldRow;
1352 }
1353 $columns_res->free();
1354 }
1355 return $output;
1356 }
1357
1358 /**
1359 * Returns information about each index key in the $table (quering the DBMS)
1360 * In a DBAL this should look up the right handler for the table and return compatible information
1361 *
1362 * @param string $tableName Table name
1363 * @return array Key information in a numeric array
1364 */
1365 public function admin_get_keys($tableName) {
1366 $output = array();
1367 $keyRes = $this->query('SHOW KEYS FROM `' . $tableName . '`');
1368 if ($keyRes !== FALSE) {
1369 while ($keyRow = $keyRes->fetch_assoc()) {
1370 $output[] = $keyRow;
1371 }
1372 $keyRes->free();
1373 }
1374 return $output;
1375 }
1376
1377 /**
1378 * Returns information about the character sets supported by the current DBM
1379 * This function is important not only for the Install Tool but probably for
1380 * DBALs as well since they might need to look up table specific information
1381 * in order to construct correct queries. In such cases this information should
1382 * probably be cached for quick delivery.
1383 *
1384 * This is used by the Install Tool to convert tables with non-UTF8 charsets
1385 * Use in Install Tool only!
1386 *
1387 * @return array Array with Charset as key and an array of "Charset", "Description", "Default collation", "Maxlen" as values
1388 */
1389 public function admin_get_charsets() {
1390 $output = array();
1391 $columns_res = $this->query('SHOW CHARACTER SET');
1392 if ($columns_res !== FALSE) {
1393 while ($row = $columns_res->fetch_assoc()) {
1394 $output[$row['Charset']] = $row;
1395 }
1396 $columns_res->free();
1397 }
1398 return $output;
1399 }
1400
1401 /**
1402 * mysqli() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
1403 *
1404 * @param string $query Query to execute
1405 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
1406 */
1407 public function admin_query($query) {
1408 $res = $this->query($query);
1409 if ($this->debugOutput) {
1410 $this->debug('admin_query', $query);
1411 }
1412 return $res;
1413 }
1414
1415 /******************************
1416 *
1417 * Connect handling
1418 *
1419 ******************************/
1420
1421 /**
1422 * Set database host
1423 *
1424 * @param string $host
1425 */
1426 public function setDatabaseHost($host = 'localhost') {
1427 $this->disconnectIfConnected();
1428 $this->databaseHost = $host;
1429 }
1430
1431 /**
1432 * Set database port
1433 *
1434 * @param int $port
1435 */
1436 public function setDatabasePort($port = 3306) {
1437 $this->disconnectIfConnected();
1438 $this->databasePort = (int)$port;
1439 }
1440
1441 /**
1442 * Set database socket
1443 *
1444 * @param string|NULL $socket
1445 */
1446 public function setDatabaseSocket($socket = NULL) {
1447 $this->disconnectIfConnected();
1448 $this->databaseSocket = $socket;
1449 }
1450
1451 /**
1452 * Set database name
1453 *
1454 * @param string $name
1455 */
1456 public function setDatabaseName($name) {
1457 $this->disconnectIfConnected();
1458 $this->databaseName = $name;
1459 }
1460
1461 /**
1462 * Set database username
1463 *
1464 * @param string $username
1465 */
1466 public function setDatabaseUsername($username) {
1467 $this->disconnectIfConnected();
1468 $this->databaseUsername = $username;
1469 }
1470
1471 /**
1472 * Set database password
1473 *
1474 * @param string $password
1475 */
1476 public function setDatabasePassword($password) {
1477 $this->disconnectIfConnected();
1478 $this->databaseUserPassword = $password;
1479 }
1480
1481 /**
1482 * Set persistent database connection
1483 *
1484 * @param bool $persistentDatabaseConnection
1485 * @see http://php.net/manual/de/mysqli.persistconns.php
1486 */
1487 public function setPersistentDatabaseConnection($persistentDatabaseConnection) {
1488 $this->disconnectIfConnected();
1489 $this->persistentDatabaseConnection = (bool)$persistentDatabaseConnection;
1490 }
1491
1492 /**
1493 * Set connection compression. Might be an advantage, if SQL server is not on localhost
1494 *
1495 * @param bool $connectionCompression TRUE if connection should be compressed
1496 */
1497 public function setConnectionCompression($connectionCompression) {
1498 $this->disconnectIfConnected();
1499 $this->connectionCompression = (bool)$connectionCompression;
1500 }
1501
1502 /**
1503 * Set commands to be fired after connection was established
1504 *
1505 * @param array $commands List of SQL commands to be executed after connect
1506 */
1507 public function setInitializeCommandsAfterConnect(array $commands) {
1508 $this->disconnectIfConnected();
1509 $this->initializeCommandsAfterConnect = $commands;
1510 }
1511
1512 /**
1513 * Set the charset that should be used for the MySQL connection.
1514 * The given value will be passed on to mysqli_set_charset().
1515 *
1516 * The default value of this setting is utf8.
1517 *
1518 * @param string $connectionCharset The connection charset that will be passed on to mysqli_set_charset() when connecting the database. Default is utf8.
1519 * @return void
1520 */
1521 public function setConnectionCharset($connectionCharset = 'utf8') {
1522 $this->disconnectIfConnected();
1523 $this->connectionCharset = $connectionCharset;
1524 }
1525
1526 /**
1527 * Connects to database for TYPO3 sites:
1528 *
1529 * @throws \RuntimeException
1530 * @throws \UnexpectedValueException
1531 * @internal param string $user Username to connect with.
1532 * @return void
1533 */
1534 public function connectDB() {
1535 // Early return if connected already
1536 if ($this->isConnected) {
1537 return;
1538 }
1539
1540 if (!$this->databaseName) {
1541 throw new \RuntimeException(
1542 'TYPO3 Fatal Error: No database selected!',
1543 1270853882
1544 );
1545 }
1546
1547 if ($this->sql_pconnect()) {
1548 if (!$this->sql_select_db()) {
1549 throw new \RuntimeException(
1550 'TYPO3 Fatal Error: Cannot connect to the current database, "' . $this->databaseName . '"!',
1551 1270853883
1552 );
1553 }
1554 } else {
1555 throw new \RuntimeException(
1556 'TYPO3 Fatal Error: The current username, password or host was not accepted when the connection to the database was attempted to be established!',
1557 1270853884
1558 );
1559 }
1560
1561 // Prepare user defined objects (if any) for hooks which extend query methods
1562 $this->preProcessHookObjects = array();
1563 $this->postProcessHookObjects = array();
1564 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'])) {
1565 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'] as $classRef) {
1566 $hookObject = GeneralUtility::getUserObj($classRef);
1567 if (!(
1568 $hookObject instanceof PreProcessQueryHookInterface
1569 || $hookObject instanceof PostProcessQueryHookInterface
1570 )) {
1571 throw new \UnexpectedValueException(
1572 '$hookObject must either implement interface TYPO3\\CMS\\Core\\Database\\PreProcessQueryHookInterface or interface TYPO3\\CMS\\Core\\Database\\PostProcessQueryHookInterface',
1573 1299158548
1574 );
1575 }
1576 if ($hookObject instanceof PreProcessQueryHookInterface) {
1577 $this->preProcessHookObjects[] = $hookObject;
1578 }
1579 if ($hookObject instanceof PostProcessQueryHookInterface) {
1580 $this->postProcessHookObjects[] = $hookObject;
1581 }
1582 }
1583 }
1584 }
1585
1586 /**
1587 * Checks if database is connected
1588 *
1589 * @return bool
1590 */
1591 public function isConnected() {
1592 // We think we're still connected
1593 if ($this->isConnected) {
1594 // Check if this is really the case or if the database server has gone away for some reason
1595 $this->isConnected = $this->link->ping();
1596 }
1597 return $this->isConnected;
1598 }
1599
1600 /**
1601 * Checks if the current connection character set has the same value
1602 * as the connectionCharset variable.
1603 *
1604 * To determine the character set these MySQL session variables are
1605 * checked: character_set_client, character_set_results and
1606 * character_set_connection.
1607 *
1608 * If the character set does not match or if the session variables
1609 * can not be read a RuntimeException is thrown.
1610 *
1611 * @return void
1612 * @throws \RuntimeException
1613 */
1614 protected function checkConnectionCharset() {
1615 $sessionResult = $this->sql_query('SHOW SESSION VARIABLES LIKE \'character_set%\'');
1616
1617 if ($sessionResult === FALSE) {
1618 GeneralUtility::sysLog(
1619 'Error while retrieving the current charset session variables from the database: ' . $this->sql_error(),
1620 'Core',
1621 GeneralUtility::SYSLOG_SEVERITY_ERROR
1622 );
1623 throw new \RuntimeException(
1624 'TYPO3 Fatal Error: Could not determine the current charset of the database.',
1625 1381847136
1626 );
1627 }
1628
1629 $charsetVariables = array();
1630 while (($row = $this->sql_fetch_row($sessionResult)) !== FALSE) {
1631 $variableName = $row[0];
1632 $variableValue = $row[1];
1633 $charsetVariables[$variableName] = $variableValue;
1634 }
1635 $this->sql_free_result($sessionResult);
1636
1637 // These variables are set with the "Set names" command which was
1638 // used in the past. This is why we check them.
1639 $charsetRequiredVariables = array(
1640 'character_set_client',
1641 'character_set_results',
1642 'character_set_connection',
1643 );
1644
1645 $hasValidCharset = TRUE;
1646 foreach ($charsetRequiredVariables as $variableName) {
1647 if (empty($charsetVariables[$variableName])) {
1648 GeneralUtility::sysLog(
1649 'A required session variable is missing in the current MySQL connection: ' . $variableName,
1650 'Core',
1651 GeneralUtility::SYSLOG_SEVERITY_ERROR
1652 );
1653 throw new \RuntimeException(
1654 'TYPO3 Fatal Error: Could not determine the value of the database session variable: ' . $variableName,
1655 1381847779
1656 );
1657 }
1658
1659 if ($charsetVariables[$variableName] !== $this->connectionCharset) {
1660 $hasValidCharset = FALSE;
1661 break;
1662 }
1663 }
1664
1665 if (!$hasValidCharset) {
1666 throw new \RuntimeException(
1667 'It looks like the character set ' . $this->connectionCharset . ' is not used for this connection even though it is configured as connection charset. ' .
1668 'This TYPO3 installation is using the $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'setDBinit\'] property with the following value: "' .
1669 $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] . '". Please make sure that this command does not overwrite the configured charset. ' .
1670 'Please note that for the TYPO3 database everything other than utf8 is unsupported since version 4.7.',
1671 1389697515
1672 );
1673 }
1674 }
1675
1676 /**
1677 * Disconnect from database if connected
1678 *
1679 * @return void
1680 */
1681 protected function disconnectIfConnected() {
1682 if ($this->isConnected) {
1683 $this->link->close();
1684 $this->isConnected = FALSE;
1685 }
1686 }
1687
1688 /**
1689 * Returns current database handle
1690 *
1691 * @return \mysqli|NULL
1692 */
1693 public function getDatabaseHandle() {
1694 return $this->link;
1695 }
1696
1697 /**
1698 * Set current database handle, usually \mysqli
1699 *
1700 * @param \mysqli $handle
1701 */
1702 public function setDatabaseHandle($handle) {
1703 $this->link = $handle;
1704 }
1705
1706 /******************************
1707 *
1708 * Debugging
1709 *
1710 ******************************/
1711 /**
1712 * Debug function: Outputs error if any
1713 *
1714 * @param string $func Function calling debug()
1715 * @param string $query Last query if not last built query
1716 * @return void
1717 */
1718 public function debug($func, $query = '') {
1719 $error = $this->sql_error();
1720 if ($error || (int)$this->debugOutput === 2) {
1721 \TYPO3\CMS\Core\Utility\DebugUtility::debug(
1722 array(
1723 'caller' => \TYPO3\CMS\Core\Database\DatabaseConnection::class . '::' . $func,
1724 'ERROR' => $error,
1725 'lastBuiltQuery' => $query ? $query : $this->debug_lastBuiltQuery,
1726 'debug_backtrace' => \TYPO3\CMS\Core\Utility\DebugUtility::debugTrail()
1727 ),
1728 $func,
1729 is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'], 'debug'))
1730 ? ''
1731 : 'DB Error'
1732 );
1733 }
1734 }
1735
1736 /**
1737 * Checks if record set is valid and writes debugging information into devLog if not.
1738 *
1739 * @param bool|\mysqli_result|object MySQLi result object / DBAL object
1740 * @return bool TRUE if the record set is valid, FALSE otherwise
1741 */
1742 public function debug_check_recordset($res) {
1743 if ($res !== FALSE) {
1744 return TRUE;
1745 }
1746 $msg = 'Invalid database result detected';
1747 $trace = debug_backtrace();
1748 array_shift($trace);
1749 $cnt = count($trace);
1750 for ($i = 0; $i < $cnt; $i++) {
1751 // Complete objects are too large for the log
1752 if (isset($trace['object'])) {
1753 unset($trace['object']);
1754 }
1755 }
1756 $msg .= ': function TYPO3\\CMS\\Core\\Database\\DatabaseConnection->' . $trace[0]['function'] . ' called from file ' . substr($trace[0]['file'], (strlen(PATH_site) + 2)) . ' in line ' . $trace[0]['line'];
1757 GeneralUtility::sysLog(
1758 $msg . '. Use a devLog extension to get more details.',
1759 'Core/t3lib_db',
1760 GeneralUtility::SYSLOG_SEVERITY_ERROR
1761 );
1762 // Send to devLog if enabled
1763 if (TYPO3_DLOG) {
1764 $debugLogData = array(
1765 'SQL Error' => $this->sql_error(),
1766 'Backtrace' => $trace
1767 );
1768 if ($this->debug_lastBuiltQuery) {
1769 $debugLogData = array('SQL Query' => $this->debug_lastBuiltQuery) + $debugLogData;
1770 }
1771 GeneralUtility::devLog($msg . '.', 'Core/t3lib_db', 3, $debugLogData);
1772 }
1773 return FALSE;
1774 }
1775
1776 /**
1777 * Explain select queries
1778 * If $this->explainOutput is set, SELECT queries will be explained here. Only queries with more than one possible result row will be displayed.
1779 * The output is either printed as raw HTML output or embedded into the TS admin panel (checkbox must be enabled!)
1780 *
1781 * @todo Feature is not DBAL-compliant
1782 *
1783 * @param string $query SQL query
1784 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
1785 * @param int $row_count Number of resulting rows
1786 * @return bool TRUE if explain was run, FALSE otherwise
1787 */
1788 protected function explain($query, $from_table, $row_count) {
1789 $debugAllowedForIp = GeneralUtility::cmpIP(
1790 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
1791 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
1792 );
1793 if (
1794 (int)$this->explainOutput == 1
1795 || ((int)$this->explainOutput == 2 && $debugAllowedForIp)
1796 ) {
1797 // Raw HTML output
1798 $explainMode = 1;
1799 } elseif ((int)$this->explainOutput == 3 && is_object($GLOBALS['TT'])) {
1800 // Embed the output into the TS admin panel
1801 $explainMode = 2;
1802 } else {
1803 return FALSE;
1804 }
1805 $error = $this->sql_error();
1806 $trail = \TYPO3\CMS\Core\Utility\DebugUtility::debugTrail();
1807 $explain_tables = array();
1808 $explain_output = array();
1809 $res = $this->sql_query('EXPLAIN ' . $query, $this->link);
1810 if (is_a($res, '\\mysqli_result')) {
1811 while ($tempRow = $this->sql_fetch_assoc($res)) {
1812 $explain_output[] = $tempRow;
1813 $explain_tables[] = $tempRow['table'];
1814 }
1815 $this->sql_free_result($res);
1816 }
1817 $indices_output = array();
1818 // Notice: Rows are skipped if there is only one result, or if no conditions are set
1819 if (
1820 $explain_output[0]['rows'] > 1
1821 || GeneralUtility::inList('ALL', $explain_output[0]['type'])
1822 ) {
1823 // Only enable output if it's really useful
1824 $debug = TRUE;
1825 foreach ($explain_tables as $table) {
1826 $tableRes = $this->sql_query('SHOW TABLE STATUS LIKE \'' . $table . '\'');
1827 $isTable = $this->sql_num_rows($tableRes);
1828 if ($isTable) {
1829 $res = $this->sql_query('SHOW INDEX FROM ' . $table, $this->link);
1830 if (is_a($res, '\\mysqli_result')) {
1831 while ($tempRow = $this->sql_fetch_assoc($res)) {
1832 $indices_output[] = $tempRow;
1833 }
1834 $this->sql_free_result($res);
1835 }
1836 }
1837 $this->sql_free_result($tableRes);
1838 }
1839 } else {
1840 $debug = FALSE;
1841 }
1842 if ($debug) {
1843 if ($explainMode) {
1844 $data = array();
1845 $data['query'] = $query;
1846 $data['trail'] = $trail;
1847 $data['row_count'] = $row_count;
1848 if ($error) {
1849 $data['error'] = $error;
1850 }
1851 if (count($explain_output)) {
1852 $data['explain'] = $explain_output;
1853 }
1854 if (count($indices_output)) {
1855 $data['indices'] = $indices_output;
1856 }
1857 if ($explainMode == 1) {
1858 \TYPO3\CMS\Core\Utility\DebugUtility::debug($data, 'Tables: ' . $from_table, 'DB SQL EXPLAIN');
1859 } elseif ($explainMode == 2) {
1860 $GLOBALS['TT']->setTSselectQuery($data);
1861 }
1862 }
1863 return TRUE;
1864 }
1865 return FALSE;
1866 }
1867
1868 /**
1869 * Serialize destructs current connection
1870 *
1871 * @return array All protected properties that should be saved
1872 */
1873 public function __sleep() {
1874 $this->disconnectIfConnected();
1875 return array(
1876 'debugOutput',
1877 'explainOutput',
1878 'databaseHost',
1879 'databasePort',
1880 'databaseSocket',
1881 'databaseName',
1882 'databaseUsername',
1883 'databaseUserPassword',
1884 'persistentDatabaseConnection',
1885 'connectionCompression',
1886 'initializeCommandsAfterConnect',
1887 'default_charset',
1888 );
1889 }
1890
1891 }