Upgrade API

From MoodleDocs

Introduction

If you have done the right thing, Moodle will automatically create the database tables for your plugin when you visit the Admin notifications page (.../admin/index.php). This process is controlled by three files within your plugin:

version.php
This records the version of the plugin code
db/install.xml
This is used when someone installs your plugin for the first time.
db/upgrade.php
This is used when someone who had an older version of your plugin installed upgrades to the latest version.

In addition, Moodle also stores in the database the currently installed version of each plugin.

In Moodle 1.9 and before, this is stored in the mdl_config table, in a row with the name plugintype_pluginname_version. For example qtype_myqtype_version. The exception to this rule are modules and blocks. Installed module version numbers are stored in the mdl_modules table. Block version numbers are in mdl_block.

In Moodle 2.0 an beyond, plugin version numbers are stored in the mdl_config_plugins table, with plugintype_pluginname in the plugin column, and 'version' in the name column - with the same exception for modules and blocks.

A specific example

For the rest of this document, I will use a particular example, because it should make the explanation easier. You should be able to see how to generalise it.

We will suppose you that you are making a new question type myqtype. This is plugin type qtype, and the code will be in the question/type/myqtype folder. The currently installed version number will be stored in the qtype_myqtype_version row of the mdl_config table.

In addition, we will just consider the first two releases of the plugin. The first release will have version number 2008080100, and will just use one database table mdl_question_myqtype, with two columns col1 and col2. Then we will suppose that the second release is 2008080200, and that requires an extra column, newcol, to be added to the mdl_question_myqtype table.

The files you need for the first release

In what follows, the bits of code you need to replace are in bold.

version.php
$plugin->version  = 2008080100;
$plugin->requires = XXXXXXXXXX; // Copy the current value from the top-level version.php file.
db/install.xml
This file, which you should create with the XMLDB editor, should contain the definition for your mdl_question_myqtype table, with the two columns col1 and col2.

At this stage, you do not need a db/upgrade.php file.

The files you need for the second release

version.php
$plugin->version  = 2008080200;
$plugin->requires = XXXXXXXXXX; // Copy the current value from the top-level version.php file.
db/install.xml
This file should now contain the updated definition for your mdl_question_myqtype table, with three columns col1, col2 and newcol. You modify this file using the XMLDB editor.
db/upgrade.php
This file should contain the code that people need to run to upgrade from version 2008080100 of your plugin. That is, the code to add a column newcol to the mdl_question_myqtype table. You don't have to write this code yourself as the XMLDB editor will generate it for you. The upgrade.php file should contain a single function xmldb_qtype_myqtype_upgrade that looks a bit like:
function xmldb_qtype_myqtype_upgrade($oldversion = 0) {
    $result = true;

    /// Add a new column newcol to the mdl_question_myqtype
    if ($result && $oldversion < 2008080200) {
        // Code to add the column, generated by the 'View PHP Code' option of the XMLDB editor.
    }

    return $result;
}

Hint: If you are modifying or adding a field/table, get the XMLDB editor to generate the PHP update code for you after making the changes in the editor. If you are deleting one, you need to generate the PHP code before making the change - or you won't be able to select the field/table to write the code for, because it no longer exists.

What happens when a user installs or upgrades your plugin

The process is triggered when an administrator goes to the Admin notifications page (.../admin/index.php). The code that does the work is the upgrade_plugins function in lib/adminlib.php and reading this code is the best way to find out exactly what happens. In pseudo-code, what it does is:

For each plugin of this type (e.g. all qtype plugins) {
    // For the body of this loop, suppose the current plugin being processed is myqtype.

    Check that question/type/myqtype/version.php, .../db/upgrade.php and .../db/install.php exist.

    if ($CFG->qtype_myqtype_version exists, and is less than the number in version.php) {
        Call the upgrade function xmldb_qtype_myqtype_upgrade from
                upgrade.php, passing the old version number ($CFG->qtype_myqtype_version)
                which says what is currently installed
        Update $CFG->qtype_myqtype_version to the latest number from version.php
                to record what is currently installed
    }

    else if ($CFG->qtype_myqtype_version does not exist) {
        Create the tables from the definitions in install.xml
        Update $CFG->qtype_myqtype_version to the latest number from version.php
                to record what is currently installed
    }
}

Of course, it is a bit more complex that than. However, the code in the upgrade_plugins is quite clear, and I encourage you to go and have a look at it so you can see all the details of how it works.

Let us now look at some worked examples:

User installs version 2008080100 of myqtype

To start with, the mdl_question_myqtype does not exist, and there will not be a qtype_myqtype_version row in the mdl_config table.

  1. The user will unzip myqtype.zip into the question/type folder.
  2. The user will visit the Admin notifications page.
  3. This will trigger Moodle to search for plugings to upgrade. It will find that the code for the qtype_myqtype plugin is now present, but there is no trace of it in the database, so it will install the plugin from the install.xml file.

At the end of this process, the mdl_question_myqtype table will exist with the two columns col1 and col2; and the qtype_myqtype_version row in the mdl_config table will contain 2008080100.

User upgrades from version 2008080100 to version 2008080200 of myqtype

To start with, the mdl_question_myqtype table will exist with the two columns col1 and col2; and the qtype_myqtype_version row in the mdl_config table will contain 2008080100.

  1. The user will delete the old question/type/myqtype folder.
  2. The user will unzip the new myqtype.zip into the question/type folder.
  3. The user will visit the Admin notifications page.
  4. This will trigger Moodle to search for plugings to upgrade. It will find that the code for version 2008080200 the qtype_myqtype plugin is now present, but the installed version of the the database tables (qtype_myqtype_version) is 2008080100. Therefore, it will call xmldb_qtype_myqtype_upgrade from upgrade.php, passing 2008080100 as the $oldversion argument.

At the end of this process, the mdl_question_myqtype table will now have three columns col1, col2 and newcol; and the qtype_myqtype_version row in the mdl_config table will contain 2008080200.

User installs version 2008080200 of myqtype into a clean Moodle install

To start with, the mdl_question_myqtype does not exist, and there will not be a qtype_myqtype_version row in the mdl_config table.

  1. The user will unzip the 2008080200 version of myqtype.zip into the question/type folder.
  2. The user will visit the Admin notifications page.
  3. This will trigger Moodle to search for plugings to upgrade. It will find that the code for the qtype_myqtype plugin is now present, but there is no trace of it in the database, so it will install the plugin from the install.xml file.

At the end of this process, the mdl_question_myqtype table will exist with three columns col1, col2 and newcol; and the qtype_myqtype_version row in the mdl_config table will contain 2008080200.

Summary

The first time a user installs any version of your plugin, the install.xml file will be used to create all the required database tables. Therefore install.xml should always contain the definition of the up-to-date database structure. Moodle recognises this situation because there is a version.php file on disc, but there is no plugintype_pluginname_version value in the mdl_config table.

If the user already had a version of your plugin installed, and then upgrades to a newer version, Moodle will detect this because the version.php file will contain a newer version number than the plugintype_pluginname_version value in the mdl_config table. In this case, Moodle will run the code in the upgrade.php file, passing in the old version number, so that the correct bits of upgrade can be run, as controlled by the if ($oldversion < XXXXXXXXXX) blocks of code.

The contents of the install.xml and upgrade.php files should be generated using the XMLDB editor.

Database upgrades and stable branches

The simple rule is, never make any database changes on a stable branch. You only need to read this section in the rare situations where a database change on the stable branch is unavoidable.

Warning!!! advanced material follows.

Suppose, in order to fix a bug, you need to make a database change in Moodle 1.9.3+ (which must also be merged into HEAD). The root of the problem is that people may upgrade their Moodle in three different ways, which

  • Upgrade from <=1.9.3 to 1.9.4 - this executes the ugprade script on the 1.9 branch.
  • Upgrade from <=1.9.3 directly to >=2.0 - this executes the upgrade script on the HEAD branch.
  • Upgrade from 1.9.4 to >=2.0 - in this case, you must ensure that the upgrade on HEAD is not executed.

The normal way to do this is ensure that your database upgrade is idempotent. That is, it does not matter if you do it twice. So for example, instead of doing

       $dbman->create_table($table);

you should do

       if (!$dbman->table_exists($table)) {
           $dbman->create_table($table);
       }

You should also think about what version numbers to put in your version.php file on each branch. Above all, test carefully.

See also