You are here

function apdqc_truncate_table in Asynchronous Prefetch Database Query Cache 7

Empties out a database table in a non metadata locking fashion.

Parameters

string $table: Name of the table you wish to truncate.

1 call to apdqc_truncate_table()
APDQCache::clear in ./apdqc.cache.inc
Implements DrupalCacheInterface::clear().

File

./apdqc.mysql.inc, line 892
APDQC Database interface code for MySQL database servers.

Code

function apdqc_truncate_table($table) {

  // Make sure the $mysqli object is available.
  $mysqli = apdqc_get_db_object(array(
    $table,
  ), array(
    '*',
  ));
  if (empty($mysqli)) {
    return db_truncate($table)
      ->execute();
  }

  // TRUNCATE is not a transaction safe statement if the table being cleared was
  // used in any query inside of a transaction since it is a DDL statement which
  // results in a metadata lock. Always use the slower, but non locking
  // transactional, DELETE on the cache_field table.
  if (Database::getConnection()
    ->inTransaction() || variable_get('cache_no_truncate', CACHE_NO_TRUNCATE) || $table === 'cache_field') {
    $query = Database::getConnection()
      ->prefixTables("DELETE FROM {" . db_escape_table($table) . "}");
    $result = apdqc_query(array(
      $table,
    ), array(
      '*',
    ), $query);
    if (is_string($result) && $result === 'NO DB') {
      return db_truncate($table)
        ->execute();
    }
    return;
  }

  // No Op if table is empty.
  $real_table_name = Database::getConnection()
    ->prefixTables("{" . db_escape_table($table) . "}");
  $results = $mysqli
    ->query("SELECT 1 FROM {$real_table_name} LIMIT 1");
  if (!empty($results) && $results instanceof mysqli_result) {
    $empty_table = $results
      ->fetch_row();
  }
  if (empty($results) || empty($empty_table)) {
    return;
  }

  // Renaming tables is a lot faster than truncate. Rename and then do an
  // async Truncate so we don't get stalled.
  // Make sure truncated table exists before trying to use it.
  $real_table_name_truncated = Database::getConnection()
    ->prefixTables("{" . db_escape_table($table) . "__truncated_table}");

  // Remove any values from the *__truncated_table if needed.
  $results = $mysqli
    ->query("SELECT 1 FROM {$real_table_name_truncated} LIMIT 1");
  if ($results === FALSE) {

    // Create truncated table if it does not exist.
    $mysqli
      ->query("CREATE TABLE IF NOT EXISTS {$real_table_name_truncated} LIKE {$real_table_name}");

    // Set to empty since this table was just created.
    $db_row = NULL;
  }
  else {

    // mysqli_result::fetch_row returns NULL when there are no more rows.
    $db_row = $results
      ->fetch_row();
  }
  if (!is_null($db_row)) {

    // Empty the truncated_table since it is not empty.
    $result = apdqc_query(array(
      $real_table_name_truncated,
    ), array(
      '*',
    ), "TRUNCATE {$real_table_name_truncated}");
    if (is_string($result) && $result === 'NO DB') {
      return db_truncate($table)
        ->execute();
    }
  }

  // Use rename so the truncate happens at the end of this request.
  $real_table_name_temp = Database::getConnection()
    ->prefixTables("{" . db_escape_table($table) . "__temp_table}");
  $query = "\n    RENAME TABLE {$real_table_name} TO {$real_table_name_temp},\n    {$real_table_name_truncated} TO {$real_table_name},\n    {$real_table_name_temp} TO {$real_table_name_truncated}\n  ";
  $mysqli
    ->query($query, MYSQLI_ASYNC);
  if (apdqc_kill_metadata_lock($mysqli->thread_id)) {

    // Use DELETE FROM syntax, as TRUNCATE is locking the database.
    $query = "DELETE FROM " . $real_table_name . "";
    $result = apdqc_query(array(
      $table,
    ), array(
      '*',
    ), $query);
    if (is_string($result) && $result === 'NO DB') {
      return db_truncate($table)
        ->execute();
    }

    // Return here as the table has been emptied.
    return;
  }

  // Run an async TRUNCATE.
  apdqc_query(array(
    $real_table_name_truncated,
  ), array(
    '*',
  ), "TRUNCATE {$real_table_name_truncated}", array(
    'async' => TRUE,
  ));
}