I've seen it's a must to perform a db clean up for the module (at least d6 and d7). Primary key is set to 'timestamp' field, instead of id. Due to this failing when two events happend in the same timestamp (quite easy to happend if two users meets in time, or one script trying to..) I'll propose changing this to 'id' field and creating the timestamp index. Also, a complete db code review will be performed to migrate from db_query to more drupal friendly drupal_write_record. The update part should also be complete for the .install file.

Subtask of #397890: create a stable version for the 6.x branch of the login_security module

Comments

ilo’s picture

Status: Active » Needs review
StatusFileSize
new4.72 KB

I need someone with pgsql to test this patch. Just do what stated in the subject, updates database schema, and changes db_query('INSERT to drupal_write_record.

ilo’s picture

StatusFileSize
new5.39 KB

Previous patch didn't include the schema changes needed in the .install file for the drupal_write_record to find the schema.

deekayen’s picture

Status: Needs review » Fixed

I'll be easier for most people to review this during development if it's in the snapshot, so I committed it.

ilo’s picture

Thanks deekayen, I guess something went wrong because of the issue #492800: Incorrect table definition, not sure if this is fixed at all..

deekayen’s picture

Status: Fixed » Needs work

I moved the index creation around in the initial commit because the API documentation says the keys should all be dropped before calling db_change_field, then re-created. That probably means the index creation probably needs a little more work since they are not re-created during change_field or after.

#492800: Incorrect table definition was caused by me moving the index creation down to the bottom of the update function. When I moved the creation down to the bottom, there was then no key for it to drop, which caused the error. The error was generated because #2 creates a key id then turns around and drops it on the next line. Why?

This is more like what I expected (untested):

function login_security_update_6001() {
  $ret = array();
  db_drop_primary_key($ret, 'login_security_track');
  db_drop_index($ret, 'login_security_track', 'name');
  db_change_field($ret, 'login_security_track', 'name', 'name', array(
      'type' => 'varchar',
      'length' => 64,
      'not null' => TRUE,
      'default' => '',
      'description' => t("Username used in the login submission."),
    )
  );
  db_change_field($ret, 'login_security_track', 'timestamp', 'timestamp', array(
      'type' => 'int',
      'not null' => TRUE,
      'default' => 0,
      'description' => t("Timestamp of the event."),
    )
  );
  db_add_index($ret, 'login_security_track', 'name', array('name'));
  db_add_index($ret, 'login_security_track', 'timestamp', array('timestamp'));
  db_add_primary_key($ret, 'login_security_track', array('id'));
  return $ret;
}
ilo’s picture

The error was generated because #2 creates a key id then turns around and drops it on the next line. Why?

Not exactly, line 3 deletes the Index of the id field, not the key.. The problem here is just trying to alter the table (drop index id) with a serial field (id) and not primary key (just dropped in line 1).

Problem was: Previous database schema is wrong..
http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/login_secur...

    'indexes' => array(
      'id' => array('id'),
      'name' => array('name'),
      'host' => array('host')
    ),
    'primary key' => array('timestamp')

1) timestamp can't be primary key, two login attempts can happend at the same timestamp
2) id is indexed, timestamp is not indexed, but queries require tstamp index.

So, the tasks were:
- primary key should be remove from timestamp
- primary key should be created for ID
- index id is deleted
- index timestamp is created.

as shown in the final schema:

    'indexes' => array(
      'name' => array('name'),
      'host' => array('host'),
      'timestamp' => array('timestamp'),
    ),
    'primary key' => array('id')

The howto was:

  // Remove current primary key
  db_drop_primary_key($ret, 'login_security_track');
  // Mysql needs a primary key to perform operations in the table. Create the new one
  db_add_primary_key($ret, 'login_security_track', array('id'));
  // drop the Watch out, it drops the current id INDEX, not the key
  db_drop_index($ret, 'login_security_track', 'id');
  // Create the new index for timestamp
  db_add_index($ret, 'login_security_track', 'timestamp', array('timestamp'));

We can just change the primary key and move the index part to the end as you did, or use the parameter $new_keys in the first alter.

http://api.drupal.org/api/function/db_change_field/6:
"On MySQL, all type 'serial' fields must be part of at least one key or index as soon as they are created. You cannot use db_add_{primary_key,unique_key,index}() for this purpose because the ALTER TABLE command will fail to add the column without a key or index specification. The solution is to use the optional $new_keys argument to create the key or index at the same time as field."

Your code should work (didn't test also, I'll give a try asap) just like this, leaving the db_add_primary_key at the begining..

function login_security_update_6001() {
  $ret = array();
  // step 1, change primary key in the table. 
  db_drop_primary_key($ret, 'login_security_track');
  // mysql needs a key if there's a  a serial field.
  db_add_primary_key($ret, 'login_security_track', array('id'));
  // step 2  remove index for name
  db_drop_index($ret, 'login_security_track', 'name');
  // Not sure if this is already dropped in all db engines because of the primary key drop, so I leave commented
  // db_drop_index($ret, 'login_security_track', 'id');
  db_change_field($ret, 'login_security_track', 'name', 'name', array(
      'type' => 'varchar',
      'length' => 64,
      'not null' => TRUE,
      'default' => '',
      'description' => t("Username used in the login submission."),
    )
  );
  db_change_field($ret, 'login_security_track', 'timestamp', 'timestamp', array(
      'type' => 'int',
      'not null' => TRUE,
      'default' => 0,
      'description' => t("Timestamp of the event."),
    )
  );
  db_add_index($ret, 'login_security_track', 'name', array('name'));
  db_add_index($ret, 'login_security_track', 'timestamp', array('timestamp'));
  return $ret;
}

Note that I just kept the primary key add before the alter. I guess, since we have a serial field, we should db_add_primary_key just inmediatly before the db_drop_primary_key.

ilo’s picture

Status: Needs work » Needs review
StatusFileSize
new1.35 KB

In fact, I've tested this:

- Uninstall module
- update login_security module to date 27-Feb-2009 (previous to db changes)
- Intall module
- Update login_security module to 6-1 branch
- Apply this patch
- perform update

And the result is:

The following queries were executed
login_security module
Update #6001

* ALTER TABLE {login_security_track} DROP PRIMARY KEY
* ALTER TABLE {login_security_track} ADD PRIMARY KEY (id)
* ALTER TABLE {login_security_track} DROP INDEX name
* ALTER TABLE {login_security_track} DROP INDEX id
* ALTER TABLE {login_security_track} CHANGE name `name` VARCHAR(64) NOT NULL DEFAULT ''
* ALTER TABLE {login_security_track} CHANGE timestamp `timestamp` INT NOT NULL DEFAULT 0
* ALTER TABLE {login_security_track} ADD INDEX name (name)
* ALTER TABLE {login_security_track} ADD INDEX timestamp (timestamp)

This is the update funtion

function login_security_update_6001() {
  $ret = array();
  // Change current primary key
  db_drop_primary_key($ret, 'login_security_track');
  db_add_primary_key($ret, 'login_security_track', array('id'));
  // Drop indexes
  db_drop_index($ret, 'login_security_track', 'name');
  db_drop_index($ret, 'login_security_track', 'id');
  db_change_field($ret, 'login_security_track', 'name', 'name', array(
      'type' => 'varchar',
      'length' => 64,
      'not null' => TRUE,
      'default' => '',
      'description' => t("Username used in the login submission."),
    )
  );
  db_change_field($ret, 'login_security_track', 'timestamp', 'timestamp', array(
      'type' => 'int',
      'not null' => TRUE,
      'default' => 0,
      'description' => t("Timestamp of the event."),
    )
  );
  // Re-create indexes
  db_add_index($ret, 'login_security_track', 'name', array('name'));
  db_add_index($ret, 'login_security_track', 'timestamp', array('timestamp'));
  return $ret;
}
deekayen’s picture

Status: Needs review » Reviewed & tested by the community

FYI, I've been committing to both HEAD and DRUPAL-6--1.

ilo’s picture

good!. Thanks for your help and support in the module's queue, deekayen.

deekayen’s picture

Status: Reviewed & tested by the community » Fixed

committed

Status: Fixed » Closed (fixed)

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

  • deekayen committed 7b919bf on 6.x-1.x, 8.x-1.x
    #399390 - database schema cleanup by ilo
    
    
  • deekayen committed e7dd303 on 6.x-1.x, 8.x-1.x
    #399390 update function wrapup of bugfixes to index modification