diff --git a/lib/schema.php b/lib/schema.php index c32e6b5230..20bd95f28a 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -142,6 +142,7 @@ class Schema */ public function buildCreateTable($name, $def) { + $def = $this->validateDef($name, $def); $def = $this->filterDef($def); $sql = array(); @@ -517,7 +518,7 @@ class Schema * @return array of SQL statements */ - function buildEnsureTable($tableName, $def) + function buildEnsureTable($tableName, array $def) { try { $old = $this->getTableDef($tableName); @@ -527,6 +528,7 @@ class Schema // Filter the DB-independent table definition to match the current // database engine's features and limitations. + $def = $this->validateDef($tableName, $def); $def = $this->filterDef($def); $statements = array(); @@ -851,10 +853,10 @@ class Schema * with plugins written for 0.9.x. * * @param string $tableName - * @param array $defs + * @param array $defs: array of ColumnDef objects * @return array */ - function oldToNew($tableName, $defs) + protected function oldToNew($tableName, array $defs) { $table = array(); $prefixes = array( @@ -864,7 +866,6 @@ class Schema 'big', ); foreach ($defs as $cd) { - $cd->addToTableDef($table); $column = array(); $column['type'] = $cd->type; foreach ($prefixes as $prefix) { @@ -928,6 +929,31 @@ class Schema return $tableDef; } + /** + * Validate a table definition array, checking for basic structure. + * + * If necessary, converts from an old-style array of ColumnDef objects. + * + * @param string $tableName + * @param array $def: table definition array + * @return array validated table definition array + * + * @throws Exception on wildly invalid input + */ + function validateDef($tableName, array $def) + { + if (count($defs) && $defs[0] instanceof ColumnDef) { + $def = $this->oldToNew($tableName, $defs); + } + + // A few quick checks :D + if (!isset($def['fields'])) { + throw new Exceptioni("Invalid table definition for $tableName: no fields."); + } + + return $def; + } + function isNumericType($type) { $type = strtolower($type); diff --git a/lib/schemaupdater.php b/lib/schemaupdater.php new file mode 100644 index 0000000000..b872b0d578 --- /dev/null +++ b/lib/schemaupdater.php @@ -0,0 +1,117 @@ +. + * + * @category Database + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class SchemaUpdater +{ + public function __construct($schema) + { + $this->schema = $schema; + $this->conn = $conn; + $this->checksums = $this->getChecksums(); + } + + /** + * @param array $tableDefs + * @fixme handle tables that belong on different database servers...? + */ + public function checkTables(array $tableDefs) + { + $checksums = $this->checksums; + foreach ($tableDefs as $table => $def) { + $checksum = $this->tableChecksum($def); + if (empty($checksums[$table])) { + common_log(LOG_DEBUG, "No previous schema_version for $table: updating to $checksum"); + } else if ($checksums[$table] == $checksum) { + common_log(LOG_DEBUG, "Last schema_version for $table up to date: $checksum"); + continue; + } else { + common_log(LOG_DEBUG, "Last schema_version for $table is {$checksums[$table]}: updating to $checksum"); + } + $this->conn->query('BEGIN'); + $this->schema->ensureTable($table, $def); + $this->saveChecksum($table, $checksum); + $this->conn->commit(); + } + } + + /** + * Calculate a checksum for this table definition array. + * + * @param array $def + * @return string + */ + public function checksum(array $def) + { + $flat = serialize($def); + return sha1($flat); + } + + /** + * Pull all known table checksums into an array for easy lookup. + * + * @return array: associative array of table names to checksum strings + */ + protected function getChecksums() + { + $checksums = array(); + + $sv = new Schema_version(); + $sv->find(); + while ($sv->fetch()) { + $checksums[$sv->table_name] = $sv->checksum; + } + + return $checksums; + } + + /** + * Save or update current available checksums. + * + * @param string $table + * @param string $checksum + */ + protected function saveChecksum($table, $checksum) + { + $sv = new Schema_version(); + $sv->table_name = $table; + $sv->checksum = $checksum; + $sv->modified = common_sql_now(); + if (isset($this->checksums[$table])) { + $sv->update(); + } else { + $sv->insert(); + } + $this->checksums[$table] = $checksum; + } +} diff --git a/scripts/dumpschema.php b/scripts/dumpschema.php index 613364487a..2b238f0066 100644 --- a/scripts/dumpschema.php +++ b/scripts/dumpschema.php @@ -28,11 +28,12 @@ Attempt to pull a schema definition for a given table. --raw skip compatibility filtering for diffs --create dump SQL that would be run to update or create this table --build dump SQL that would be run to create this table fresh + --checksum just output checksums from the source schema defs END_OF_CHECKSCHEMA_HELP; -$longoptions = array('diff', 'all', 'create', 'update', 'raw'); +$longoptions = array('diff', 'all', 'create', 'update', 'raw', 'checksum'); require_once INSTALLDIR.'/scripts/commandline.inc'; function indentOptions($indent) @@ -207,6 +208,24 @@ function tweakPrimaryKey($def) return $def; } +function dumpChecksum($tableName) +{ + $schema = Schema::get(); + $def = getCoreSchema($tableName); + + $updater = new SchemaUpdater($schema); + $checksum = $updater->checksum($def); + $old = @$updater->checksums[$tableName]; + + if ($old == $checksum) { + echo "OK $checksum $tableName\n"; + } else if (!$old) { + echo "NEW $checksum $tableName\n"; + } else { + echo "MOD $checksum $tableName (was $old)\n"; + } +} + if (have_option('all')) { $args = getCoreTables(); } @@ -219,6 +238,8 @@ if (count($args)) { dumpBuildTable($tableName); } else if (have_option('update')) { dumpEnsureTable($tableName); + } else if (have_option('checksum')) { + dumpChecksum($tableName); } else { dumpTable($tableName, true); }