/**
  * Prepare (if needed) and execute SQL query.
  *
  * @param  string             $sql
  * @param  array|null         $arguments
  * @return mysqli_result|bool
  */
 private function prepareAndExecuteQuery($sql, $arguments)
 {
     if ($this->log || $this->on_log_query) {
         $microtime = microtime(true);
         $prepared_sql = empty($arguments) ? $sql : call_user_func_array([&$this, 'prepare'], array_merge([$sql], $arguments));
         $result = $this->link->query($prepared_sql);
         $execution_time = rtrim(number_format(microtime(true) - $microtime, 6, '.', ''), '0');
         if ($this->log) {
             if ($result === false) {
                 $this->log->error('Query error {error_message}', ['error_message' => $this->link->error, 'error_code' => $this->link->errno, 'sql' => $prepared_sql, 'exec_time' => $execution_time]);
             } else {
                 $this->log->debug('Query {sql} executed in {exec_time}s', ['sql' => $prepared_sql, 'exec_time' => $execution_time]);
             }
         }
         if ($this->on_log_query) {
             call_user_func($this->on_log_query, $prepared_sql, $execution_time);
         }
         return $result;
     } else {
         return empty($arguments) ? $this->link->query($sql) : $this->link->query(call_user_func_array([&$this, 'prepare'], array_merge([$sql], $arguments)));
     }
 }