- <?php
- * @file
- * Database interface code for MySQL database servers.
- */
-
- * @addtogroup database
- * @{
- */
-
- class DatabaseConnection_mysql extends DatabaseConnection {
-
- * Flag to indicate if the cleanup function in __destruct() should run.
- *
- * @var boolean
- */
- protected $needsCleanup = FALSE;
-
-
- * Flag set in utf8mb4IsSupported().
- *
- * @var boolean
- */
- protected $utf8mb4Supported;
-
- public function __construct(array $connection_options = array()) {
-
- $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE);
-
-
- $this->transactionalDDLSupport = FALSE;
-
- $this->connectionOptions = $connection_options;
-
- $charset = 'utf8';
-
- if ($this->utf8mb4IsActive()) {
- $charset = 'utf8mb4';
- }
-
-
- if (isset($connection_options['unix_socket'])) {
- $dsn = 'mysql:unix_socket=' . $connection_options['unix_socket'];
- }
- else {
-
- $dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
- }
-
-
-
- $dsn .= ';charset=' . $charset;
- if (!empty($connection_options['database'])) {
- $dsn .= ';dbname=' . $connection_options['database'];
- }
-
- $connection_options += array(
- 'pdo' => array(),
- );
- $connection_options['pdo'] += array(
-
-
-
- PDO::MYSQL_ATTR_FOUND_ROWS => TRUE,
-
- PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
-
- PDO::ATTR_EMULATE_PREPARES => TRUE,
-
-
-
-
- PDO::ATTR_STRINGIFY_FETCHES => TRUE,
-
- PDO::MYSQL_ATTR_MULTI_STATEMENTS => FALSE,
- );
-
- parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
-
-
-
-
- if (!empty($connection_options['collation'])) {
- $this->pdo->exec('SET NAMES ' . $charset . ' COLLATE ' . $connection_options['collation']);
- }
- else {
- $this->pdo->exec('SET NAMES ' . $charset);
- }
-
-
-
-
-
-
- $connection_options += array(
- 'init_commands' => array(),
- );
- $connection_options['init_commands'] += array(
- 'sql_mode' => "SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO'",
- );
-
- foreach ($connection_options['init_commands'] as $sql) {
- $this->pdo->exec($sql);
- }
- }
-
-
- * {@inheritdoc}
- */
- protected function setPrefix($prefix) {
- if (is_array($prefix)) {
- $this->prefixes = $prefix + array('default' => '');
- }
- else {
- $this->prefixes = array('default' => $prefix);
- }
-
-
-
- $this->prefixSearch = array();
- $this->prefixReplace = array();
- foreach ($this->prefixes as $key => $val) {
- if ($key != 'default') {
- $this->prefixSearch[] = '{' . $key . '}';
-
-
-
- $this->prefixReplace[] = '`' . str_replace('.', '`.`', $val) . $key . '`';
- }
- }
-
- $this->prefixSearch[] = '{';
- $this->prefixReplace[] = '`' . $this->prefixes['default'];
- $this->prefixSearch[] = '}';
- $this->prefixReplace[] = '`';
- }
-
-
- * {@inheritdoc}
- */
- public function escapeField($field) {
- $field = parent::escapeField($field);
- return $this->quoteIdentifier($field);
- }
-
-
- * {@inheritdoc}
- */
- public function escapeAlias($field) {
- $field = parent::escapeAlias($field);
- return $this->quoteIdentifier($field);
- }
-
-
- * Quotes an identifier with backticks for MySQL 8 compatibility.
- *
- * Not all identifiers need quotes, only keywords, but we add them on all
- * fields and table names for consistency and to ease compatibility in the
- * future.
- *
- * @param string $identifier
- * The field to check.
- *
- * @return string
- * The identifier, quoted with backticks.
- */
- private function quoteIdentifier($identifier) {
-
- if (strlen($identifier) === 0) {
- return $identifier;
- }
-
- elseif (strpos($identifier, '.') !== FALSE) {
- list($table, $identifier) = explode('.', $identifier, 2);
- return "`$table`" . '.' . "`$identifier`";
- }
-
- return "`$identifier`";
- }
-
- public function __destruct() {
- if ($this->needsCleanup) {
- $this->nextIdDelete();
- }
- }
-
- public function queryRange($query, $from, $count, array $args = array(), array $options = array()) {
- return $this->query($query . ' LIMIT ' . (int) $from . ', ' . (int) $count, $args, $options);
- }
-
- public function queryTemporary($query, array $args = array(), array $options = array()) {
- $tablename = $this->generateTemporaryTableName();
- $this->query('CREATE TEMPORARY TABLE {' . $tablename . '} Engine=MEMORY ' . $query, $args, $options);
- return $tablename;
- }
-
- public function driver() {
- return 'mysql';
- }
-
-
- * Returns the version of the database server.
- */
- public function version() {
- $version = parent::version();
-
-
-
-
-
-
-
-
-
- $regex = '/^(?:5\.5\.5-)?(\d+\.\d+\.\d+.*-mariadb.*)/i';
- preg_match($regex, $version, $matches);
- if (!empty($matches[1])) {
- $version = $matches[1];
- }
-
- return $version;
- }
-
- public function databaseType() {
- return 'mysql';
- }
-
-
- * Overrides DatabaseConnection::createDatabase().
- *
- * @param string $database
- * The name of the database to create.
- *
- * @throws DatabaseNotFoundException
- */
- public function createDatabase($database) {
- try {
-
- $this->pdo->exec("CREATE DATABASE IF NOT EXISTS $database");
- $this->pdo->exec("USE $database");
- }
- catch (\Exception $e) {
- throw new DatabaseNotFoundException($e->getMessage());
- }
- }
-
- public function mapConditionOperator($operator) {
-
- return NULL;
- }
-
- public function nextId($existing_id = 0) {
- $new_id = $this->query('INSERT INTO {sequences} () VALUES ()', array(), array('return' => Database::RETURN_INSERT_ID));
-
- if ($existing_id >= $new_id) {
-
-
-
-
-
-
-
- $this->query('INSERT INTO {sequences} (value) VALUES (:value) ON DUPLICATE KEY UPDATE value = value', array(':value' => $existing_id));
- $new_id = $this->query('INSERT INTO {sequences} () VALUES ()', array(), array('return' => Database::RETURN_INSERT_ID));
- }
- $this->needsCleanup = TRUE;
- return $new_id;
- }
-
- public function nextIdDelete() {
-
-
-
-
-
-
-
-
- try {
- $max_id = $this->query('SELECT MAX(value) FROM {sequences}')->fetchField();
-
- $this->query('DELETE FROM {sequences} WHERE value < :value', array(':value' => $max_id));
- }
-
-
-
-
-
-
- catch (PDOException $e) {
- }
- }
-
-
- * Overridden to work around issues to MySQL not supporting transactional DDL.
- */
- protected function popCommittableTransactions() {
-
- foreach (array_reverse($this->transactionLayers) as $name => $active) {
-
- if ($active) {
- break;
- }
-
-
- unset($this->transactionLayers[$name]);
- if (empty($this->transactionLayers)) {
- if (!$this->doCommit()) {
- throw new DatabaseTransactionCommitFailedException();
- }
- }
- else {
-
- try {
- $this->query('RELEASE SAVEPOINT ' . $name);
- }
- catch (PDOException $e) {
-
-
-
-
-
-
-
- if ($e->errorInfo[1] == '1305') {
-
-
- $this->transactionLayers = array();
-
-
- $this->doCommit();
- }
- else {
- throw $e;
- }
- }
- }
- }
- }
-
-
- * Do the actual commit, including a workaround for PHP 8 behaviour changes.
- *
- * @return bool
- * Success or otherwise of the commit.
- */
- protected function doCommit() {
- if ($this->pdo->inTransaction()) {
- return $this->pdo->commit();
- }
- else {
-
-
- return TRUE;
- }
- }
-
-
- * {@inheritdoc}
- */
- public function rollback($savepoint_name = 'backdrop_transaction') {
-
-
-
-
- if (!$this->pdo->inTransaction()) {
-
-
-
-
-
-
- if (!$this->inTransaction()) {
- throw new DatabaseTransactionNoActiveException();
- }
-
-
- if (!isset($this->transactionLayers[$savepoint_name])) {
- throw new DatabaseTransactionNoActiveException();
- }
-
- trigger_error('Rollback attempted when there is no active transaction. This can cause data integrity issues.', E_USER_WARNING);
- return;
- }
- return parent::rollback($savepoint_name);
- }
-
- public function utf8mb4IsActive() {
- return isset($this->connectionOptions['charset']) && $this->connectionOptions['charset'] === 'utf8mb4';
- }
-
- public function utf8mb4IsSupported() {
-
- if (isset($this->utf8mb4Supported)) {
- return $this->utf8mb4Supported;
- }
-
-
- try {
- $this->query("DROP TABLE IF EXISTS {backdrop_utf8mb4_test}");
- $this->query("CREATE TABLE {backdrop_utf8mb4_test} (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB");
- }
- catch (Exception $e) {
- $this->utf8mb4Supported = FALSE;
- return FALSE;
- }
- $this->query("DROP TABLE IF EXISTS {backdrop_utf8mb4_test}");
- $this->utf8mb4Supported = TRUE;
- return TRUE;
- }
- }
-
-
- * @} End of "addtogroup database".
- */