Friday Sep 18, 2009

Recently I got this question twice. Although SIGNAL was implemented in version 6.0 (which is partially mysql-trunk now) this version is not stable yet, so users still need to use workaround.

Here it is. Create 2 procedures as following:

DROP PROCEDURE IF EXISTS raise_application_error;
DROP PROCEDURE IF EXISTS get_last_custom_error;
DROP TABLE IF EXISTS RAISE_ERROR;

DELIMITER $$
CREATE PROCEDURE raise_application_error(IN CODE INTEGER, IN MESSAGE VARCHAR(255)) SQL SECURITY INVOKER DETERMINISTIC
BEGIN
  CREATE TEMPORARY TABLE IF NOT EXISTS RAISE_ERROR(F1 INT NOT NULL);

  SELECT CODE, MESSAGE INTO @error_code, @error_message;
  INSERT INTO RAISE_ERROR VALUES(NULL);
END;
$$

CREATE PROCEDURE get_last_custom_error() SQL SECURITY INVOKER DETERMINISTIC
BEGIN
  SELECT @error_code, @error_message;
END;
$$
DELIMITER ;

You can use them as:

CALL raise_application_error(1234, 'Custom message');
CALL get_last_custom_error();


Example: table which stores only odd numbers.

DROP TABLE IF EXISTS ex1;
DROP TRIGGER IF EXISTS ex1_bi;
DROP TRIGGER IF EXISTS ex1_bu;

CREATE TABLE ex1(only_odd_numbers INT UNSIGNED);

DELIMITER $$

CREATE TRIGGER ex1_bi BEFORE INSERT ON ex1 FOR EACH ROW
BEGIN
  IF NEW.only_odd_numbers%2 != 0 THEN
    CALL raise_application_error(3001, 'Not odd number!');
  END IF;
END
$$

CREATE TRIGGER ex1_bu BEFORE UPDATE ON ex1 FOR EACH ROW
BEGIN
  IF NEW.only_odd_numbers%2 != 0 THEN
    CALL raise_application_error(3001, 'Not odd number!');
  END IF;
END
$$

DELIMITER ;



Usage:


mysql> INSERT INTO ex1 VALUES(2);
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO ex1 VALUES(3);
ERROR 1048 (23000): Column 'F1' cannot be null
mysql> CALL get_last_custom_error();
+-------------+-----------------+
| @error_code | @error_message  |
+-------------+-----------------+
| 3001        | Not odd number! |
+-------------+-----------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM ex1;
+------------------+
| only_odd_numbers |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

Tuesday Jun 02, 2009


Reporter of bug #44604 pointed to common problem: "Why don't backport bugfix to previous major version?" Especially if this version is GA.


This particular bug seems to be fixed by redesign of MySQL Optimizer which was dramatically improved in version 6.0


At the same time while some feature is improved in new version backport this improvement or even part of it into older one is not easy and can lead to instability or affect other applications. There is always balance between risk and effort to fix.


Same reasons apply for other bugs in similar state.


Of course this applies only to bugs with good workaround or to rare cases and should never apply to crashing bugs. While sometime is not easy to properly fix crashing bug in stable version. Example of such a bug were bug #37846 and bug #37847 Real fix for version 5.0 contained message in error log instead of crash while in 5.1 error doesn't occur at all.

Monday May 18, 2009

Sometimes you need to run EXPLAIN on long running queries. Most time EXPLAIN takes few seconds, but sometimes it looks like it executes query itself instead of using statistic.


Like in the example following:


mysql> explain select * from (select sleep(10) as foo) bar;
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
| id | select_type | table      | type   | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL | NULL    | NULL |    1 |                |
|  2 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
2 rows in set (10.02 sec)


Look at the time: 10.02 sec for such easy query.


This happens because query in the subquery executes first, then executes whole statement. If rewrite query in a way what it does not use subquery EXPLAIN would work fast as usual:


mysql> explain select sleep(10) as foo;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.01 sec)


For not generic this will mean you should replace subquery with JOIN.

Tuesday May 12, 2009

Lately we saw many complains of how MySQL Config Wizard works.


Most of them are just special case of bug #42820 verified some time ago. I though it has trivial workaround, but one of users complained he read a lot of articles related to this bug, but nobody pointed the workaround.


So here you are: if your my.ini is broken after update, copy old saved configuration file (it should be file named my.ini.bak or similar in the installation directory) or fix new one manually, then don't run Config Wizard again, just manually start the service.

Wednesday May 06, 2009


I read in Russian the talk which I presented at MySQL Conference & Expo this year. Really it was not exactly same, but some parts existed in both. And there was one of my friends who heard the talk in Russian at the conference. He didn't attend English version, but when it finished he came and asked: "Did you say the most important?" "The most important what?" "Why people can not connect to MySQL"


So here is most important part of my talk :)


And if go through our bugs database you will see enormous quantity of bug reports with similar synopsis: "I can not connect to my fresh installed MySQL". Why does it happen?


Major reason is localhost has special meaning. This problem can look different, but has same route.


Favorite problem of Windows users, especcially ones who love GUI Tools is default host. Default host in MySQL is %, but not localhost, neither 127.0.0.1. So if you CREATE USER `abc` IDENTIFIED BY 'xyz'; you really create user `abc`@`%`. Af first glance it looks like you are safe for "Access denied" errors, but MySQL sorts its privilege tables in such a way what more exact address is first and less exact is last and if you have default anonymous user (and you have unless you had run script mysql_secure_installation or manually edited privilege tables) this user would be chosen if you connect as abc from localhost


Favorite problem of UNIX users, especcially ones who use Windows as developer's machine and UNIX on production, is mix of localhost and 127.0.0.1. Assuming you have user test@'127.0.0.1' identified by 'password'; and trying to connect from the same machine MySQL is installed on. Again you will get "Access denied" error, because localhost on UNIX has special meaning and used by default even if you specify --port option. Workaround here is force mysql to use protocol TCP/IP with option --protocol=TCP/IP or --host=127.0.0.1


And last but not least popular reason for "Access denied" errors is mixing name-based and IP-based hostnames for the same username, then specifying different privileges for these users. Which privilege MySQL chooses depends from sort order of mysql.* tables, but in such cases this can be unpredictable and can create bad headaches, especcially in case of upgrade. Better just don't mix them.

This blog copyright 2009 by Sveta Smirnova