A Little Noise

February 6, 2013

Default user

Filed under: MySQL — snoyes @ 2:20 pm

It came up twice in two days: if you do not specify the user name when connecting, what gets picked?

The manual says:
“On Unix, most MySQL clients by default try to log in using the current Unix user name as the MySQL user name, but that is for convenience only.”
http://dev.mysql.com/doc/refman/5.6/en/user-names.html

“The default user name is ODBC on Windows or your Unix login name on Unix.”
http://dev.mysql.com/doc/refman/5.6/en/connecting.html

Let’s be a little more specific. The relevant section of code is in libmysql/libmysql.c

On Linux, we check the following in this order:
– if (geteuid() == 0), user is “root”
– getlogin()
– getpwuid(geteuid())
– environment variables $USER, $LOGNAME, $LOGIN
If none of those return non-NULL results, use “UNKNOWN_USER”

On Windows:
– environment variable $USER
If that’s not set, use “ODBC”.

I wondered why on Windows we check $USER but not $USERNAME. I gather that it’s an ODBC thing.

October 1, 2012

Connection Conundrum

Filed under: MySQL Gotchas,PHP Gotchas — snoyes @ 12:13 pm

Define a user like this:

GRANT ALL ON *.* TO 'myuser'@'localhost' IDENTIFIED BY 'super$ecret';

Then try a PHP script like this:

<?php
mysqli_connect("localhost", "myuser", "super$ecret");
?>

What happens and why? How could you avoid it?

And for glory, what single line could you add that would prevent the error, without making any changes to the mysqli_connect line?

September 5, 2012

Auto_increment gaps

Filed under: MySQL — snoyes @ 3:12 pm

Why are there gaps in my auto_increment sequence, even if there are no deletes or rolled back transactions?

Is it a bug?

The manual says, “For lock modes 1 or 2, gaps may occur between successive statements because for bulk inserts the exact number of auto-increment values required by each statement may not be known and overestimation is possible.”

Where does that overestimation come from?

An example to illustrate:

DROP TABLE IF EXISTS t;
CREATE TABLE t (a bigint unsigned auto_increment primary key) ENGINE=InnoDB SELECT NULL AS a;
/* #1 */ INSERT INTO t SELECT NULL FROM t;
/* #2 */ INSERT INTO t SELECT NULL FROM t;
/* #3 */ INSERT INTO t SELECT NULL FROM t;
/* #4 */ INSERT INTO t SELECT NULL FROM t;
SELECT * FROM t;

+----+
| a |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 6 |
| 7 |
| 8 |
| 9 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
+----+
16 rows in set (0.02 sec)

Notice that 5 and 10-12 are missing. If we did another insert, we’d be missing 21-27 (try it and see!)

Here’s a model of what MySQL is doing:

Create the table and simultaneously insert a single row. That is the row where a=1.

#1: Insert as many rows as there are in the table (it’s one row, but MySQL doesn’t know that.)
– Grab a chunk of auto_increment values. How many in the chunk? One – the value ‘2’. Insert it (one row inserted).
– No more rows to insert, so all done.

#2: Insert as many rows as there are in the table (it’s two rows, but MySQL doesn’t know that.)
– Grab a chunk of auto_increment values. How many in the chunk? One – the value ‘3’. Insert it (one row inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – two values, ‘4’ and ‘5’. Insert the ‘4’ (two rows inserted).
– No more rows to insert. Discard the left over ‘5’.

#3: Insert as many rows as there are in the table (it’s four rows, but MySQL doesn’t know that.)
– Grab a chunk of auto_increment values. How many in the chunk? One – the value ‘6’. Insert it (one row inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – two values, ‘7’ and ‘8’. Insert them (three rows inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – four values, ‘9’, ’10’, ’11’, ’12’. Insert the ‘9’ (four rows inserted).
– No more rows to insert. Discard the left over ’10’, ’11’, and ’12’.

#4: Insert as many rows as there are in the table (it’s eight rows, but MySQL doesn’t know that.)
– Grab a chunk of auto_increment values. How many in the chunk? One – the value ’13’. Insert it (one row inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – two values, ’14’ and ’15’. Insert them (three rows inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – four values, ’16’, ’17’, ’18’, ’19’. Insert them (seven rows inserted).
– Still more rows to insert. Grab another chunk, twice as big as before – eight values, ’20’, ’21’, ’22’, …, ’27’. Insert the ’20’ (eight rows inserted).
– No more rows to insert. Discard the left over ’21’, ’22’, etc.

The gap can get as big as 65535 (I didn’t look in the code to confirm that, it’s just what running the test above a few more times seems to suggest).

When innodb_autoinc_lock_mode=1, there can be gaps between statements, but not within a statement, because there’s a lock on the auto_increment until we’re done. #4 above is guaranteed to get 8 consecutive values, you just might not be sure where they are going to start (well, now you are, because you read this post).

When innodb_autoinc_lock_mode=2, there can be gaps within a statement, because the auto_increment is not locked. Imagine we’re in the middle of #4 above. Our statement is inserting the ’14’ and ’15’, when another statement comes along wanting just a single auto_increment value. It gets the ’16’. Now it’s our turn to ask for another chunk, and we get ’17’, ’18’, ’19’, ’20’. While we’re doing those, another statement comes along and steals our ’21’. So the last row for our statement is ’22’.

December 10, 2011

MidPoint of a LineString

Filed under: MySQL — snoyes @ 10:12 am

CREATE FUNCTION MidPoint(ls LINESTRING)
RETURNS POINT
DETERMINISTIC
BEGIN
DECLARE len double;
DECLARE workLength double DEFAULT 0;
DECLARE workPoint int DEFAULT 1;
DECLARE point0 POINT;
DECLARE point1 POINT;
DECLARE distanceOver double;
DECLARE segmentLength double;

IF (NumPoints(ls) = 1) THEN return PointN(ls, 1); END IF;

-- find half the length of the linestring
SET len := GLength(ls) / 2;

-- walk the linestring until we exceed the distance
WHILE (workLength < len) DO SET point0 = PointN(ls, workPoint); SET workPoint := workPoint + 1; SET point1 = PointN(ls, workPoint); SET segmentLength = GLength(LineString(point0, point1)); SET workLength := workLength + segmentLength; END WHILE; -- distance to backup SET distanceOver = workLength - len; -- midpoint is distanceOver back down the last segement RETURN POINT( X(point1) - distanceOver / segmentLength * (X(point1) - X(point0)), Y(point1) - distanceOver / segmentLength * (Y(point1) - Y(point0)) ); END

November 9, 2011

Quiz: A More Perfect UNION

Filed under: MySQL Gotchas — snoyes @ 2:59 pm

Today I saw a query like this:
SELECT d FROM t;

Performance was terrible. I ran out of patience after several minutes and killed the thread.

I changed the query to this:
(SELECT d FROM t) UNION ALL (SELECT NULL LIMIT 0);

It completed in under 3 seconds.

Can you explain how a no-op UNION so dramatically improved performance? (I couldn’t have, without help from Jesper Krogh and James Day).

Hint #1 ▼

Hint #2 ▼

Hint #3 ▼

Hint #4 ▼

Answer ▼

October 12, 2011

Yet another way to count occurences of a substring in a string

Filed under: MySQL — snoyes @ 11:20 am

In response to http://forge.mysql.com/snippets/view.php?id=60 – adding support to count strings, not just characters

(
  character_length(<string1>)
  - character_length(REPLACE(<string1>, <searchString>, '')
) / character_length(<searchString>)

Easter date

Filed under: MySQL — snoyes @ 11:19 am

Finds the date for Easter (Western) given a year.
Sample usage: SELECT easter(YEAR(NOW()));

DELIMITER //
CREATE FUNCTION easter(inYear YEAR) RETURNS DATE DETERMINISTIC
BEGIN
    DECLARE a, b, c, d, e, k, m, n, p, q INT;

    DECLARE easter DATE;

    SET k = FLOOR(inYear / 100);
    SET a = MOD(inYear, 19);
    SET b = MOD(inYear, 4);
    SET c = MOD(inYear, 7);
    SET q = FLOOR(k / 4);
    SET p = FLOOR((13 + 8 * k) / 25);
    SET m = MOD((15-p+k-q), 30);
    SET d = MOD((19 * a + m), 30);
    SET n = MOD((4+k-q), 7);
    SET e = MOD((2*b+4*c+6*d+n), 7);

    SET easter = CASE
        WHEN d + e <= 9 THEN CONCAT_WS('-', inYear, '03', 22 + d + e)
        WHEN d = 29 AND e = 6 THEN CONCAT_WS('-', inYear, '04-19')
        WHEN d = 28 AND e = 6 AND a > 10 THEN CONCAT_WS('-', inYear, '04-18')
        ELSE CONCAT_WS('-', inYear, '04', LPAD(d + e - 9, 2, 0))
    END;

    RETURN easter;
END
//
DELIMITER ;

Show all grants

Filed under: MySQL — snoyes @ 11:18 am

A stored procedure to show all grants in the database.

USE mysql;

DELIMITER //
CREATE PROCEDURE showAllGrants() BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE theUser CHAR(16);
    DECLARE theHost CHAR(60);
    DECLARE cur1 CURSOR FOR SELECT user, host FROM mysql.user;
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
    OPEN cur1;

    REPEAT
        FETCH cur1 INTO theUser, theHost;
        IF NOT done THEN
            SET @sql := CONCAT('SHOW GRANTS FOR ', QUOTE(theUser), '@', QUOTE(theHost));
            PREPARE grantStatement FROM @sql;
            EXECUTE grantStatement;
            DROP PREPARE grantStatement;
        END IF;
    UNTIL done END REPEAT;

    CLOSE cur1;
END//
DELIMITER ;

CALL showAllGrants();

Roman numerals

Filed under: MySQL — snoyes @ 11:18 am

Convert an integer in the range 0 to 3999 to Roman numerals.

CREATE FUNCTION `toRoman`(inArabic int unsigned) RETURNS varchar(15) CHARSET latin1 DETERMINISTIC
BEGIN
    DECLARE numeral CHAR(7) DEFAULT 'IVXLCDM';

    DECLARE stringInUse CHAR(3);
    DECLARE position tinyint DEFAULT 1;
    DECLARE currentDigit tinyint;

    DECLARE returnValue VARCHAR(15) DEFAULT '';

    IF(inArabic > 3999) THEN RETURN 'overflow'; END IF;
    IF(inArabic = 0) THEN RETURN 'N'; END IF;

    WHILE position <= CEIL(LOG10(inArabic + .1)) DO
        SET currentDigit := MOD(FLOOR(inArabic / POW(10, position - 1)), 10);

        SET returnValue := CONCAT(
            CASE currentDigit 
                WHEN 4 THEN CONCAT(SUBSTRING(numeral, position * 2 - 1, 1), SUBSTRING(numeral, position * 2, 1))
                WHEN 9 THEN CONCAT(SUBSTRING(numeral, position * 2 - 1, 1), SUBSTRING(numeral, position * 2 + 1, 1))
                ELSE CONCAT(
                    REPEAT(SUBSTRING(numeral, position * 2, 1), currentDigit >= 5),
                    REPEAT(SUBSTRING(numeral, position * 2 - 1, 1), MOD(currentDigit, 5))
                )
            END,
            returnValue);

        SET position := position + 1;
    END WHILE;
    RETURN returnValue;
END

SHOW CREATE TRIGGER 5.0

Filed under: MySQL — snoyes @ 11:17 am

SHOW CREATE TRIGGER for 5.0 – 5.1.20

CREATE FUNCTION SHOW_CREATE_TRIGGER(in_trigger varchar(255)) 
RETURNS text READS SQL DATA 
BEGIN
    DECLARE returnText text;
    SELECT CONCAT_WS(
        " ",
        "CREATE TRIGGER", 
        TRIGGER_NAME, 
        ACTION_TIMING, 
        EVENT_MANIPULATION, 
        "ON", 
        EVENT_OBJECT_TABLE, 
        "FOR EACH ROW", 
        ACTION_STATEMENT) into returnText
    FROM information_schema.triggers;
RETURN returnText;
END
« Newer PostsOlder Posts »

Powered by WordPress