You are here

transaction.module in Transaction 6

Same filename and directory in other branches
  1. 8 transaction.module
  2. 5 transaction.module

Provides a database transaction system for use with InnoDB tables in MySQL.

File

transaction.module
View source
<?php

/**
 * @file Provides a database transaction system for use with InnoDB tables
 * in MySQL.
 */

/**
 * Provides a nestable transaction system for handling commits and rollbacks.
 *
 * Although transactions can be nested, a rollback or commit from a nested
 * transaction will cause it to propagate upwards.
 */
class Transaction {

  /** The number of transactions that have been nested within this one. */
  private static $Layers = 0;

  /** Will the commit be allowed? */
  private static $AllowCommit = TRUE;

  /**
   * Create a new transaction.
   */
  public function __construct() {
    if (self::$Layers == 0) {
      db_query('BEGIN');
    }
    self::$Layers++;
  }

  /**
   * Complete the transaction by either performing a commit or a rollback
   * depending on the state of the object's allow_commit property.
   */
  public function __destruct() {
    self::$Layers--;
    if (self::$Layers == 0) {
      if (self::$AllowCommit) {
        db_query('COMMIT');
      }
      else {
        db_query('ROLLBACK');
      }

      // Reset the ROLLBACK propagator
      self::$AllowCommit = TRUE;
    }
  }

  /**
   * If the given value is FALSE (value AND type), roll back the current
   * transaction.
   *
   * @param boolean $var Roll back the current transaction iff TRUE.
   */
  public static function rollbackIfFalse($var) {
    if ($var === FALSE) {
      self::rollback();
    }
  }

  /**
   * Force this transaction to roll back when finished.
   */
  public static function rollback() {
    self::$AllowCommit = FALSE;
  }

  /**
   * Will this transaction roll back instead of committing?
   *
   * @return boolean TRUE if the transaction will roll back, FALSE if it
   *         will commit.
   */
  public static function willRollback() {
    return !self::$AllowCommit;
  }

}

/**
 * Utility function that abstracts transactionalized updates or inserts
 * on a single row.
 *
 * @param string $table (required) The un-prefixed table name without curly 
 *        braces.
 * @param mixed $key_field (required) The name(s) of the column(s) to use when
 *        identifying the row to update. If only a single column is being used
 *        to select the row, this may be a string. If more than one column
 *        is being used, this must be an array of strings.
 *        If this operation is an update, these columns must be present as keys
 *        in $fields with their desired WHERE = values.
 * @param array $fields An associative array mapping column names (keys) to
 *        the values to be inserted/updated. For updates, only the columns
 *        with updated values need to be in this array.
 *
 * @return mixed  
 */
function transaction_update($table, $key_field, $fields) {
  $txn = new Transaction();

  // Accept a string if the key field is one column
  $return_simple_key_field = FALSE;
  if (!is_array($key_field)) {
    $return_simple_key_field = TRUE;
    $key_field = array(
      $key_field,
    );
  }

  // Convert a passed object to an array
  $fields = (array) $fields;

  // Check if key columns are specified
  $key_field_set = TRUE;
  foreach ($key_field as $column) {
    if (!array_key_exists($column, $fields)) {
      $key_field_set = FALSE;
    }
  }

  // Update
  if ($key_field_set) {
    $sql = 'UPDATE {' . db_escape_table($table) . '} t SET ';
    $where = '';
    $updates = array();
    foreach ($fields as $key => $value) {
      if (!in_array($key, $key_field)) {
        if ($value !== NULL) {
          $updates[] = 't.' . $key . ' = "' . db_escape_string($value) . '"';
        }
        else {
          $updates[] = 't.' . $key . ' = NULL';
        }
      }
    }
    $sql .= implode(',', $updates) . ' ';
    $where .= ' WHERE 1 ';
    foreach ($key_field as $column) {
      if ($fields[$column] !== NULL) {
        $where .= 'AND ' . $column . ' = "' . $fields[$column] . '" ';
      }
      else {
        $where .= 'AND ' . $column . ' IS NULL ';
      }
    }
    $sql .= $where;
    db_query($sql);

    // Return if we've actually updated something
    if (db_affected_rows()) {

      // If we were passed a simple key, return the same
      if ($return_simple_key_field) {
        return $fields[$key_field[0]];
      }
      $return_fields = array();
      foreach ($key_field as $column) {
        $return_fields[$column] = $fields[$column];
      }
      return $return_fields;
    }

    // Return if there was nothing to change, but the row actually exists
    $sql = 'SELECT COUNT(*) FROM {' . db_escape_table($table) . '} ' . $where;
    $exists = db_result(db_query($sql, 0, 1));
    if ($exists) {
      $return_fields = array();
      foreach ($key_field as $column) {
        $return_fields[$column] = $fields[$column];
      }
      return $return_fields;
    }
  }
  else {

    // We can't autogenerate if the key is more than one column
    if (count($key_field) > 1) {
      return FALSE;
    }
  }

  // Insert
  $sql = 'INSERT INTO {' . db_escape_table($table) . '} ';
  $keys = array_keys($fields);
  $values = array();
  foreach ($fields as $key => $value) {
    if ($value !== NULL) {
      $values[] = '"' . db_escape_string($value) . '"';
    }
    else {
      $values[] = 'NULL';
    }
  }
  $sql .= '({' . db_escape_table($table) . '}.' . implode(',{' . db_escape_table($table) . '}.', $keys) . ') ';
  $sql .= 'VALUES (' . implode(',', $values) . ')';
  db_query($sql);
  if (!$key_field_set) {
    $fields[$key_field[0]] = db_last_insert_id($table, $key_field[0]);
  }

  // If we were passed a scalar key, return the key of the new row as a scalar
  if ($return_simple_key_field) {
    return $fields[$key_field[0]];
  }
  $return_fields = array();
  foreach ($key_field as $column) {
    $return_fields[$column] = $fields[$column];
  }
  return $return_fields;
}

Functions

Namesort descending Description
transaction_update Utility function that abstracts transactionalized updates or inserts on a single row.

Classes

Namesort descending Description
Transaction Provides a nestable transaction system for handling commits and rollbacks.