]> git.mxchange.org Git - friendica.git/blobdiff - src/Database/Database.php
Add feedback
[friendica.git] / src / Database / Database.php
index ae06f00e26cd5562e425008b773a833eb777855e..28283b1c8175d8273d980c0ffe17b021b8074cdc 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @copyright Copyright (C) 2020, Friendica
+ * @copyright Copyright (C) 2010-2021, the Friendica project
  *
  * @license GNU AGPL version 3 or any later version
  *
@@ -468,6 +468,7 @@ class Database
        public function p($sql)
        {
 
+               $this->profiler->startRecording('database');
                $stamp1 = microtime(true);
 
                $params = DBA::getParam(func_get_args());
@@ -695,7 +696,7 @@ class Database
                        $this->errorno = $errorno;
                }
 
-               $this->profiler->saveTimestamp($stamp1, 'database');
+               $this->profiler->stopRecording();
 
                if ($this->configCache->get('system', 'db_log')) {
                        $stamp2   = microtime(true);
@@ -727,7 +728,7 @@ class Database
        public function e($sql)
        {
 
-               $stamp = microtime(true);
+               $this->profiler->startRecording('database_write');
 
                $params = DBA::getParam(func_get_args());
 
@@ -779,7 +780,7 @@ class Database
                        $this->errorno = $errorno;
                }
 
-               $this->profiler->saveTimestamp($stamp, "database_write");
+               $this->profiler->stopRecording();
 
                return $retval;
        }
@@ -914,7 +915,7 @@ class Database
         */
        public function fetch($stmt)
        {
-               $stamp1 = microtime(true);
+               $this->profiler->startRecording('database');
 
                $columns = [];
 
@@ -962,7 +963,7 @@ class Database
                                }
                }
 
-               $this->profiler->saveTimestamp($stamp1, 'database');
+               $this->profiler->stopRecording();
 
                return $columns;
        }
@@ -1217,175 +1218,29 @@ class Database
                return $ret;
        }
 
-       /**
-        * Build the array with the table relations
-        *
-        * The array is build from the database definitions in DBStructure.php
-        *
-        * This process must only be started once, since the value is cached.
-        */
-       private function buildRelationData()
-       {
-               $definition = DBStructure::definition($this->configCache->get('system', 'basepath'));
-
-               foreach ($definition AS $table => $structure) {
-                       foreach ($structure['fields'] AS $field => $field_struct) {
-                               if (isset($field_struct['relation'])) {
-                                       foreach ($field_struct['relation'] AS $rel_table => $rel_field) {
-                                               $this->relation[$rel_table][$rel_field][$table][] = $field;
-                                       }
-                               }
-                       }
-               }
-       }
-
        /**
         * Delete a row from a table
         *
-        * Note: this methods does NOT accept schema => table arrays because of the complex relation stuff.
-        *
         * @param string $table      Table name
         * @param array  $conditions Field condition(s)
-        * @param array  $options
-        *                           - cascade: If true we delete records in other tables that depend on the one we're deleting through
-        *                           relations (default: true)
-        * @param array  $callstack  Internal use: prevent endless loops
         *
         * @return boolean was the delete successful?
         * @throws \Exception
         */
-       public function delete($table, array $conditions, array $options = [], array &$callstack = [])
+       public function delete($table, array $conditions)
        {
                if (empty($table) || empty($conditions)) {
                        $this->logger->info('Table and conditions have to be set');
                        return false;
                }
 
-               $commands = [];
-
-               // Create a key for the loop prevention
-               $key = $table . ':' . json_encode($conditions);
-
-               // We quit when this key already exists in the callstack.
-               if (isset($callstack[$key])) {
-                       return true;
-               }
-
-               $callstack[$key] = true;
-
-               $commands[$key] = ['table' => $table, 'conditions' => $conditions];
-
-               // Don't use "defaults" here, since it would set "false" to "true"
-               if (isset($options['cascade'])) {
-                       $cascade = $options['cascade'];
-               } else {
-                       $cascade = true;
-               }
-
-               // To speed up the whole process we cache the table relations
-               if ($cascade && count($this->relation) == 0) {
-                       $this->buildRelationData();
-               }
-
-               // Is there a relation entry for the table?
-               if ($cascade && isset($this->relation[$table])) {
-                       // We only allow a simple "one field" relation.
-                       $field   = array_keys($this->relation[$table])[0];
-                       $rel_def = array_values($this->relation[$table])[0];
-
-                       // Create a key for preventing double queries
-                       $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
-                       if ((count($conditions) == 1) && ($field == array_keys($conditions)[0])) {
-                               foreach ($rel_def AS $rel_table => $rel_fields) {
-                                       foreach ($rel_fields AS $rel_field) {
-                                               $this->delete($rel_table, [$rel_field => array_values($conditions)[0]], $options, $callstack);
-                                       }
-                               }
-                               // We quit when this key already exists in the callstack.
-                       } elseif (!isset($callstack[$qkey])) {
-                               $callstack[$qkey] = true;
-
-                               // Fetch all rows that are to be deleted
-                               $data = $this->select($table, [$field], $conditions);
-
-                               while ($row = $this->fetch($data)) {
-                                       $this->delete($table, [$field => $row[$field]], $options, $callstack);
-                               }
-
-                               $this->close($data);
-
-                               // Since we had split the delete command we don't need the original command anymore
-                               unset($commands[$key]);
-                       }
-               }
-
-               // Now we finalize the process
-               $do_transaction = !$this->in_transaction;
-
-               if ($do_transaction) {
-                       $this->transaction();
-               }
-
-               $compacted = [];
-               $counter   = [];
-
-               foreach ($commands AS $command) {
-                       $conditions = $command['conditions'];
-                       reset($conditions);
-                       $first_key = key($conditions);
-
-                       $condition_string = DBA::buildCondition($conditions);
-
-                       if ((count($command['conditions']) > 1) || is_int($first_key)) {
-                               $sql = "DELETE FROM " . DBA::quoteIdentifier($command['table']) . " " . $condition_string;
-                               $this->logger->info($this->replaceParameters($sql, $conditions), ['callstack' => System::callstack(6), 'internal_callstack' => $callstack]);
-
-                               if (!$this->e($sql, $conditions)) {
-                                       if ($do_transaction) {
-                                               $this->rollback();
-                                       }
-                                       return false;
-                               }
-                       } else {
-                               $key_table     = $command['table'];
-                               $key_condition = array_keys($command['conditions'])[0];
-                               $value         = array_values($command['conditions'])[0];
-
-                               // Split the SQL queries in chunks of 100 values
-                               // We do the $i stuff here to make the code better readable
-                               $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;
-                               }
-
-                               $compacted[$key_table][$key_condition][$i][$value] = $value;
-                               $counter[$key_table][$key_condition]               = $i;
-                       }
-               }
-               foreach ($compacted AS $table => $values) {
-                       foreach ($values AS $field => $field_value_list) {
-                               foreach ($field_value_list AS $field_values) {
-                                       $sql = "DELETE FROM " . DBA::quoteIdentifier($table) . " WHERE " . DBA::quoteIdentifier($field) . " IN (" .
-                                              substr(str_repeat("?, ", count($field_values)), 0, -2) . ");";
+               $table_string = DBA::buildTableString($table);
 
-                                       $this->logger->info($this->replaceParameters($sql, $field_values), ['callstack' => System::callstack(6), 'internal_callstack' => $callstack]);
+               $condition_string = DBA::buildCondition($conditions);
 
-                                       if (!$this->e($sql, $field_values)) {
-                                               if ($do_transaction) {
-                                                       $this->rollback();
-                                               }
-                                               return false;
-                                       }
-                               }
-                       }
-               }
-               if ($do_transaction) {
-                       $this->commit();
-               }
-               return true;
+               $sql = "DELETE FROM " . $table_string . " " . $condition_string;
+               $this->logger->debug($this->replaceParameters($sql, $conditions), ['callstack' => System::callstack(6)]);
+               return $this->e($sql, $conditions);
        }
 
        /**
@@ -1412,7 +1267,7 @@ class Database
         * @param string|array  $table      Table name or array [schema => table]
         * @param array         $fields     contains the fields that are updated
         * @param array         $condition  condition array with the key values
-        * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate)
+        * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
         *
         * @return boolean was the update successfull?
         * @throws \Exception
@@ -1512,7 +1367,7 @@ class Database
         *
         *
         * Example:
-        * $table = 'item';
+        * $table = 'post';
         * or:
         * $table = ['schema' => 'table'];
         * @see DBA::buildTableString()
@@ -1575,7 +1430,7 @@ class Database
         * @return int
         *
         * Example:
-        * $table = "item";
+        * $table = "post";
         *
         * $condition = ["uid" => 1, "network" => 'dspr'];
         * or:
@@ -1606,19 +1461,24 @@ class Database
 
                $row = $this->fetchFirst($sql, $condition);
 
-               // Ensure to always return either a "null" or a numeric value
-               return is_numeric($row['count']) ? (int)$row['count'] : $row['count'];
+               if (!isset($row['count'])) {
+                       $this->logger->notice('Invalid count.', ['table' => $table, 'row' => $row, 'expression' => $expression, 'condition' => $condition_string, 'callstack' => System::callstack()]);
+                       return 0;
+               } else {
+                       return (int)$row['count'];
+               }
        }
 
        /**
         * Fills an array with data from a query
         *
-        * @param object $stmt statement object
-        * @param bool   $do_close
+        * @param object $stmt     statement object
+        * @param bool   $do_close Close database connection after last row
+        * @param int    $count    maximum number of rows to be fetched
         *
         * @return array Data array
         */
-       public function toArray($stmt, $do_close = true)
+       public function toArray($stmt, $do_close = true, int $count = 0)
        {
                if (is_bool($stmt)) {
                        return [];
@@ -1627,6 +1487,9 @@ class Database
                $data = [];
                while ($row = $this->fetch($stmt)) {
                        $data[] = $row;
+                       if (($count != 0) && (count($data) == $count)) {
+                               return $data;
+                       }
                }
 
                if ($do_close) {
@@ -1731,7 +1594,7 @@ class Database
        public function close($stmt)
        {
 
-               $stamp1 = microtime(true);
+               $this->profiler->startRecording('database');
 
                if (!is_object($stmt)) {
                        return false;
@@ -1757,7 +1620,7 @@ class Database
                                break;
                }
 
-               $this->profiler->saveTimestamp($stamp1, 'database');
+               $this->profiler->stopRecording();
 
                return $ret;
        }