X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fdba.php;h=061f5399c76d11b38bdb72c1a9a7ec60b2b11e40;hb=1ffe0cfd818ad48f7c227915d3abf68d26d8a06c;hp=478a1a10c2d40e673298121290f26676e3878321;hpb=759dfe6bb1b03a80ebabe92d89eb55ab3081abad;p=friendica.git diff --git a/include/dba.php b/include/dba.php index 478a1a10c2..061f5399c7 100644 --- a/include/dba.php +++ b/include/dba.php @@ -76,6 +76,7 @@ class dba { } try { self::$db = @new PDO($connect, $user, $pass); + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); self::$connected = true; } catch (PDOException $e) { } @@ -171,7 +172,7 @@ class dba { */ public static function database_name() { $ret = self::p("SELECT DATABASE() AS `db`"); - $data = self::inArray($ret); + $data = self::inArray($ret); return $data[0]['db']; } @@ -349,7 +350,7 @@ class dba { * For all regular queries please use dba::select or dba::exists * * @param string $sql SQL statement - * @return bool|object statement object + * @return bool|object statement object or result object */ public static function p($sql) { $a = get_app(); @@ -840,10 +841,25 @@ class dba { */ public static function lock($table) { // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html - self::e("SET autocommit=0"); + if (self::$driver == 'pdo') { + self::e("SET autocommit=0"); + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + } else { + self::$db->autocommit(false); + } + $success = self::e("LOCK TABLES `".self::escape($table)."` WRITE"); + + if (self::$driver == 'pdo') { + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + } + if (!$success) { - self::e("SET autocommit=1"); + if (self::$driver == 'pdo') { + self::e("SET autocommit=1"); + } else { + self::$db->autocommit(true); + } } else { self::$in_transaction = true; } @@ -857,9 +873,21 @@ class dba { */ public static function unlock() { // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html - self::e("COMMIT"); + self::performCommit(); + + if (self::$driver == 'pdo') { + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + } + $success = self::e("UNLOCK TABLES"); - self::e("SET autocommit=1"); + + if (self::$driver == 'pdo') { + self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + self::e("SET autocommit=1"); + } else { + self::$db->autocommit(true); + } + self::$in_transaction = false; return $success; } @@ -870,23 +898,51 @@ class dba { * @return boolean Was the command executed successfully? */ public static function transaction() { - if (!self::e('COMMIT')) { + if (!self::performCommit()) { return false; } - if (!self::e('START TRANSACTION')) { - return false; + + switch (self::$driver) { + case 'pdo': + if (self::$db->inTransaction()) { + break; + } + if (!self::$db->beginTransaction()) { + return false; + } + break; + case 'mysqli': + if (!self::$db->begin_transaction()) { + return false; + } + break; } + self::$in_transaction = true; return true; } + private static function performCommit() + { + switch (self::$driver) { + case 'pdo': + if (!self::$db->inTransaction()) { + return true; + } + return self::$db->commit(); + case 'mysqli': + return self::$db->commit(); + } + return true; + } + /** * @brief Does a commit * * @return boolean Was the command executed successfully? */ public static function commit() { - if (!self::e('COMMIT')) { + if (!self::performCommit()) { return false; } self::$in_transaction = false; @@ -899,11 +955,20 @@ class dba { * @return boolean Was the command executed successfully? */ public static function rollback() { - if (!self::e('ROLLBACK')) { - return false; + switch (self::$driver) { + case 'pdo': + if (!self::$db->inTransaction()) { + $ret = true; + break; + } + $ret = self::$db->rollBack(); + break; + case 'mysqli': + $ret = self::$db->rollback(); + break; } self::$in_transaction = false; - return true; + return $ret; } /** @@ -950,7 +1015,7 @@ class dba { $commands = []; // Create a key for the loop prevention - $key = $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions); + $key = $table . ':' . json_encode($conditions); // We quit when this key already exists in the callstack. if (isset($callstack[$key])) { @@ -977,7 +1042,7 @@ class dba { $rel_def = array_values(self::$relation[$table])[0]; // Create a key for preventing double queries - $qkey = $field . '-' . $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions); + $qkey = $field . '-' . $table . ':' . json_encode($conditions); // When the search field is the relation field, we don't need to fetch the rows // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend @@ -1044,7 +1109,7 @@ class dba { // Split the SQL queries in chunks of 100 values // We do the $i stuff here to make the code better readable - $i = $counter[$key_table][$key_condition]; + $i = isset($counter[$key_table][$key_condition]) ? $counter[$key_table][$key_condition] : 0; if (isset($compacted[$key_table][$key_condition][$i]) && count($compacted[$key_table][$key_condition][$i]) > 100) { ++$i; } @@ -1296,6 +1361,30 @@ class dba { $condition_string .= " AND "; } if (is_array($value)) { + /* Workaround for MySQL Bug #64791. + * Never mix data types inside any IN() condition. + * In case of mixed types, cast all as string. + * Logic needs to be consistent with dba::p() data types. + */ + $is_int = false; + $is_alpha = false; + foreach ($value as $single_value) { + if (is_int($single_value)) { + $is_int = true; + } else { + $is_alpha = true; + } + } + + if ($is_int && $is_alpha) { + foreach ($value as &$ref) { + if (is_int($ref)) { + $ref = (string)$ref; + } + } + unset($ref); //Prevent accidental re-use. + } + $new_values = array_merge($new_values, array_values($value)); $placeholders = substr(str_repeat("?, ", count($value)), 0, -2); $condition_string .= "`" . $field . "` IN (" . $placeholders . ")"; @@ -1404,8 +1493,18 @@ class dba { $ret = $stmt->closeCursor(); break; case 'mysqli': - $stmt->free_result(); - $ret = $stmt->close(); + // MySQLi offers both a mysqli_stmt and a mysqli_result class. + // We should be careful not to assume the object type of $stmt + // because dba::p() has been able to return both types. + if ($stmt instanceof mysqli_stmt) { + $stmt->free_result(); + $ret = $stmt->close(); + } elseif ($stmt instanceof mysqli_result) { + $stmt->free(); + $ret = true; + } else { + $ret = false; + } break; }