Someone, attempting to leaving a comment and being asked register, probably with spurious credentials and evil intent generates this notice:

Notice: unserialize(): Error at offset 0 of 4 bytes in UserController->attachLoad() (line 268 of modules/user/user.module).

when I log in and access the (unapproved) account:

/user/1#overlay=user/3/edit%3Fdestination%3Dadmin/people

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EvanDonovan’s picture

Title: A New user registration generates error. » "Notice: unserialize() ...in UserController->attachLoad()" when editing unapproved user account via overlay

This is a PHP notice, so I don't think that it is caused by the person who registered. Notices are one of the lowest levels of messages that PHP can throw, so this probably just wasn't caught by the developers of Drupal core yet.

I have made the title of this issue contain the actual error, so that it will be more likely to be reviewed by someone who can figure it out.

mnicholas’s picture

You're right, it doesn't look like user input. I registered using simple names and the error is present too.

WILD GUESS: Maybe it's a SQLite problem - someone will have spotted this before otherwise.

This also pops up:
# Notice: unserialize(): Error at offset 0 of 4 bytes in _drupal_session_read() (line 108 of /home/sp/drupal/drupal7/includes/session.inc).

scotwith1t’s picture

I'm getting this just viewing a user page, whether logged in or not.

Error messageNotice: unserialize(): Error at offset 5 of 22 bytes in UserController->attachLoad() (line 268 of /{path_to}/user.module).

not using SQLite, so i think that may rule that out.

mnicholas’s picture

Version: 7.0-beta2 » 7.0-beta3

Still pesent in beta3.

EvanDonovan’s picture

Version: 7.0-beta3 » 7.x-dev

Can someone reproduce this with the devel module's backtrace error handler on? (or else XDebug?) That way, we can see what is leading up to the error.

EvanDonovan’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when editing unapproved user account via overlay » "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page

Retitling, since it occurs under more general conditions.

mnicholas’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default » "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page
Component: sqlite database » user.module
Priority: Major » Normal
Issue tags: -D7 upgrade path

This error arises from the users.data field being initialised to the string 'NULL'.

The users table in my test SQLite DB looks a bit like this:

INSERT INTO "users" VALUES(0,'','','','','',0,0,0,0,NULL,'',0,'',NULL,NULL);
INSERT INTO "users" VALUES(1,'Martin','REDACTED','REDACTED','','',0,0,0,1,NULL,'',0,'REDACTED',NULL,NULL);
INSERT INTO "users" VALUES(5,'REDACTED','REDACTED','','',0,0,0,1,'Europe/London','',0,'REDACTED','NULL','2');

This arises from this:

CREATE TABLE "users" (
uid INTEGER NOT NULL DEFAULT '0', name VARCHAR(60) NOT NULL DEFAULT '', pass VARCHAR(128) NOT NULL DEFAULT '', 
mail VARCHAR(254) NULL DEFAULT '', theme VARCHAR(255) NOT NULL DEFAULT '', signature VARCHAR(255) NOT NULL DEFAULT '', 
created INTEGER NOT NULL DEFAULT '0', access INTEGER NOT NULL DEFAULT '0', login INTEGER NOT NULL DEFAULT '0', 
status INTEGER NOT NULL DEFAULT '0', timezone VARCHAR(32) NULL DEFAULT 'NULL', language VARCHAR(12) NOT NULL DEFAULT '', 
picture INTEGER NOT NULL DEFAULT '0', init VARCHAR(254) NULL DEFAULT '', 
data BLOB NULL DEFAULT 'NULL', 
signature_format VARCHAR(255) NULL DEFAULT NULL, PRIMARY KEY (uid));

There are numerous other examples of this sort of thing, for example: filter.settings, field_revision_comment_body.comment_body_value etc.
The above examples are taken from an SQL dump of the database.
This is really a database system or an install system issue now; I'll leave it to a wiser head to decide which.

mnicholas’s picture

Comment withdrawn.

EvanDonovan’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page » "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default
Version: 7.x-dev » 7.0-rc1
Component: user.module » install system
Priority: Normal » Major
Issue tags: +D7 upgrade path

Reassigning and escalating, since I think #7 implies a schema update will be necessary.

bfroehle’s picture

MySQL's createFieldSql

    // $spec['default'] can be NULL, so we explicitly check for the key here.
    if (array_key_exists('default', $spec)) {
      if (is_string($spec['default'])) {
        $spec['default'] = "'" . $spec['default'] . "'";
      }
      elseif (!isset($spec['default'])) {
        $spec['default'] = 'NULL';
      }
      $sql .= ' DEFAULT ' . $spec['default'];
    }

vs. SQLite's createFieldSql

      if (isset($spec['default'])) {
        if (is_string($spec['default'])) {
          $spec['default'] = "'" . $spec['default'] . "'";
        }
        $sql .= ' DEFAULT ' . $spec['default'];
      }

vs. pgsql

    if (isset($spec['default'])) {
      $default = is_string($spec['default']) ? "'" . $spec['default'] . "'" : $spec['default'];
      $sql .= " default $default";
    }
catch’s picture

Component: install system » sqlite database

Moving to sqlite queue.

catch’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page » "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default
Version: 7.0-rc1 » 7.x-dev
Component: user.module » sqlite database
Priority: Normal » Major
Issue tags: +D7 upgrade path

Please leave the version at 7.x-dev, otherwise it doesn't show up in the right lists.

Stevel’s picture

Title: Altering table messes up default values caused by a bug in introspectSchema() » "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default

INSERT INTO "users" VALUES(5,'REDACTED','REDACTED','','',0,0,0,1,'Europe/London','',0,'REDACTED','NULL','2');

The last NULL value (for the data column) is actually a string 'NULL' instead of the constant NULL, which I think is the cause of the error. Now the real problem is 'NULL' being inserted as a string of course...

edit: The default for the data column is the string 'NULL' as well, which I think is caused by an error in introspectSchema():

$schema['fields'][$row->name] = array(
 	  	  	'type' => $type,
 	  	  	'size' => $size,
 	  	  	'not null' => !empty($row->notnull),
 	  	  	'default' => trim($row->dflt_value, "'"),
 	  	  	); 

This sets $schema['fields'][$row->name]['default'] to 'NULL' (string) instead of NULL (constant) in case of the {users}.data field.

Stevel’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default » Altering table messes up default values caused by a bug in introspectSchema()

Changing the title to reflect the underlying bug.

The same error occurs for the default of the INTEGER columns being set to '0' (string) instead of 0 (int), which is not so much a problem because of re-casting on insert.

Damien Tournoud’s picture

Title: "Notice: unserialize() ...in UserController->attachLoad()" when viewing or editing user page due to bad table default » Altering table messes up default values caused by a bug in introspectSchema()
Priority: Major » Critical

Ew. I see.

Raising to critical because we might need a schema update for this :(

Stevel’s picture

Small debug session:

sqlite> CREATE TABLE "test" (
   ...> field_null VARCHAR(10) NULL DEFAULT NULL,
   ...> field_null_string VARCHAR(10) NULL DEFAULT 'NULL',
   ...> field_int INTEGER NOT NULL DEFAULT 0,
   ...> field_int_string INTEGER NOT NULL DEFAULT '0',
   ...> field_text TEXT NULL DEFAULT NULL,
   ...> field_text_string TEXT NULL DEFAULT 'NULL',
   ...> field_float FLOAT NOT NULL DEFAULT 0.45,
   ...> field_float_string FLOAT NOT NULL DEFAULT '0.45');
sqlite> PRAGMA table_info('test');
0|field_null|VARCHAR(10)|0|NULL|0
1|field_null_string|VARCHAR(10)|0|'NULL'|0
2|field_int|INTEGER|1|0|0
3|field_int_string|INTEGER|1|'0'|0
4|field_text|TEXT|0|NULL|0
5|field_text_string|TEXT|0|'NULL'|0
6|field_float|FLOAT|1|0.45|0
7|field_float_string|FLOAT|1|'0.45'|0

This means that when the default value is defined as a string, it is returned enclosed in single quotes. When it is NULL, an integer, a float... it is returned without quotes.

edit: does this mean we need to alter each and every field that has been altered in a previous update function again after the introspection is fixed? Or maybe only those after beta 1 is released, since sqlite wasn't included in D6?

Damien Tournoud’s picture

Yes, the real issue is just that 'default' => trim($row->dflt_value, "'") is too naive.

chx’s picture

FileSize
863 bytes

Yup. Write tests for this please. Note that trim is wrong, it rips out valid apostrophes, if the default starts or end with one.

Crell’s picture

Status: Active » Needs review
chx’s picture

note that even this might not be enough, i did not check that default value does not come out the string N-U-L-L instead of just being NULL. In the latter case we need some truly ugly else cause to my patch.

carlos8f’s picture

Note that trim is wrong, it rips out valid apostrophes, if the default starts or end with one.

+        if (isset($row->dflt_value[0]) && $row->dflt_value[0] == "'") {
+          $row->dflt_value = substr($row->dflt_value, 1, -1);
+        }

Isn't this the same as trim($row->dflt_value, "'")? I don't see what the patch accomplishes.

marketacumen’s picture

I'm new to the drupal community, but is there something like unquote in the core?

carlos8f's suggestion is OK, but will break a string like:

$row->dflt_value = "'oy there mate!";

How about an unquote function like:

/**
 * Unquote a string and optionally return the quote removed.
 *
 * @param string $s A string to unquote
 * @param string $quotes A list of quote pairs to unquote
 * @param string $left_quote Returns the quotes removed
 * @return Unquoted string, or same string if quotes not found
 */
function unquote($s, $quotes = "''\"\"", &$left_quote = null) {
	if (is_array($s)) {
		$result = array();
		foreach ($s as $k => $ss) {
			$result[$k] = unquote($ss, $quotes, $left_quote);
		}
		return $result;
	}
	if (strlen($s) < 2) {
		$left_quote = false;
		return $s;
	}
	$q = substr($s, 0, 1);
	$qleft = strpos($quotes, $q);
	if ($qleft === false) {
		$left_quote = false;
		return $s;
	}
	$qright = $quotes[$qleft + 1];
	if (substr($s, -1) === $qright) {
		$left_quote = $quotes[$qleft];
		return substr($s, 1, -1);
	}
	return $s;
}
carlos8f’s picture

I see now why trim() is wrong, it removes multiple instances of single quotes, whereas we only want to remove the outer instances.

How about:

if ($row->dflt_value == 'NULL') {
  $default = NULL;
}
elseif (is_string($row->dflt_value) && $row->dflt_value[0] == "'") {
  $default = substr($row->dflt_value, 1, -1);
}
else {
  $default = $row->dflt_value; // Cast to proper type?
}
Damien Tournoud’s picture

Are we really sure that we get a quoted string here? That sounds very very doubtful to me.

chx’s picture

carlos8f’s picture

Re: #24, yes we do get a quoted string.

And #25 looks correct because as we've found out, we should also be unescaping literal single quotes here, by un-doubling them.

So who wants to write the test? :P

Dries’s picture

+++ includes/database/sqlite/schema.inc	2010-12-10 23:28:35 +0000
@@ -380,11 +379,28 @@ class DatabaseSchema_sqlite extends Data
+        // The default is always a PHP string, if the default is a string in
+        // SQLite too then it arrives quotes, remove them.

This is not proper English.

+++ includes/database/sqlite/schema.inc	2010-12-10 23:28:35 +0000
@@ -380,11 +379,28 @@ class DatabaseSchema_sqlite extends Data
+          // Single quotes are escaped by storing them doubled-up. De-escape
+          // them to avoid the driver double escaping them.
+          $default = str_replace("''", "'", substr($default, 1, -1));

I don't know what is meant with 'doubled-up'. Do you mean they are escaped twice? It is not clear why there are 2 quotes.

carlos8f’s picture

"doubled up" means literally that -- SQLite requires you to escape single quotes by doubling them, i.e. 'Dries''s comment'

dmitrig01’s picture

dmitrig01’s picture

Ups, that was meant to be a comment saying "this patch fixes the issues Dries pointed out with the documentation"

EvanDonovan’s picture

+ // are discarted using substr.

Should be "discarded".

Leaving at "needs review" for review of actual code.

chx’s picture

Assigned: Unassigned » chx

Working on tests.

chx’s picture

Title: Altering table messes up default values caused by a bug in introspectSchema() » schema defaults are not quoted and just broken in sqlite
FileSize
6.38 KB

Here we go.

bleen’s picture

FileSize
6.38 KB

identical to #33 w/o the whitespace issue

mnicholas’s picture

I've been away from this for a few days - sorry about that.

It seems to me that the simplest way to produce a column default of NULL (not the string of the same name!) is to have no DEFAULT clause at all. From the SQLite documentation (http://www.sqlite.org/lang_createtable.html):

If there is no explicit DEFAULT clause attached to a column definition, then the default value of the column is NULL. An explicit DEFAULT clause may specify that the default value is NULL, ...

Cheers!

Damien Tournoud’s picture

+      if (!empty($field_spec['default'])) {
+        $this->assertEqual($field_value, $field_spec['default'], t('Default value registered.'));
+      }
+      else {
+        $this->assertIdentical($field_value, $field_spec['default'], t('Default value registered.'));
+      }

I guess this is intended to be a isset() instead of a !empty()?

chx’s picture

No, I wanted to avoid the usual 0-null-'' equivalence.

Crell’s picture

This seems to affect primarily SQLite and Postgres, so I will let chx and Damien make the final RTBC. However, reading through the patch in #34 it looks good to me. +1 when chx and Damien are happy with it.

chx’s picture

The remaining problem here: if someone installed a site with RC2 on SQLite they *might* have a broken site. We might need to go over the tables and fix defaults from quoted-null to just null. Not sure, needs testing. As for #37, consider setting the default to NULL or 0 and getting back something else, assertEqual will give you a pass. There might be better ways but this is good enough. You can't always do assertIdentical because we stringify fetches so setting a default of 7 and reading it back is '7'

Damien Tournoud’s picture

Status: Needs review » Reviewed & tested by the community

Sounds good to me.

chx’s picture

Status: Reviewed & tested by the community » Needs work

wth, no, this is not ready, what about the upgrade path I mentioned?

matt2000’s picture

Confirming the upgrade issue. The test fails both before and after the fix is applied to a 7.0-rc2 installation.

chx’s picture

Priority: Critical » Major
Status: Needs work » Needs review

While the usage of quote is more appropriate (and I am sure semicolons in defaults would present a huge problem), I am unable to reproduce the issue -- note that there are no clear reproduction instructions. I have tried beta 3 and RC2 both, created a user, visited the profile, no probs. Tried field_revision_comment_body (edited for brevity):

sqlite>  insert into field_revision_comment_body (etid,entity_id,revision_id,delta) values (1,1,1,1);
sqlite> select * from field_revision_comment_body;
1||0|1|1||1||
sqlite> .dump field_revision_comment_body
CREATE TABLE field_revision_comment_body (
....
comment_body_format VARCHAR(255) NULL DEFAULT NULL, 
...
INSERT INTO "field_revision_comment_body" VALUES(1,'',0,1,1,'',1,NULL,NULL);

not a 'NULL' in sight.

Now, I can prove that indeed 'NULL' was sent to SQLite but the above proves that SQLite somehow converts that to NULL (once again edited for brevity):

sqlite> .mode insert foo
sqlite> pragma table_info(field_revision_comment_body);
INSERT INTO foo VALUES(0,'etid','INTEGER',1,NULL,1);
...
INSERT INTO foo VALUES(7,'comment_body_value','TEXT',0,'NULL',0);
INSERT INTO foo VALUES(8,'comment_body_format','VARCHAR(255)',0,'NULL',0);

See how the 'NULL' became a NULL both in INSERT and CREATE TABLE? (Edit: i tested and the 'NULL' is not an artifact of the mode insert. It can use and display NULL just fine.)

chx’s picture

Issue tags: -D7 upgrade path

And there is nothing to upgrade.

bojanz’s picture

I tried to replicate the initial problem, but wasn't able to.

Installed the latest dev on sqlite, created a new user, created content (with body empty), browsed and clicked, didn't see anything.
It's very possible that I missed something, but I just wanted to report this...

Damien Tournoud’s picture

Component: sqlite database » database system
Garrett Albright’s picture

Priority: Major » Normal
Status: Needs review » Needs work

I gave this a look by checking out 7.x-beta2 and doing an install. I also cannot replicate the problem of quoted NULLs showing up in the schema, as in #43.

The patch in #34 no longer cleanly applies; modules/simpletest/tests/schema.test has changed enough that only one hunk works. I'm not sure where the tests it's trying to change have wandered off to; git grepping around didn't find them.

I think we still want to quote things the right way, so this patch is still in the running, but someone in the know about where the tests are nowadays needs to update that part of it. Reducing priority, however, as it appears that the problem is not as dire as it ever seemed.

caponey’s picture

I'm getting this error too, using Drupal 7.2

Notice: unserialize() [function.unserialize]: Error at offset 0 of 4 bytes in UserController->attachLoad() (line 287 of /var/www/docs/www.mysite.com/htdocs/modules/user/user.module).

I'm still setting up my site, and hadn't noticed it before until after I deleted some users. This error shows up on all user profile pages, except for the Admin's profile.

I can't patch; I have tried before, and fail every time. Will this be fixed in a future version of Drupal?

Damien Tournoud’s picture

Component: database system » sqlite database

Triaging.

chx’s picture

Component: sqlite database » ajax system
Assigned: chx » Unassigned
Issue summary: View changes
Stevel’s picture

Component: ajax system » database system
pwolanin’s picture

Version: 7.x-dev » 9.3.x-dev

Looks like this is still an issue in Drupal 9 in terms of not casting the default value:

'default' => trim($row->dflt_value, "'"),

The default does seem to be escaped by the driver, at least.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Status: Needs work » Closed (outdated)
Issue tags: +Bug Smash Initiative

This was a bugsmash triage target. lendude suggested that this was fixed now that everything is quoted. He also pointed out that this was reported not reproducible in #45. Also, the code around the line from #52 was modified in #3232699: SQLite schema introspection implementation struggles with NULL default values.

I think it is time to close this as outdated.

If you are experiencing this problem on a supported version of Drupal reopen the issue, by setting the status to 'Active', and provide complete steps to reproduce the issue (starting from "Install Drupal core").