I tried to use this function inside a hook_install after installing a schema and it is not working.

Is it a bug or there is a good reason? Probably there is a good reason why it is not working there but as the documentation does not say nothing about it and I use schema API for module installation process I have tried to use that schema API function there and it is not working.

Exactly same code works on other functions so must be a restriction inside hook_install.

Can someone explain why?

Comments

apaderno’s picture

What is the code you were executing? What is the database schema?

manfer’s picture

The problem shouldnt be the code. Exactly same code works on other function while module is working.

The problem seems to be on hook_install.

On hook_install I only does this process:

  • Install the schema with drupal_install_schema
  • Create an object that conforms to the schema
  • Try to insert it on the database table with drupal_write_record

That insertion is not working. Confirmed not new record on database table. Confirmed drupal_write_record returned FALSE.

Exactly the same object creation that conforms to the schema, followed by the exactly same call to drupal_write_record on a function on normal process of module works perfect.

I was thinking there were some restriction to use drupal_write_record inside hook_install and someone could explain but if it is necesary I would post the code tomorrow.

Thanks.

manfer’s picture

This is the schema:

function pageear_schema() {
  $schema['pageears'] = array(
    'description' => 'Stores pageear settings.',
    'fields' => array(
      'peid' => array(
        'type' => 'serial',
        'not null' => TRUE,
        'description' => 'Primary Key: Unique pageear ID.',
      ),
      'status' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
        'size' => 'tiny',
        'description' => 'Pageear enabled status. (1 = enabled, 0 = disabled).',
      ),
      'name' => array(
        'type' => 'varchar',
        'length' => 64,
        'not null' => TRUE,
        'default' => 'Sample Pageear',
        'description' => 'Name of the pageear. The name will be added to the pageear class to enable custom theming.',
      ),
      'peelPosition' => array(
        'type' => 'varchar',
        'length' => 9,
        'not null' => TRUE,
        'default' => 'topright',
        'description' => 'Position of pageear. In left or right top browser corner (topleft: left, topright: right).',
      ),
      'flagWidth' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 100,
        'size' => 'normal',
        'description' => 'Width of flag.',
      ),
      'flagHeight' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 100,
        'size' => 'normal',
        'description' => 'Height of flag.',
      ),
      'peelWidth' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 500,
        'size' => 'normal',
        'description' => 'Width of peel.',
      ),
      'peelHeight' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 500,
        'size' => 'normal',
        'description' => 'Height of peel.',
      ),
      'smallURL' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'URL of the image to show when the ad is not peeled.',
      ),
      'bigURL' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'URL of the image to show when the ad is peeled.',
      ),
      'mirror' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 1,
        'size' => 'tiny',
        'description' => 'Mirror the ad on the back of the peeled page. (1 = enabled, 0 = disabled).',
      ),
      'inTransition' => array(
        'type' => 'varchar',
        'length' => 15,
        'not null' => TRUE,
        'default' => 'none',
        'description' => 'In Transition when pageear is loaded.',
      ),
      'transitionDuration' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 4,
        'size' => 'tiny',
        'description' => 'Duration of in transition.',
      ),
      'peelColor' => array(
        'type' => 'varchar',
        'length' => 7,
        'not null' => TRUE,
        'default' => 'custom',
        'description' => 'If the mirror effect is disabled, this color will be used on the back.',
      ),
      'redValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Red value of Custom Color for the Peel (0 - 255).',
      ),
      'greenValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Green value of Custom Color for the Peel (0 - 255).',
      ),
      'blueValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Blue value of Custom Color for the Peel (0 - 255).',
      ),
      'linkTarget' => array(
        'type' => 'varchar',
        'length' => 7,
        'not null' => TRUE,
        'default' => '_blank',
        'description' => 'Where to open the URL.',
      ),
      'link' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => 'http://www.drupal.org/',
        'description' => 'URL to go when user click on the ad.',
      ),
      'loadSoundURL' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'URL of mp3 file to play when the ad is loaded.',
      ),
      'openSoundURL' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'URL of mp3 file to play when the peel is opened.',
      ),
      'closeSoundURL' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'URL of mp3 file to play when the peel is closed.',
      ),
      'flagSpeed' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 4,
        'size' => 'tiny',
        'description' => 'Speed for the motion in the unpeeled state.',
      ),
      'peelSpeed' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 4,
        'size' => 'tiny',
        'description' => 'Speed for the motion in the peeled state.',
      ),
      'automaticOpen' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'size' => 'normal',
        'description' => 'Millisecons to unpeel automatically after the pageear loads.',
      ),
      'automaticClose' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'size' => 'normal',
        'description' => 'Millisecons to automatically close after unpeeling.',
      ),
      'close_button_enable' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
        'size' => 'tiny',
        'description' => 'Show a close button on open peel.',
      ),
      'text_on_close_button' => array(
        'type' => 'varchar',
        'length' => 20,
        'not null' => TRUE,
        'default' => 'close',
        'description' => 'Text on clickable close button.',
      ),
      'close_redValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Color red value of close button (0 - 255).',
      ),
      'close_greenValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Color green value of close button (0 - 255).',
      ),
      'close_blueValue' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 255,
        'size' => 'tiny',
        'description' => 'Color blue value of close button (0 - 255).',
      ),
      'languages' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The language visibility of the pageear. Comma separated string of language names.',
      ),
      'roles' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'The role visibility of the pageear. Comma separated string of role ids (rid).',
      ),
      'visibility' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
        'size' => 'tiny',
        'description' => 'Flag to indicate how to show pageears on pages. (0 = Show on all pages except listed pages, 1 = Show only on listed pages, 2 = Use custom PHP code to determine visibility)',
      ),
      'pages' => array(
        'type' => 'text',
        'not null' => TRUE,
        'description' => 'Contents of the "Pages" textarea; contains either a list of paths on which to include/exclude the pageear or PHP code, depending on "visibility" setting.',
      ),
    ),
    'primary key' => array('peid'),
    'indexes' => array(
      'list' => array('status'),
    ),
  );

  return $schema;
}

This is the code to insert a row that is not working on hook_install and that is working on a form submit function:

  $new_pageear->status = 0;
  $new_pageear->name = 'drupal';
  $new_pageear->peelPosition = 'topright';
  $new_pageear->smallURL = drupal_get_path('module', 'pageear') .'/pageturn/small.jpg';
  $new_pageear->bigURL = drupal_get_path('module', 'pageear') .'/pageturn/big.jpg';
  $new_pageear->mirror = 1;
  $new_pageear->inTransition = 'Squeeze';
  $new_pageear->transitionDuration = 4;
  $new_pageear->peelColor = 'custom';
  $new_pageear->redValue = 255;
  $new_pageear->greenValue = 255;
  $new_pageear->blueValue = 255;
  $new_pageear->linkTarget = '_blank';
  $new_pageear->link = 'http://www.drupal.org/';
  $new_pageear->loadSoundURL = '';
  $new_pageear->openSoundURL = '';
  $new_pageear->closeSoundURL = '';
  $new_pageear->flagSpeed = 4;
  $new_pageear->peelSpeed = 4;
  $new_pageear->automaticOpen = 0;
  $new_pageear->automaticClose = 0;
  $new_pageear->close_button_enable = 0;
  $new_pageear->text_on_close_button = 'close';
  $new_pageear->close_redValue = 255;
  $new_pageear->close_greenValue = 255;
  $new_pageear->close_blueValue = 255;
  $new_pageear->languages = '';
  $new_pageear->roles = '';
  $new_pageear->visibility = 0;
  $new_pageear->pages = '';
  $new_pageear->flagWidth = 100;
  $new_pageear->flagHeight = 100;
  $new_pageear->peelWidth = 500;
  $new_pageear->peelHeight = 500;

  drupal_write_record('pageears', $new_pageear);

On submit function the values are going to come from the form but I used this same code for testing and works fine on submit. On hook_install is where the code is not working. The function drupal_write_record is returning FALSE and of course the database is not receiving the row.

Hook_install is exactly this one:

function pageear_install() {

  $result = drupal_install_schema('pageear');
  // TODO: is this the correct way to check if install is successful?
  if ($result[0]['success']) {
    // Insert an example pageear
    $new_pageear->status = 0;
    $new_pageear->name = 'drupal';
    $new_pageear->peelPosition = 'topright';
    $new_pageear->smallURL = drupal_get_path('module', 'pageear') .'/pageturn/small.jpg';
    $new_pageear->bigURL = drupal_get_path('module', 'pageear') .'/pageturn/big.jpg';
    $new_pageear->mirror = 1;
    $new_pageear->inTransition = 'Squeeze';
    $new_pageear->transitionDuration = 4;
    $new_pageear->peelColor = 'custom';
    $new_pageear->redValue = 255;
    $new_pageear->greenValue = 255;
    $new_pageear->blueValue = 255;
    $new_pageear->linkTarget = '_blank';
    $new_pageear->link = 'http://www.drupal.org/';
    $new_pageear->loadSoundURL = '';
    $new_pageear->openSoundURL = '';
    $new_pageear->closeSoundURL = '';
    $new_pageear->flagSpeed = 4;
    $new_pageear->peelSpeed = 4;
    $new_pageear->automaticOpen = 0;
    $new_pageear->automaticClose = 0;
    $new_pageear->close_button_enable = 0;
    $new_pageear->text_on_close_button = 'close';
    $new_pageear->close_redValue = 255;
    $new_pageear->close_greenValue = 255;
    $new_pageear->close_blueValue = 255;
    $new_pageear->languages = '';
    $new_pageear->roles = '';
    $new_pageear->visibility = 0;
    $new_pageear->pages = '';
    $new_pageear->flagWidth = 100;
    $new_pageear->flagHeight = 100;
    $new_pageear->peelWidth = 500;
    $new_pageear->peelHeight = 500;

    drupal_write_record('pageears', $new_pageear);
    
  }
}

I'm not able to see any error. But drupal_write_record returns FALSE inside this hook_install. (When I saw it was not including the row I added some code to see that return value of drupal_write_record, that's why I say it is returning FALSE. I don't know which is the problem because the function that's not return some kind of error code, just FALSE to indicate something went wrong).

If I get that code to work I can even reduce it a lot because almost all values are going to be the default ones for the schema and it is not needed to define them in new_pageear object (if I understood drupal_write_record documentation).

manfer’s picture

I tested and it looks like this code on drupal_write_record:

  $schema = drupal_get_schema($table);
  if (empty($schema)) {
    return FALSE;
  }

is the code that returns FALSE. The drupal_get_schema function seems to be returning an empty schema (probably FALSE) if it is called just after drupal_install_schema inside hook_install. Why? am I doing something wrong?

manfer’s picture

This is the function that returns FALSE:

function drupal_get_schema($table = NULL, $rebuild = FALSE) {
  static $schema = array();

  if (empty($schema) || $rebuild) {
    // Try to load the schema from cache.
    if (!$rebuild && $cached = cache_get('schema')) {
      $schema = $cached->data;
    }
    // Otherwise, rebuild the schema cache.
    else {
      $schema = array();
      // Load the .install files to get hook_schema.
      module_load_all_includes('install');

      // Invoke hook_schema for all modules.
      foreach (module_implements('schema') as $module) {
        $current = module_invoke($module, 'schema');
        _drupal_initialize_schema($module, $current);
        $schema = array_merge($schema, $current);
      }

      drupal_alter('schema', $schema);
      cache_set('schema', $schema);
    }
  }

  if (!isset($table)) {
    return $schema;
  }
  elseif (isset($schema[$table])) {
    return $schema[$table];
  }
  else {
    return FALSE;
  }
}

I'm not sure how this function works. I can't know if that static variable has a value or not when the function is called. Anyway there are three options.

First. That static variable has a value so it is not empty. As the call to that drupal_get_schema function inside drupal_write_record only has one argument (the table name), the first "if" statement in this drupal_get_schema function wont be executed. My module schema is not on that static schema variable and then the function returns false.

Second. That static variable is empty. Then the first if statement in drupal_get_schema is executed and the schema is probably retrieved from cache. My module schema is not in this cached schema and the function returns false.

Third. The static schema variable is empty, schema is not cached, then schema is rebuild inside drupal_get_schema. But even in this case the rebuild code is based on enabled modules (module_load_all_includes('install')) and the module is still not enabled on hook_install. Then the rebuilded schema would not include my module. The function would return false too.

So, it seems calling drupal_get_schema inside hook_install function returns FALSE. I don't know which other functions rebuilds the cached schema and in which moment my module schema is added to that cached schema (but it seems it is not included with the call to drupal_install_schema). When drupal_write_record is called inside a form_submit function it is working fine so in that moment my module schema should be in that cached schema or in that static schema variable or it must be rebuilded and included in schema as the module is enabled.

I'm not sure what is exactly happening but all indicates I can't use drupal_write_record inside that hook_install. Can someone clarify this?

I was going to change my insertions and updates to drupal_write_record. But if it is not working here probably I don't modify the code and continue using db_query.

apaderno’s picture

The problem is that the function uses hook_schema() to get the schema definition; as the module is being installed, Drupal doesn't find the implementation of hook_schema() made from the module (and it should not, as the module is till being installed, and it is not full-operative).

IMO, that makes sense. Maybe that should be said in the documentation, or it could be reported in a comment made in the function documentation.

manfer’s picture

So to add a sample row to the table on hook_install I have to use any other function. By now I'm using a db_query.

Maybe there is another solution I don't know. Is there another hook I could use to add a sample row to the table just after installation or I forget about it and cotinue using db_query for that purpose?

I cant do it on hook_enable because I want the sample row only added on installation, it cant be added if the module is disabled and reenabled again.

apaderno’s picture

Title: Documentation problem with drupal_write_record » Documentation problem with drupal_write_record()
Status: Active » Fixed

I think that using db_query() is the only solution.
I added a comment in the documentation page for the function.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

pwolanin’s picture

Version: 6.x-dev » 7.x-dev
Component: documentation » database system
Category: support » bug
Status: Closed (fixed) » Active

I just ran into the same problem and this seems to be a serious core bug to me - essentially a failure of the install code to clear the schema caches.

pwolanin’s picture

Title: Documentation problem with drupal_write_record() » drupal_write_record() fails on hook_install
David_Rothstein’s picture

Status: Active » Closed (duplicate)

See #200931: Schema not available in hook_install/hook_enable, which spun off #620298: Schema not available in hook_install() as a followup issue.

As described there, workarounds exist for doing anything in hook_enable() that you actually wanted to do in hook_install(), but I agree this is a bug which should be fixed.