Bug #69327 mysqldump dumps view with illegal mix of collations
Submitted: 27 May 2013 13:40 Modified: 28 Jun 2013 14:15
Reporter: Benjamin Behringer Email Updates:
Status: No Feedback Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:5.5.31-0ubuntu0.12.04.1 OS:Linux (Ubuntu 12.04 64bit)
Assigned to: CPU Architecture:Any
Tags: collation mysqldump view

[27 May 2013 13:40] Benjamin Behringer
Description:
What did I do:
I have this View in the Database:

CREATE VIEW ldap_memberof
AS
	SELECT userdb_person_group_maps.person_id AS person_id, 
		  CONCAT('cn=', userdb_entities.dn, ',ou=groups,dc=example,dc=com') AS group_dn
	  FROM userdb_person_group_maps
		   INNER JOIN userdb_groups ON userdb_person_group_maps.group_id=userdb_groups.entity_id
		   INNER JOIN userdb_entities ON userdb_groups.entity_id=userdb_entities.id
	 WHERE userdb_groups.is_posix_group=TRUE
UNION
	SELECT userdb_effective_person_permissions.person_id AS person_id, 
		   CONCAT('cn=', userdb_effective_person_permissions.permission_name, ',cn=', userdb_account_types.name, ',ou=permissions,dc=example,dc=com') AS group_dn
	  FROM userdb_effective_person_permissions
		   INNER JOIN userdb_account_types ON userdb_effective_person_permissions.permission_type=userdb_account_types.id;

I dumped my Database with

"mysqldump --routines --events --opts > db.sql"

then recreated the Database:

mysql> DROP DATABASE utest;
mysql> CREATE DATABASE utest

mysql utest < db.sql

And finally I tried to accress the view:

mysql> SHOW CREATE VIEW ldap_memberof;

What did I expect:

I expected to see the view definition.

What happened:

I got this error message:

mysql> SHOW CREATE VIEW ldap_memberof;
ERROR 1271 (HY000): Illegal mix of collations for operation 'UNION'

How to repeat:
Use a Ubunut 12.04 Server 64bit (only tested this Distribution), and install mysql-server-5.5.

Then create the Database:

mysql> CREATE DATABASE utest

and add these tables and views:

CREATE TABLE userdb_entities (
	id INTEGER NOT NULL AUTO_INCREMENT,
	dn VARCHAR(100) NOT NULL UNIQUE,
	type ENUM('person', 'group') NOT NULL,
	created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (id)
) Engine=InnoDB, CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_persons (
	entity_id INTEGER NOT NULL,
	forename1 VARCHAR(45) NOT NULL,
	forename2 VARCHAR(45),
	surname VARCHAR(45) NOT NULL,
	birthdate DATE,
	ssha BINARY(38),
	uid INTEGER,
	gid INTEGER,
	login_shell VARCHAR(45),
	home_directory VARCHAR(45),
	PRIMARY KEY (entity_id),
	FOREIGN KEY (entity_id) 
	 REFERENCES userdb_entities(id) 
	 ON DELETE CASCADE ON UPDATE CASCADE
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_groups (
	entity_id INTEGER NOT NULL,
	description VARCHAR(100),
	is_posix_group BOOLEAN NOT NULL DEFAULT FALSE,
	gid INTEGER,
	PRIMARY KEY (entity_id),
	FOREIGN KEY (entity_id) 
	 REFERENCES userdb_entities(id) 
	 ON DELETE CASCADE ON UPDATE CASCADE 
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_person_group_maps (
	person_id INTEGER NOT NULL,
	group_id INTEGER NOT NULL,
	PRIMARY KEY (person_id, group_id),
	FOREIGN KEY (person_id) 
	 REFERENCES userdb_persons(entity_id) 
	 ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (group_id) 
	 REFERENCES userdb_groups(entity_id) 
	 ON DELETE CASCADE ON UPDATE CASCADE
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_account_types (
	id INTEGER NOT NULL AUTO_INCREMENT,
	name VARCHAR(45) NOT NULL UNIQUE,
	description VARCHAR(100),
	PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_accounts (
	person_id INTEGER NOT NULL,
	type_id INTEGER NOT NULL,
	created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
	expiration_date DATE DEFAULT 0,
	active BOOLEAN DEFAULT TRUE NOT NULL,
	PRIMARY KEY (person_id, type_id),
	FOREIGN KEY (person_id)
	 REFERENCES userdb_persons (entity_id) 
	 ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (type_id)
	 REFERENCES userdb_account_types (id) 
	 ON DELETE CASCADE ON UPDATE CASCADE
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_permissions (
	id INTEGER NOT NULL AUTO_INCREMENT,
	type_id INTEGER NOT NULL,
	name VARCHAR(20) NOT NULL UNIQUE,
	description VARCHAR(100),
	PRIMARY KEY (id),
	FOREIGN KEY (type_id)
	 REFERENCES userdb_account_types (id) 
	 ON DELETE CASCADE ON UPDATE CASCADE
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE userdb_permission_entity_maps (
	permission_id INTEGER NOT NULL,
	entity_id INTEGER NOT NULL,
	PRIMARY KEY (permission_id, entity_id),
	FOREIGN KEY (permission_id) 
	 REFERENCES userdb_permissions(id) 
	 ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (entity_id) 
	 REFERENCES userdb_entities(id) 
	 ON DELETE CASCADE ON UPDATE CASCADE
) Engine=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE VIEW userdb_effective_person_permissions
AS
	SELECT userdb_entities.id AS person_id,
		   userdb_entities.dn AS person_dn, 
		   userdb_permissions.id AS permission_id, 
		   userdb_permissions.name AS permission_name,
		   userdb_permissions.type_id AS permission_type
	  FROM userdb_entities
	       INNER JOIN userdb_permission_entity_maps ON userdb_entities.id=userdb_permission_entity_maps.entity_id
	       INNER JOIN userdb_permissions ON userdb_permission_entity_maps.permission_id=userdb_permissions.id
	       INNER JOIN userdb_persons ON userdb_entities.id=userdb_persons.entity_id
	       INNER JOIN userdb_accounts ON userdb_persons.entity_id=userdb_accounts.person_id
	 WHERE userdb_entities.type='person'
	   AND userdb_permissions.type_id=userdb_accounts.type_id
	   AND userdb_accounts.active=TRUE
	   AND (userdb_accounts.expiration_date>NOW() OR userdb_accounts.expiration_date=0)
UNION
	SELECT person_entities.id AS person_id,
		   person_entities.dn AS person_dn, 
		   userdb_permissions.id AS permission_id, 
		   userdb_permissions.name AS permission_name,
		   userdb_permissions.type_id AS permission_type
	  FROM userdb_entities AS person_entities
	  	   INNER JOIN userdb_persons ON person_entities.id=userdb_persons.entity_id
	  	   INNER JOIN userdb_person_group_maps ON userdb_persons.entity_id=userdb_person_group_maps.person_id
	  	   INNER JOIN userdb_groups ON userdb_person_group_maps.group_id=userdb_groups.entity_id
	  	   INNER JOIN userdb_entities AS group_entities ON userdb_groups.entity_id=group_entities.id
	  	   INNER JOIN userdb_permission_entity_maps ON group_entities.id=userdb_permission_entity_maps.entity_id
		   INNER JOIN userdb_permissions ON userdb_permission_entity_maps.permission_id=userdb_permissions.id
		   INNER JOIN userdb_accounts ON userdb_persons.entity_id=userdb_accounts.person_id
	 WHERE userdb_permissions.type_id=userdb_accounts.type_id
	   AND userdb_accounts.active=TRUE
	   AND (userdb_accounts.expiration_date>NOW() OR userdb_accounts.expiration_date=0);

CREATE VIEW ldap_memberof
AS
	SELECT userdb_person_group_maps.person_id AS person_id, 
		  CONCAT('cn=', userdb_entities.dn, ',ou=groups,dc=example,dc=com') AS group_dn
	  FROM userdb_person_group_maps
		   INNER JOIN userdb_groups ON userdb_person_group_maps.group_id=userdb_groups.entity_id
		   INNER JOIN userdb_entities ON userdb_groups.entity_id=userdb_entities.id
	 WHERE userdb_groups.is_posix_group=TRUE
UNION
	SELECT userdb_effective_person_permissions.person_id AS person_id, 
		   CONCAT('cn=', userdb_effective_person_permissions.permission_name, ',cn=', userdb_account_types.name, ',ou=permissions,dc=example,dc=com') AS group_dn
	  FROM userdb_effective_person_permissions
		   INNER JOIN userdb_account_types ON userdb_effective_person_permissions.permission_type=userdb_account_types.id;

Then dump it all, recreate the database and replay the dump:

"mysqldump --routines --events --opts > db.sql"

mysql> DROP DATABASE utest;
mysql> CREATE DATABASE utest

mysql utest < db.sql

and try to access the view "ldap_memberof":

mysql> SHOW CREATE VIEW ldap_memberof;

Suggested fix:
I found two kinds of workarounds:

1. Rewrite the view, so that it doesn't reference another view:

CREATE VIEW ldap_memberof
AS
	SELECT userdb_person_group_maps.person_id AS person_id, 
		   CONCAT('cn=', userdb_entities.dn, ',ou=groups,dc=example,dc=com') AS group_dn
	  FROM userdb_person_group_maps
		   INNER JOIN userdb_groups ON userdb_person_group_maps.group_id=userdb_groups.entity_id
		   INNER JOIN userdb_entities ON userdb_groups.entity_id=userdb_entities.id
	 WHERE userdb_groups.is_posix_group=TRUE
UNION
	SELECT userdb_entities.id AS person_id,
		   CONCAT('cn=', userdb_permissions.name, ',cn=', userdb_account_types.name, ',ou=permissions,dc=example,dc=com') AS group_dn
	  FROM userdb_entities
	       INNER JOIN userdb_permission_entity_maps ON userdb_entities.id=userdb_permission_entity_maps.entity_id
	       INNER JOIN userdb_permissions ON userdb_permission_entity_maps.permission_id=userdb_permissions.id
	       INNER JOIN userdb_persons ON userdb_entities.id=userdb_persons.entity_id
	       INNER JOIN userdb_accounts ON userdb_persons.entity_id=userdb_accounts.person_id
	       INNER JOIN userdb_account_types ON userdb_accounts.type_id=userdb_account_types.id
	 WHERE userdb_entities.type='person'
	   AND userdb_permissions.type_id=userdb_accounts.type_id
	   AND userdb_accounts.active=TRUE
	   AND (userdb_accounts.expiration_date>NOW() OR userdb_accounts.expiration_date=0)
UNION
	SELECT person_entities.id AS person_id,
		   CONCAT('cn=', userdb_permissions.name, ',cn=', userdb_account_types.name, ',ou=permissions,dc=example,dc=com') AS group_dn
	  FROM userdb_entities AS person_entities
	  	   INNER JOIN userdb_persons ON person_entities.id=userdb_persons.entity_id
	  	   INNER JOIN userdb_person_group_maps ON userdb_persons.entity_id=userdb_person_group_maps.person_id
	  	   INNER JOIN userdb_groups ON userdb_person_group_maps.group_id=userdb_groups.entity_id
	  	   INNER JOIN userdb_entities AS group_entities ON userdb_groups.entity_id=group_entities.id
	  	   INNER JOIN userdb_permission_entity_maps ON group_entities.id=userdb_permission_entity_maps.entity_id
		   INNER JOIN userdb_permissions ON userdb_permission_entity_maps.permission_id=userdb_permissions.id
		   INNER JOIN userdb_accounts ON userdb_persons.entity_id=userdb_accounts.person_id
		   INNER JOIN userdb_account_types ON userdb_accounts.type_id=userdb_account_types.id
	 WHERE userdb_permissions.type_id=userdb_accounts.type_id
	   AND userdb_accounts.active=TRUE
	   AND (userdb_accounts.expiration_date>NOW() OR userdb_accounts.expiration_date=0);

2. Change collation on all underlying tables to utf8_general_ci:

ALTER TABLE userdb_account_types CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_accounts CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_entities CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_groups CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_permission_entity_maps CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_permissions CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_person_group_maps CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE userdb_persons CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
[28 May 2013 14:15] MySQL Verification Team
Thank you for the bug report. Please check for duplicate/related with http://bugs.mysql.com/bug.php?id=60854 . Read the last comment from the reporter how to workaround the issue. Thanks.
[29 Jun 2013 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".