Question

There are such table and functions:

CREATE TABLE `test` (`ID` BINARY(16)) ENGINE=InnoDB;

CREATE FUNCTION `UUID_ENCODE`(`uuid` CHAR(36))
    RETURNS binary(16)
    DETERMINISTIC
BEGIN
    RETURN UNHEX(REVERSE(REPLACE(uuid,'-','')));
END

CREATE FUNCTION `UUID_DECODE`(`uuid` BINARY(16))
    RETURNS char(36)
    DETERMINISTIC
BEGIN
RETURN LOWER(CONCAT_WS('-',
    REVERSE(HEX(RIGHT(uuid,6))),
    REVERSE(HEX(MID(uuid,9,2))),
    REVERSE(HEX(MID(uuid,7,2))),
    REVERSE(HEX(MID(uuid,5,2))),
    REVERSE(HEX(LEFT(uuid,4)))
));
END

Insert is working properly:

INSERT INTO test (ID) VALUES(UUID_ENCODE('323febe6-cd89-4773-a46c-aab794fb7cbc'));

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+

Just create a trigger:

CREATE TRIGGER `test_uuid_encode` BEFORE INSERT ON `test` FOR EACH ROW BEGIN
    SET NEW.ID = UUID_ENCODE(NEW.ID);
END

and get half truncated value in table:

INSERT INTO test (ID) VALUES('323febe6-cd89-4773-a46c-aab794fb7cbc');

Warning (Code 1265): Data truncated for column 'ID' at row 1

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 000000000000-0000-0032-3feb-e6cd8947 |
+--------------------------------------+

What is wrong with this trigger?

Software version is:

Ver 5.5.33a-MariaDB for Win64 on x86 (mariadb.org binary distribution)

Was it helpful?

Solution

The ID column of your table is declared as BINARY(16), and your UUID_ENCODE expects a CHAR(36). When you invoke your UUID_ENCODE function directly without a trigger, it correctly receives your 36 characters string. When you use the trigger instead, the value you are inserting is first converted to the type of the column, so NEW.ID will contain the results of CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16). When the trigger invokes the function, it will cast again the value of NEW.ID to the type expected by your function. So, this is the value your function will receive:

SELECT CAST(CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16)) AS CHAR);

323febe6-cd89-47

As you can see, your function receives the value truncated. The results you are getting are equivalent to:

SELECT UUID_DECODE(UUID_ENCODE('323febe6-cd89-47'));

000000000000-0000-0032-3feb-e6cd8947

UPDATE

One way to implement the desired functionality in a trigger would be to add a dummy nullable column with the type your function expects:

CREATE TABLE test (ID BINARY(16) KEY DEFAULT 0, CHARID CHAR(36) NULL);

CREATE TRIGGER test_uuid_encode BEFORE INSERT ON test FOR EACH ROW BEGIN
    SET NEW.ID = UUID_ENCODE(NEW.CHARID)
      , NEW.CHARID = NULL;
END

This way you can do:

INSERT test (CHARID) VALUES ('323febe6-cd89-4773-a46c-aab794fb7cbc');

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+

I created a fiddle here.

Note that:

  • Although CHARID accepts NULLs, ID does not, so an attempt to insert a NULL value in CHARID would result in the trigger trying to set ID to NULL and the insertion being rejected.
  • Attempting to insert an invalid value into CHARID that causes UUID_ENCODE to return NULL will also fail.
  • The DEFAULT 0 clause for ID is there only to allow you to omit the column from the insert list. The value written to the table will always be the one generated by your trigger.
  • A nullable column that is always NULL should take between 0 and 2 bytes of additional storage per row depending on your table layout and row format (you would need to avoid for instance the FIXED format of the MyISAM engine).
  • There are many variations possible. If you don't mind the extra storage, you can keep the CHARID value without setting it to NULL. If you want to allow explicit insert of binary ID values you can add a check to the trigger so you compute NEW.ID only if it is 0, etc.
  • You should have a similar trigger for UPDATE operations, if they are allowed.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top