7 system.api.php hook_update_N(&$sandbox)
4.7 install.php hook_update_N()
5 install.php hook_update_N()
6 install.php hook_update_N(&$sandbox)
8 module.api.php hook_update_N(&$sandbox)

Perform a single update.

For each change that requires one or more actions to be performed when updating a site, add a new hook_update_N(), which will be called by update.php. The documentation block preceding this function is stripped of newlines and used as the description for the update on the pending updates task list. Schema updates should adhere to the Schema API.

Implementations of hook_update_N() are named (module name)_update_(number). The numbers are composed of three parts:

  • 1 digit for Drupal core compatibility.
  • 1 digit for your module's major release version (e.g., is this the 7.x-1.* (1) or 7.x-2.* (2) series of your module?). This digit should be 0 for initial porting of your module to a new Drupal core API.
  • 2 digits for sequential counting, starting with 00.

Examples:

  • mymodule_update_7000(): This is the required update for mymodule to run with Drupal core API 7.x when upgrading from Drupal core API 6.x.
  • mymodule_update_7100(): This is the first update to get the database ready to run mymodule 7.x-1.*.
  • mymodule_update_7200(): This is the first update to get the database ready to run mymodule 7.x-2.*. Users can directly update from 6.x-2.* to 7.x-2.* and they get all 70xx and 72xx updates, but not 71xx updates, because those reside in the 7.x-1.x branch only.

A good rule of thumb is to remove updates older than two major releases of Drupal. See hook_update_last_removed() to notify Drupal about the removals. For further information about releases and release numbers see: Maintaining a drupal.org project with Git

Never renumber update functions.

Implementations of this hook should be placed in a mymodule.install file in the same directory as mymodule.module. Drupal core's updates are implemented using the system module as a name and stored in database/updates.inc.

Not all module functions are available from within a hook_update_N() function. In order to call a function from your mymodule.module or an include file, you need to explicitly load that file first.

During database updates the schema of any module could be out of date. For this reason, caution is needed when using any API function within an update function - particularly CRUD functions, functions that depend on the schema (for example by using drupal_write_record()), and any functions that invoke hooks. See Update versions of API functions for details.

The $sandbox parameter should be used when a multipass update is needed, in circumstances where running the whole update at once could cause PHP to timeout. Each pass is run in a way that avoids PHP timeouts, provided each pass remains under the timeout limit. To signify that an update requires at least one more pass, set $sandbox['#finished'] to a number less than 1 (you need to do this each pass). The value of $sandbox['#finished'] will be unset between passes but all other data in $sandbox will be preserved. The system will stop iterating this update when $sandbox['#finished'] is left unset or set to a number higher than 1. It is recommended that $sandbox['#finished'] is initially set to 0, and then updated each pass to a number between 0 and 1 that represents the overall % completed for this update, finishing with 1.

See the Batch operations topic for more information on how to use the Batch API.

Parameters

array $sandbox: Stores information for multipass updates. See above for more information.

Return value

string|null Optionally, update hooks may return a translated string that will be displayed to the user after the update has completed. If no message is returned, no message will be presented to the user.

Throws

DrupalUpdateException|PDOException In case of error, update hooks should throw an instance of DrupalUpdateException with a meaningful message for the user. If a database query fails for whatever reason, it will throw a PDOException.

See also

Batch operations

Schema API

Update versions of API functions

hook_update_last_removed()

update_get_update_list()

Related topics

246 functions implement hook_update_N()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

aggregator_update_7000 in modules/aggregator/aggregator.install
Add hash column to aggregator_feed table.
aggregator_update_7001 in modules/aggregator/aggregator.install
Add aggregator teaser length to settings from old global default teaser length
aggregator_update_7002 in modules/aggregator/aggregator.install
Add queued timestamp.
aggregator_update_7003 in modules/aggregator/aggregator.install
Increase the length of {aggregator_feed}.url.
aggregator_update_7004 in modules/aggregator/aggregator.install
Add index on timestamp.

... See full list

File

modules/system/system.api.php, line 3485
Hooks provided by Drupal core and the System module.

Code

function hook_update_N(&$sandbox) {
  // For non-multipass updates, the signature can simply be;
  // function hook_update_N() {

  // For most updates, the following is sufficient.
  db_add_field('mytable1', 'newcol', array('type' => 'int', 'not null' => TRUE, 'description' => 'My new integer column.'));

  // However, for more complex operations that may take a long time,
  // you may hook into Batch API as in the following example.

  // Update 3 users at a time to have an exclamation point after their names.
  // (They're really happy that we can do batch API in this hook!)
  if (!isset($sandbox ['progress'])) {
    $sandbox ['progress'] = 0;
    $sandbox ['current_uid'] = 0;
    // We'll -1 to disregard the uid 0...
    $sandbox ['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1;
  }

  $users = db_select('users', 'u')
    ->fields('u', array('uid', 'name'))
    ->condition('uid', $sandbox ['current_uid'], '>')
    ->range(0, 3)
    ->orderBy('uid', 'ASC')
    ->execute();

  foreach ($users as $user) {
    $user->name .= '!';
    db_update('users')
      ->fields(array('name' => $user->name))
      ->condition('uid', $user->uid)
      ->execute();

    $sandbox ['progress'];
    ++$sandbox ['current_uid'] = $user->uid;
  }

  $sandbox ['#finished'] = empty($sandbox ['max']) ? 1 : ($sandbox ['progress'] / $sandbox ['max']);

  // To display a message to the user when the update is completed, return it.
  // If you do not want to display a completion message, simply return nothing.
  return t('The update did what it was supposed to do.');

  // In case of an error, simply throw an exception with an error message.
  throw new DrupalUpdateException('Something went wrong; here is what you should do.');
}

Comments

When implementing a multi-pass update and using the $sandbox parameter provided by batch API, note that the $sandbox['#finished'] is not preserved across function calls.

The code inside a hook_update_N() implementation should not call any API functions that are provided by any .module file, including its own, unless:

  1. The update code explicitly loads the module file, e.g., by calling drupal_load().
  2. It is absolutely certain that the called API function does not rely on other API functions which may not be available.

See also:

Schema updates should adhere to the Schema API: http://drupal.org/node/150215

The link in the quote above points to a document that, while helpful, is written for drupal 6.x and makes no mention of the fact that many of the database functions have changed in D7, much less a link to the actual D7 Schema API, which can be found here http://api.drupal.org/api/drupal/includes--database--database.inc/group/... .

The text for the update list on "update.php?op=selection" is the comment block of the update hook.

Means that:

<?php
/**
* My Update
*/
function hook_update_7001(&$sandbox) {
}
?>

Will give you "7001 - My Update" in the list of updates.

Good observation...

Instead of having to dissect the above example... I wrote a simple update hook function that loads every user (4 at a time) and performs an action on them.

The action currently is just a comment, because I just wanted to share a base template for the hook update, I hope someone finds this useful.

<?php
/**
 *
 * A hook update that goes through every user and performs an action
 *
 */
function hook_update_7000(&$sandbox){
  if(!isset(
$sandbox['progress'])){
   
$sandbox['progress'] = 0;
   
$sandbox['last_uid'] = 0;
   
$sandbox['limit'] = 4;
   
$sandbox['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1;
  }
 
$q = db_query('SELECT uid FROM users WHERE uid > '.$sandbox['last_uid'].' LIMIT '.$sandbox['limit'].'')->fetchCol();
 
$user_array = user_load_multiple($q,array());

  foreach(
$user_array as $loaded_user){   
   
//Perform actions on an individual user   
   
$sandbox['progress']++;
  }
 
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
 
$sandbox['last_uid'] = $loaded_user->uid;
}
?>

It seems the comment you put above the function is used in the interface, and therefore should not be a typical doxygen comment, but a description of the update.

Indeed. I've filed a bug on the docs about this: http://drupal.org/node/1619482

The docs say:

mymodule_update_7200(): This is the first update to get the database ready to run mymodule 7.x-2.*. Users can directly update from 6.x-2.* to 7.x-2.* and they get all 70xx and 72xx updates, but not 71xx updates, because those reside in the 7.x-1.x branch only.

This neglects to mention that it will also run any available new 6xxx updates too (ones higher than the schema_version).

If you want to remove fields from objects, this stackoverflow question has some answers.

Here is a great example on how to use this as a multipass update (Batch API) => http://bleen.net/blog/running-batch-processes-update-hook-bed

My hook is failing, and I'm throwing a DrupalUpdateException.

Still, drupal updates the schema in the system table.

Is there any way I can force update to fail so that subsequent hook_update_N functions are not run until the problem is fixed?

Just to clarify (because it wasn't immediately clear to me).

2 digits for sequential counting, starting with 00.

This means if you have version = 7.x-1.1 in your .info file, then your update function would be called hook_update_7101().

If an update partially fails, how can one force the update to fail gracefully without repeating again? Is there some variable that once can manipulate for this? I imagine some situation where the update was already performed and uploaded to the database outside of the module. I suppose one could remark the entire update function to not execute.

You can manually change the schema version so it won't attempt to run your update again, see https://api.drupal.org/api/drupal/includes!install.inc/function/drupal_s...

Dave Reid has written a custom drush command that can do this, see https://bitbucket.org/davereid/drush-schema-version

If you're using Drush but don't want to install the extension you can also just use the command

drush sql-query "UPDATE system SET schema_version = 7001 WHERE name = 'my_custom_module';"

I've got a write up on my blog that'll give you a bit more information http://interactivejunky.com/blog/resetting-module-schema-drush

Any any how to update serialized data? For example I update the node options, so from:

a:1:{i:0;s:6:"status";}

to

a:3:{i:0;s:6:"status";i:1;s:7:"promote";i:2;s:8:"revision";}

Get the data, pass it through unserialize, update the array as needed, then serialize and put it back into the database;

We're running into an issue where the first update to a module was hooked as my_module_update_7000 instead of 7100. When we run the updb, it will attempt to run this update even though the module is disabled. Is this due to the 7000 being the convention for d6 to d7?

You tried truncating the cache tables?

Drupal's update system runs all of the hook_update functions, even for disabled modules. See drupal_get_installed_schema_version for where the query is run to figure out which update hooks to run.

If you need to control the order that hooks run across different modules then you might be interested in hook_update_dependencies.