Hi All,

I am developing a module which creates a database table using hook_schema(). I want to change the data type of one of the fields from varchar to int. I made the changes to the module install file, disabled the module, uninstalled it, and cleared the cache. However, when I reinstall the module, the data type remains unchanged. I cannot work out why this would be the case. I checked, and the table is being successfully dropped when I uninstall the module, so the problem is when the module is reinstalled.

Is there some kind of cached copy of the schema stored in the Drupal system, which is created the first time the module in installed? How can this be removed? I don't see the point of writing an schema update function at this stage since the module is in a very early stage of development and is only used on my own drupal installation.

Original function

Function namecards_schema() {
  $schema['user_namecard_settings'] = array(
    'description' => t('Stores custom UI setting information for a given user'),
    'fields' => array(
      'uid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => t('The uid is the id of the user to which these settings belong to.'),
      ),
      'default_page' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => 'organizations',
        'description' => t('The default page shown when user logins in.'),
      ),
      'font_size' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 2,
        'description' => t('Is the relative default font size used. total range of value is 1-5 (Extra small - extra large)'),
      ),
    ),
    'primary key' => array('uid'),
  );
	
  return $schema;
}

Updated function (data type and related properties have been updated for field "default_page")

Function namecards_schema() {
  $schema['user_namecard_settings'] = array(
    'description' => t('Stores custom UI setting information for a given user'),
    'fields' => array(
      'uid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => t('The uid is the id of the user to which these settings belong to.'),
      ),
      'default_page' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 1, // 0 => 'browse', 1 => 'organizations', 2 => 'events'
        'description' => t('The default page shown when user logins in.'),
      ),
      'font_size' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 2, // 0 => 'Extra-small', 1 => 'Small', 2 => 'Normal', 3 => 'Large', 4 => 'Extra-large'
        'description' => t('Is the relative default font size used. total range of value is 1-5 (extra small - extra large)'),
      ),
    ),
    'primary key' => array('uid'),
  );
	
  return $schema;
}

Comments

jaypan’s picture

The schema is not cached, and the database tables should be created with the new database definition. Are you sure you aren't editing the wrong file? i.e. editing one copy and installing another.

Contact me to contract me for D7 -> D10/11 migrations.

jason_gates’s picture

Hi,
Can you please post your implementation of hook_uninstall(). Docs here: http://api.drupal.org/api/drupal/developer--hooks--install.php/function/...

begun’s picture

Here is the content of the namecards.install file, including implementation of hook_uninstall().

/**
* Implementation of hook_requirements()
*/
function namecards_requirements($phase) {
  $requirements = array();
  $t = get_t();

  // an array of the dependencies
  // where the array key is the module machine-readable name
  // and the value is the module human-readable name
  $dependencies = array(
    'content' => 'Content',
    'fieldgroup' => 'Field Group',
    'text' => 'Text',
    'nodereference' => 'Node Reference',
    'computed_field' => 'Computed Field',
    'number' => 'Number',
    'address_fld' => 'Address Field',
    'noderelationships' => 'Node Relationships',
    'noderefcreate' => 'Node Reference Create',
    'views' => 'Views',
    'jquery_ui' => 'jQuery UI',
    'date' => 'Date',
    'date_popup' => 'Date',
    'pathauto' => 'Path Auto',
  );
	
  switch ($phase) {
    case 'install':
      $error = FALSE;
      $value = '';
      foreach ($dependencies as $dependency => $module_nice_name) {
        if (!module_exists($dependency)) {
          $error = TRUE;
          $value .= $t($module_nice_name . " to be pre-installed; ");
          $severity = REQUIREMENT_ERROR;
        }
      }

      if ($error) {
        $requirements['namecards'] = array(
          'title' => $t('Namecards requires: '),
          'value' => $value . $t(' if the required modules are now installed, please enable this module again.'),
          'severity' => $severity,
        );
      }
      break;
    }

  return $requirements;
}



/**
 * Implementation of hook_install()
 */
function namecards_install() {
  if (!db_table_exists('user_namecard_settings')) {
    drupal_install_schema('user_namecard_settings');
    drupal_set_message('Table \'user_namecard_settings\' has been created');
  }
  // Create node types required by namecard module.
  _namecards_save_cck_node('position');
  _namecards_save_cck_node('department');
  _namecards_save_cck_node('event');
  _namecards_save_cck_node('organization');
  _namecards_save_cck_node('namecard');
}

/**
 * Implementation of hook_uninstall()
 */
function namecards_uninstall() {
  if (db_table_exists('user_namecard_settings')) {
    drupal_uninstall_schema('user_namecard_settings');
    drupal_set_message('Table \'user_namecard_settings\' has been deleted');
  }
  // Delete the content types used by namecards module
  node_type_delete('namecard_namecard');
  node_type_delete('namecard_event');
  node_type_delete('namecard_organization');
  node_type_delete('namecard_position');
  node_type_delete('namecard_department');
  menu_rebuild();
}

// EXAMPLE hook_update_N
// every hook_update_N will look the same
// and you will need to write a new one
// every time you update the .def.inc file

//function namecards_update_1() {
//    _namecards_save_cck_node('event');
//    return array();
//}

/**
 * Implementation of hook_schema()
 */

function namecards_schema() {
  $schema['user_namecard_settings'] = array(
    'description' => t('Stores custom UI setting information for a given user'),
    'fields' => array(
      'uid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => t('The uid is the id of the user to which these settings belong to.'),
      ),
      'default_page' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 1, // 0 => 'browse', 1 => 'organizations', 2 => 'events'
        'description' => t('The default page shown when user logins in.'),
      ),
      'font_size' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 2, // 0 => 'Extra-small', 1 => 'Small', 2 => 'Normal', 3 => 'Large', 4 => 'Extra-large'
        'description' => t('Is the relative default font size used. total range of value is 1-5 (extra small - extra large)'),
      ),
    ),
    'primary key' => array('uid'),
  );
	
  return $schema;
}

function _namecards_save_cck_node($str) {
    module_load_include('inc', 'namecards', 'namecards.def');
    $content = _namecards_cck_export($str);

    // we do not want too many modules enabled - the content_copy and
    // alternate_content_copy modules are just needed in order to install the
    // content type, so we just require them here (require_once prevent to
    // include it more than once in case it is already enabled)
    require_once './' . drupal_get_path('module', 'content') .  '/modules/content_copy/content_copy.module';
    require_once './' . drupal_get_path('module', 'alternate_content_copy') .  '/alternate_content_copy.module';

    alternate_content_copy_import_content($content);
}
jaypan’s picture

That looks fine. Again, are you sure that the file you are editing is the same as the form that is being executed on your server?

Contact me to contract me for D7 -> D10/11 migrations.

begun’s picture

I checked the other custom modules I have on the server and found one other module which uses the exact same schema definition. Since the hook_schema implementation in this other module is the same, I thought this must be what is causing the problem (although I am not sure how, since this module has not been enabled).

Just to be safe I have deleted this other module entirely from the server. Now when I try installing my module I get a strange error message:


warning: Invalid argument supplied for foreach() in C:\www\drupal6\includes\common.inc on line 3389.
warning: Invalid argument supplied for foreach() in C:\www\drupal6\includes\common.inc on line 3310.

I checked common.inc and found these warnings refer to following two functions:

function drupal_install_schema($module) {
$schema = drupal_get_schema_unprocessed($module);
_drupal_initialize_schema($module, $schema);

$ret = array();
foreach ($schema as $name => $table) {
db_create_table($ret, $name, $table);
}
return $ret;
}
.........
function _drupal_initialize_schema($module, &$schema) {
// Set the name and module key for all tables.
foreach ($schema as $name => $table) {
if (empty($table['module'])) {
$schema[$name]['module'] = $module;
}
if (!isset($table['name'])) {
$schema[$name]['name'] = $name;
}
}
}

jaypan’s picture

    drupal_install_schema('user_namecard_settings');

The value passed should be the name of your hook_schema (minus '_schema'), not the name of the table.

Contact me to contract me for D7 -> D10/11 migrations.

begun’s picture

Thanks Jay, you got it

jason_gates’s picture

Hi,
Your namecards_uninstall() implementation is using a "mysql" specific routine db_table_exists(). How about removing the conditional statement? Are you sure you are using "mysql" (i.e. not mysqli or postgres) ?

How are you debugging this? How are you verifying the database schema?

However, when I reinstall the module, the data type remains unchanged.

.
OK, how are you determining the schema? What database engine are you using, what database driver are you using?

Do you know how to manually drop a table?

begun’s picture

It turns out that I have another custom module (called module 'user_namecard_settings') which uses the exact same schema definition, which explains why the table remains unchanged when I reinstall the module 'namecards' (the module I am trying to work with). I know this because I have tested it. If I update the schema definition in module 'user_namecard_settings', sure enough the correct table is created when I install module 'namecards'.

The strange thing is that module 'user_namecard_settings' is not installed/enabled, so I don't understand why it would be effecting module 'namecards'. Still, I decided to delete module 'user_namecard_settings', just to be safe. However, when I now try to install module 'namecards' I get warning messages (see post above) and the table is not created.

As an experiment, I put the module 'user_namecard_settings' back on the server (e.g. place it in the modules directory) without enabling it within Drupal. As soon as I do this the correct table is created/deleted when I install/uninstall module 'namecards'.

Why on earth would such a relationship exist between these two modules?

BTW: I am using mysql. Removing the conditional statement doesn't help. I am using 'schema module' and PhpMyAdmin to debug.

Code from module 'user_namecard_settings' install file (i.e. user_namecard_settings.install)

// $Id:$

/**
 * Implementation of hook_install()
 */
Function user_namecard_settings_install() {
  if (!db_table_exists('user_namecard_settings')) {
	  drupal_install_schema('user_namecard_settings');
	  drupal_set_message('Table \'user_namecard_settings\' has been deleted');
	}
}

/**
 * Implementation of hook_schema()
 */

Function user_namecard_settings_schema() {
  $schema['user_namecard_settings'] = array(
    'description' => t('Stores custom UI setting information for a given user'),
    'fields' => array(
      'uid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => t('The uid is the id of the user to which these settings belong to.'),
      ),
      'default_page' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 1, // 0 => 'browse', 1 => 'organizations', 2 => 'events'
        'description' => t('The default page shown when user logins in.'),
      ),
      'font_size' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 2, // 0 => 'Extra-small', 1 => 'Small', 2 => 'Normal', 3 => 'Large', 4 => 'Extra-large'
        'description' => t('Is the relative default font size used. total range of value is 1-5 (extra small - extra large)'),
      ),
    ),
    'primary key' => array('uid'),
  );
	
  return $schema;
}

/**
 * Implementation of hook_uninstall()
 */
Function user_namecard_settings_uninstall() {
  if (db_table_exists('user_namecard_settings')) {
	  drupal_uninstall_schema('user_namecard_settings');
	  drupal_set_message('Table \'user_namecard_settings\' has been deleted');
	}
}
begun’s picture

I was using the table name instead of the module name in the hook_install/hook_unistall functions [insert embarrassed smiley here]. Since the table name was the same as the other module name, it kept using the schema definition in the other module (http://drupal.org/node/973064#comment-3716460).

Thanks Mat and Jay for your help.