Category: Technical

Anything that uses 1s and 0s

  • Proxy Projects

    When I first heard of MySQL Proxy, I thought, “That’s way cool.”

    Then I thought, “Neat toy, but I can’t think of anything I’d actually want it for. I can spell ‘SELECT’ without the client correcting me, and I’d just as soon use the built in logs. ”

    Now I’m starting to gather ideas that might be practical:
    (more…)

  • Complete Set

    Occasionally in #mysql the question pops up, “How can I get all the values from a table that satisfy all of multiple conditions?”

    Then we beat the questioner with a large stick, until they give an example of what they really mean.

    SELECT * FROM quizAnswers;
    +-------------+----------+
    | studentName | question |
    +-------------+----------+
    | seekwill    | A        |
    | seekwill    | B        |
    | seekwill    | C        |
    | roxlu       | A        |
    | fury        | B        |
    | fury        | B        |
    +-------------+----------+

    Find all the students who have answered both questions ‘A’ and ‘B’.
    (more…)

  • Views and Social Engineering

    CREATE TABLE secretData (
        secretValue int COMMENT 'If this goes over 5, WWIII will start'
    );
    
    CREATE SQL SECURITY DEFINER VIEW censoredData AS 
    SELECT * FROM secretData WHERE secretValue < 5 WITH CHECK OPTION;
    
    GRANT SELECT, INSERT ON test.censoredData TO 'evilFiend'@'%';
    

    <telephone> ring ring

    <sysadmin> "Hello?"

    <evilFiend> "I'd like to create an insertable view on some tables I already have rights to. I don't know just yet what I'll use for my select statement."

    <sysadmin> "Ok. I'll set it up so you can do what you'd like."

    CREATE SQL SECURITY INVOKER VIEW evilFiendsView AS SELECT 1;
    GRANT SELECT, INSERT, ALTER ON test.evilFiendsView TO 'evilFiend'@'%';
    

    Evil Fiend

    evilFiend connects to the server, while twiddling the end of his handlebar mustache.

    ALTER VIEW evilFiendsView AS 
    SELECT * FROM censoredData WITH LOCAL CHECK OPTION;
    
    INSERT INTO evilFiendsView VALUES (42);
    

    Muhahaha!

  • Messing with LAST_INSERT_ID()

    Time for another MySQL Community Quiz:

    Everybody knows that LAST_INSERT_ID() returns the value most recently created in an auto_increment column. Fewer of us know that you can pass a value to LAST_INSERT_ID(), and that value will be returned for the next call to LAST_INSERT_ID(). For example,

    INSERT INTO table (someNonAutoIncrementField) VALUES (LAST_INSERT_ID(42));
    SELECT LAST_INSERT_ID();

    The INSERT inserts ’42’ into the table, and the SELECT returns 42.

    So, here’s the question: What if we pass an expression to LAST_INSERT_ID and also use an auto_increment field in the same statement?

    CREATE TABLE test (id int auto_increment primary key, field int);
    INSERT INTO test (id, field) VALUES (NULL, LAST_INSERT_ID(42));
    SELECT LAST_INSERT_ID();


    You get the auto_increment field, at least in this case:

    mysql> INSERT INTO test (id, field) VALUES (NULL, LAST_INSERT_ID(42));
    Query OK, 1 row affected (0.08 sec)
    
    mysql> SELECT LAST_INSERT_ID();
    +------------------+
    | LAST_INSERT_ID() |
    +------------------+
    |                1 |
    +------------------+
    1 row in set (0.00 sec)

    And for a follow up, does it matter if you swap the insertion around?

    INSERT INTO test (field, id) VALUES (LAST_INSERT_ID(42), NULL);
    SELECT LAST_INSERT_ID();


    Nope.

    mysql> INSERT INTO test (field, id) VALUES (LAST_INSERT_ID(42), NULL);
    Query OK, 1 row affected (0.08 sec)
    
    mysql> SELECT LAST_INSERT_ID();
    +------------------+
    | LAST_INSERT_ID() |
    +------------------+
    |                2 |
    +------------------+
    1 row in set (0.00 sec)

    The manual warns about mixing LAST_INSERT_ID() and LAST_INSERT_ID(expr). This may fall under that same caveat.

  • Access Control Quiz

    First, the setup:

    CREATE TABLE `user` (
      `user` varchar(255) DEFAULT NULL,
      `host` varchar(255) DEFAULT NULL,
      `sort` int(11) DEFAULT NULL
    );
    
    INSERT INTO `user` 
        (`user`, `host`, `sort`) 
    VALUES 
        ('','%',8),
        ('testUser','%',7),
        ('','%localhost',9),
        ('testUser','%localhost',5),
        ('','%localhost%',10),
        ('testUser','%localhost%',6),
        ('','localhost',2),
        ('testUser','localhost',1),
        ('','localhost%',4),
        ('testUser','localhost%',3);

    Now, the quiz:

    SELECT * FROM user ORDER BY ___

    Fill in the blank to get the following output. Difficulty: the `sort` field may not appear anywhere in the query.

    +----------+-------------+------+
    | user     | host        | sort |
    +----------+-------------+------+
    | testUser | localhost   |    1 |
    |          | localhost   |    2 |
    | testUser | localhost%  |    3 |
    |          | localhost%  |    4 |
    | testUser | %localhost  |    5 |
    | testUser | %localhost% |    6 |
    | testUser | %           |    7 |
    |          | %           |    8 |
    |          | %localhost  |    9 |
    |          | %localhost% |   10 |
    +----------+-------------+------+
  • Capital. Just capital.

    Culled from the Certification Study Guide:

    mysql> SELECT * FROM test;
    +----------------+
    | data           |
    +----------------+
    | This is a test |
    +----------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT UPPER(data) FROM test;
    +----------------+
    | UPPER(data)    |
    +----------------+
    | This is a test |
    +----------------+
    1 row in set (0.03 sec)

    How’s that work?

     CREATE TABLE `test` (
       `data` varbinary(255) default NULL
     );
    

    Binary strings are just a list of bytes. They aren’t characters anymore, so there is no upper or lower case. Remember that when choosing between char/varchar/text and binary/varbinary/blob.

  • LineBreak URL

    A bookmarklet to show the URL of the current page, unescaped, and with the ? and & replaced with line breaks. Handy for viewing the key/value pairs in long query strings.

    LineBreak URL

  • Octets from INET_ATON

    So you’ve follow Arjen’s suggestion and are storing all your IP addresses in a nice integer format, having converted them with INET_ATON and selecting them with INET_NTOA.

    But now you want to use one of the octets, maybe to find out how many connections you get from 192.x.x.x.

    One way is to turn the integer back into a string, and use one of the substring functions to grab the parts you need. Let’s try that out for speed (I’ll use 192.168.1.1 in my examples, which translates to 3232235777):

    mysql> SELECT BENCHMARK(10000000, SUBSTRING_INDEX(INET_NTOA(3232235777), '.', 1) = '192');
    +-----------------------------------------------------------------------------+
    | BENCHMARK(10000000, SUBSTRING_INDEX(INET_NTOA(3232235777), '.', 1) = '192') |
    +-----------------------------------------------------------------------------+
    |                                                                           0 |
    +-----------------------------------------------------------------------------+
    1 row in set (6.28 sec)

    Is there a better way? Of course; I’d hardly be writing this blog entry if there weren’t. According to the manual, the calculation for INET_ATON is
    192x256^3 + 168x256^2 + 1x256^1 + 1x256^0
    Notice all those 256s. 256 is 2^8, so all we have to do is shift the bits to the right by the appropriate multiple of 8, and then throw away anything higher than the 8th bit. We’ll check real quick that gives us the right number:

    mysql> SELECT
        ->   (3232235777 >> 24) & 255 AS oct1,
        ->   (3232235777 >> 16) & 255 AS oct2,
        ->   (3232235777 >> 8) & 255 AS oct3,
        ->   (3232235777 >> 0) & 255 AS oct4;
    +------+------+------+------+
    | oct1 | oct2 | oct3 | oct4 |
    +------+------+------+------+
    |  192 |  168 |    1 |    1 |
    +------+------+------+------+

    Looks like the right answer. Let’s see how it compares.

    mysql> SELECT BENCHMARK(10000000, (3232235777 >> 24) & 255 = 192);
    +-----------------------------------------------------+
    | BENCHMARK(10000000, (3232235777 >> 24) & 255 = 192) |
    +-----------------------------------------------------+
    |                                                   0 |
    +-----------------------------------------------------+
    1 row in set (1.61 sec)

    Noticeably faster, and you get to use those bitwise operators that you’ve always wondered about but never had a reason to use.

  • Comparing version output

    Features and bugs come and go with each release of MySQL, making it tricky for us hobbyists to help each other out when we don’t have the same version installed and observed behavior is a little different for each.

    So I installed a dozen versions of MySQL, ranging from 3.23.58 to the current beta, and wrote a script (download below) to send one stream of SQL to each and compare output. Now I can show how identical SQL produces different results.

    There’s no reason they have to be different versions; I might run this script against a bunch of identical slaves to check replication status.

    I found some unexpected things along the way (who’d have thought the ON clause was so late to join the syntax?) and some silly trivia that just might win me a book someday (table formatted output differs in justification and whitespace between a few of the versions – can you find where?)

    MySQL Demux

    Requires the PEAR package Console_Getopt.

  • Drop all tables

    Since there exists neither DROP TABLE * nor DROP TABLE WHERE name LIKE ‘something’, here are two ways to achieve the same:

    For version 5.0+:

    mysql -N -e "SELECT CONCAT('DROP TABLE ', GROUP_CONCAT(table_name), ';') FROM information_schema.tables WHERE table_schema = 'dbName'" | mysql dbName

    (more…)