You are here

function schema_compare_table in Schema 7

Same name and namespace in other branches
  1. 8 schema.module \schema_compare_table()
  2. 5 schema.module \schema_compare_table()
  3. 6 schema.module \schema_compare_table()

Compares a reference specification (such as one returned by a module's hook_schema) to an inspected specification from the database.

Parameters

$inspect if not provided, the database is inspected.:

2 calls to schema_compare_table()
SchemaRegressionTest::testInspectionConflict518210 in tests/schema_regression.test
Test API for adding tables
schema_compare_schemas in ./schema.module
Compares two complete schemas.

File

./schema.module, line 493
The Schema module provides functionality built on the Schema API.

Code

function schema_compare_table($ref, $inspect = NULL) {
  $_db_type = db_driver();
  if (!isset($inspect)) {

    // TODO: Handle prefixing the D7 way
    //    $ref_name = db_prefix_tables('{' . $ref['name'] . '}');
    $ref_name = $ref['name'];
    $inspect = schema_dbobject()
      ->inspect(NULL, $ref_name);
    $inspect = $inspect[$ref['name']];
  }
  if (!isset($inspect)) {
    return array(
      'status' => 'missing',
    );
  }
  $reasons = $notes = array();
  $col_keys = array_flip(array(
    'type',
    'size',
    'not null',
    'length',
    'unsigned',
    'default',
    'scale',
    'precision',
  ));
  foreach ($ref['fields'] as $colname => $col) {

    // Many Schema types can map to the same engine type (e.g. in
    // PostgresSQL, text:{small,medium,big} are all just text).  When
    // we inspect the database, we see the common type, but the
    // reference we are comparing against can have a specific type.
    // We therefore run the reference's specific type through the
    // type conversion cycle to get its common type for comparison.
    //
    // Sadly, we need a special-case hack for 'serial'.
    $serial = $col['type'] == 'serial' ? TRUE : FALSE;
    $name = isset($ref['name']) ? $ref['name'] : '';
    $dbtype = schema_engine_type($col, $name, $colname);
    list($col['type'], $col['size']) = schema_schema_type($dbtype, $name, $colname);
    if ($serial) {
      $col['type'] = 'serial';
    }

    // If an engine-specific type is specified, use it.  XXX $inspect
    // will contain the schema type for the engine type, if one
    // exists, whereas dbtype_type contains the engine type.
    if (isset($col[$_db_type . '_type'])) {
      $col['type'] = $col[$_db_type . '_type'];
    }
    $col = array_intersect_key($col, $col_keys);
    if (!isset($inspect['fields'][$colname])) {
      $reasons[] = "{$colname}: not in database";
      continue;
    }

    // Account for schemas that contain unnecessary 'default' => NULL
    if (!isset($col['default']) || is_null($col['default']) && !isset($inspect['fields'][$colname]['default'])) {
      unset($col['default']);
    }
    $kdiffs = array();
    foreach ($col_keys as $key => $val) {
      if (!(isset($col[$key]) && !is_null($col[$key]) && $col[$key] !== FALSE && isset($inspect['fields'][$colname][$key]) && $inspect['fields'][$colname][$key] !== FALSE && $col[$key] == $inspect['fields'][$colname][$key] || (!isset($col[$key]) || is_null($col[$key]) || $col[$key] === FALSE) && (!isset($inspect['fields'][$colname][$key]) || $inspect['fields'][$colname][$key] === FALSE))) {

        // One way or another, difference between the two so note it to explicitly identify it later.
        $kdiffs[] = $key;
      }
    }
    if (count($kdiffs) != 0) {
      $reasons[] = "column {$colname} - difference" . (count($kdiffs) > 1 ? 's' : '') . " on: " . implode(', ', $kdiffs) . "<br/>declared: " . schema_phpprint_column($col) . '<br/>actual: ' . schema_phpprint_column($inspect['fields'][$colname]);
    }
    unset($inspect['fields'][$colname]);
  }
  foreach ($inspect['fields'] as $colname => $col) {
    $reasons[] = "{$colname}: unexpected column in database";
  }
  if (isset($ref['primary key'])) {
    if (!isset($inspect['primary key'])) {
      $reasons[] = "primary key: missing in database";
    }
    elseif ($ref['primary key'] !== $inspect['primary key']) {
      $reasons[] = "primary key:<br />declared: " . schema_phpprint_key($ref['primary key']) . '<br />actual: ' . schema_phpprint_key($inspect['primary key']);
    }
  }
  elseif (isset($inspect['primary key'])) {
    $reasons[] = "primary key: missing in schema";
  }
  foreach (array(
    'unique keys',
    'indexes',
  ) as $type) {
    if (isset($ref[$type])) {
      foreach ($ref[$type] as $keyname => $key) {
        if (!isset($inspect[$type][$keyname])) {
          $reasons[] = "{$type} {$keyname}: missing in database";
          continue;
        }

        // $key is column list
        if ($key !== $inspect[$type][$keyname]) {
          $reasons[] = "{$type} {$keyname}:<br />declared: " . schema_phpprint_key($key) . '<br />actual: ' . schema_phpprint_key($inspect[$type][$keyname]);
        }
        unset($inspect[$type][$keyname]);
      }
    }
    if (isset($inspect[$type])) {
      foreach ($inspect[$type] as $keyname => $col) {

        // this is not an error, the dba might have added it on purpose
        $notes[] = "{$type} {$keyname}: unexpected (not an error)";
      }
    }
  }
  $status = count($reasons) ? 'different' : 'same';
  return array(
    'status' => $status,
    'reasons' => $reasons,
    'notes' => $notes,
  );
}