#706842: the taxonomy upgrade path is broken. From: Damien Tournoud --- simpletest/simpletest.info | 1 simpletest/tests/upgrade/upgrade.taxonomy.test | 49 +++++++++++++++ taxonomy/taxonomy.install | 80 +++++++++++++++++++----- 3 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 simpletest/tests/upgrade/upgrade.taxonomy.test diff --git modules/simpletest/simpletest.info modules/simpletest/simpletest.info index 63f61e6..076c606 100644 --- modules/simpletest/simpletest.info +++ modules/simpletest/simpletest.info @@ -40,3 +40,4 @@ files[] = tests/update.test files[] = tests/xmlrpc.test files[] = tests/upgrade/upgrade.test files[] = tests/upgrade/upgrade.poll.test +files[] = tests/upgrade/upgrade.taxonomy.test diff --git modules/simpletest/tests/upgrade/upgrade.taxonomy.test modules/simpletest/tests/upgrade/upgrade.taxonomy.test new file mode 100644 index 0000000..f088564 --- /dev/null +++ modules/simpletest/tests/upgrade/upgrade.taxonomy.test @@ -0,0 +1,49 @@ + 'Taxonomy upgrade path', + 'description' => 'Taxonomy upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFile = drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php'; + parent::setUp(); + } + + /** + * Basic tests for the taxonomy upgrade. + */ + public function testTaxonomyUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Visit the front page to assert for PHP warning and errors. + $this->drupalGet(''); + + // TODO: Check that taxonomy_vocabulary_node_type and taxonomy_term_node + // have been removed. + + // TODO: Check that the node type 'page' has been associated to a taxonomy + // reference field for each vocabulary. + + // TODO: Check that the node type 'story' has been associated to a taxonomy + // reference field for each vocabulary. It was not explicitely in + // $vocabulary->nodes but each node of type 'story' was associated to + // one or more terms. + + // TODO: Check that the node type 'poll' has been associated to no taxonomy + // reference field. + + // TODO: Check that each nodes of type 'page' and 'story' is associated to + // all the terms, except terms 'nid' and term '49 - nid' + + } +} diff --git modules/taxonomy/taxonomy.install modules/taxonomy/taxonomy.install index 385f556..b911351 100644 --- modules/taxonomy/taxonomy.install +++ modules/taxonomy/taxonomy.install @@ -394,7 +394,7 @@ function taxonomy_update_7004() { field_create_instance($instance); } } - db_drop_table('taxonomy_vocabulary_node_type'); + $fields = array('help', 'multiple', 'required', 'tags'); foreach ($fields as $field) { db_drop_field('taxonomy_vocabulary', $field); @@ -405,15 +405,6 @@ function taxonomy_update_7004() { * Migrate {taxonomy_term_node} table to field storage. */ function taxonomy_update_7005(&$sandbox) { - // Since we are upgrading from Drupal 6, we know that only - // field_sql_storage.module will be enabled. - $field = field_info_field($field['field_name']); - $data_table = _field_sql_storage_tablename($field); - $revision_table = _field_sql_storage_revision_tablename($field); - $etid = _field_sql_storage_etid('node'); - $value_column = $field['field_name'] . '_value'; - $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta', $value_column); - // This is a multi-pass update. On the first call we need to initialize some // variables. if (!isset($sandbox['total'])) { @@ -423,14 +414,51 @@ function taxonomy_update_7005(&$sandbox) { $query = db_select('taxonomy_term_node', 't'); $sandbox['total'] = $query->countQuery()->execute()->fetchField(); $found = (bool) $sandbox['total']; + $result = db_query('SELECT v.*, n.type FROM {taxonomy_vocabulary} v LEFT JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid ORDER BY v.weight, v.name'); + $vocabularies = array(); + foreach ($result as $record) { + // If no node types are associated with a vocabulary, the LEFT JOIN will + // return a NULL value for type. + if (isset($record->type)) { + $node_types[$record->vid][$record->type] = $record->type; + unset($record->type); + $record->nodes = $node_types[$record->vid]; + } + elseif (!isset($record->nodes)) { + $record->nodes = array(); + } + $vocabularies[$record->vid] = $record; + } + if (!empty($vocabularies)) { + $sandbox['vocabularies'] = $vocabularies; + } } else { + // Grab the current (first) vocabulary. When this vocabulary's terms have + // all been updated, it will be removed from the sandbox. + reset($sandbox['vocabularies']); + $vid = key($sandbox['vocabularies']); + $vocabulary = $sandbox['vocabularies'][$vid]; + + // Since we are upgrading from Drupal 6, we know that only + // field_sql_storage.module will be enabled. + $field = field_info_field('taxonomy_' . $vocabulary->machine_name); + $data_table = _field_sql_storage_tablename($field); + $revision_table = _field_sql_storage_revision_tablename($field); + $etid = _field_sql_storage_etid('node'); + $value_column = $field['field_name'] . '_tid'; + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'language', 'delta', $value_column); + // We do each pass in batches of 1000, this should result in a // maximum of 2000 insert queries each operation. - $batch = 1000 + $sandbox['last']; + $batch = 1000; + + // Track if we found rows in that run. + $found = FALSE; // Query and save data for the current revision. - $result = db_query_range('SELECT td.tid, tn.nid, td.weight, tn.vid, n2.type, n2.created, n2.sticky FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n2 ON tn.nid = n2.nid INNER JOIN {node} n ON tn.vid = n.vid AND td.vid = :vocabulary_id ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid), $sandbox['last'], $batch); + $result = db_query_range('SELECT td.tid, tn.nid, td.weight, tn.vid, n2.type, n2.created, n2.sticky FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n2 ON tn.nid = n2.nid INNER JOIN {node} n ON tn.vid = n.vid AND td.vid = :vocabulary_id ORDER BY td.weight ASC', $sandbox['last'], $batch, array(':vocabulary_id' => $vocabulary->vid)); + $deltas = array(); foreach ($result as $record) { $found = TRUE; @@ -438,7 +466,7 @@ function taxonomy_update_7005(&$sandbox) { // Start deltas from 0, and increment by one for each // term attached to a node. $deltas[$record->nid] = isset($deltas[$record->nid]) ? ++$deltas[$record->nid] : 0; - $values = array($etid, $record->nid, $record->vid, $record->type, $deltas[$record->nid], $record->tid); + $values = array($etid, $record->nid, $record->vid, $record->type, LANGUAGE_NONE, $deltas[$record->nid], $record->tid); db_insert($data_table)->fields($columns)->values($values)->execute(); // Update the {taxonomy_index} table. @@ -449,20 +477,38 @@ function taxonomy_update_7005(&$sandbox) { } // Query and save data for all revisions. - $result = db_query('SELECT td.tid, tn.nid, td.weight, tn.vid, n.type FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid AND td.vid = :vocabulary_id INNER JOIN {node} n ON tn.nid = n.nid ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid), $sandbox['last'][$batch]); + $result = db_query_range('SELECT td.tid, tn.nid, td.weight, tn.vid, n.type FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid AND td.vid = :vocabulary_id INNER JOIN {node} n ON tn.nid = n.nid ORDER BY td.weight ASC', $sandbox['last'], $batch, array(':vocabulary_id' => $vocabulary->vid)); $deltas = array(); foreach ($result as $record) { $found = TRUE; $sandbox['count'] += 1; // Start deltas at 0, and increment by one for each term attached to a revision. $deltas[$record->vid] = isset($deltas[$record->vid]) ? ++$deltas[$record->vid] : 0; - $values = array($etid, $record->nid, $record->vid, $record->type, $deltas[$record->vid], $record->tid); + $values = array($etid, $record->nid, $record->vid, $record->type, LANGUAGE_NONE, $deltas[$record->vid], $record->tid); db_insert($revision_table)->fields($columns)->values($values)->execute(); } - $sandbox['last'] = $batch; + + $sandbox['last'] += $batch; + + // If there were no rows returned, we're finished with the current vocab. + // Advance vid counter and reset batch counter or fall through and finish + // if there are no vocabularies left. + if (!$found) { + unset($sandbox['vocabularies'][$vid]); + if (!empty($sandbox['vocabularies'])) { + $found = TRUE; + $sandbox['last'] = 0; + } + } } if (!$found) { - db_drop_table('taxonomy_term_node'); + db_drop_table('taxonomy_vocabulary_node_type'); + db_drop_table('taxonomy_term_node'); + // If there are no vocabs, we're done. + $sandbox['#finished'] = TRUE; + } + else { + $sandbox['#finished'] = FALSE; } }