Storing MySQL GUID/UUIDs - mysql

This is the best way I could come up with to convert a MySQL GUID/UUID generated by UUID() to a binary(16):
UNHEX(REPLACE(UUID(),'-',''))
And then storing it in a BINARY(16)
Are there any implications of doing it this way that I should know of?

Not many implications. It will slow down the queries a little, but you will hardly notice it.
UNIQUEIDENTIFIER is stored as 16-byte binary internally anyway.
If you are going to load the binary into a client and parse it there, note the bit order, it may have other string representation than the initial NEWID().
Oracle's SYS_GUID() function is prone to this issue, converting it to a string gives different results on client and on server.

From MySQL 8.0 and above you could use UUID_TO_BIN:
UUID_TO_BIN(string_uuid), UUID_TO_BIN(string_uuid, swap_flag)
Converts a string UUID to a binary UUID and returns the result. (The IS_UUID() function description lists the permitted string UUID formats.) The return binary UUID is a VARBINARY(16) value.
CREATE TABLE t (id binary(16) PRIMARY KEY);
INSERT INTO t VALUES(UUID_TO_BIN(UUID(), true));
INSERT INTO t VALUES(UUID_TO_BIN(UUID(), true));
INSERT INTO t VALUES(UUID_TO_BIN(UUID(), true));
SELECT *, BIN_TO_UUID(id) FROM t;
DB-Fiddle.com Demo

Related

Strange answer behavior with SELECT

I have a simple table using a non auto inc INT as primary key.
When querying the table with condition e.g. WHERE id='2,5,6' (unintentionally!) it returns a result set!
Ok, it works, but why?
id is an integer and you compare it with a string '2,5,6'. MySQL converts the string to a number in order to compare the two.
Well, '2,5,6' isn't a number and other DBMS would throw an error. But MySQL uses another approach: it converts character per character until the string is ended or the character is not numeric. So it sees the 2 then the comma. Depending on your settings the comma is the dicimal separater or not. So MySQL either converts to 2 or to 2.5.
Here is the documentation on implicit conversions in MySQL: https://dev.mysql.com/doc/refman/5.5/en/type-conversion.html.
The algorithm on how to convert a string to a number is not explicitly described there, but they say for instance
there are many different strings that may convert to the value 1, such as '1', ' 1', or '1a'.
They also point out in that document that implicit conversion is dangerous, because strings are not converted to DECIMAL (as I would have thought), but to the approximate datatype DOUBLE. So in MySQL we should always avoid implicit conversion from string to number.

How can I encrypt a value in MySQL?

I have this code:
CREATE TABLE Person
(
primaryKey int unsigned NOT NULL,
emailAddress mediumblob NOT NULL
);
What attribute (like NOT NULL) can I use so that the emailAddress would be encrypted?
I would greatly appreciate the help. I tried encrypt() but that's giving me errors.
if you looking to encrypt data where you can decrypt it later then you should use ES_ENCRYPT() AND AES_DECRYPT()
According to the Manual
AES_ENCRYPT() encrypts a string and returns a binary string. AES_DECRYPT() decrypts the encrypted string and returns the original string. .
MySQL 5.1 Doc: AES_ENCRYPT() / AES_DECRYPT()
you can use it like this
INSERT INTO table (email)VALUES(AES_ENCRYPT('myemail', 'secrectkey' ))
to read the data that is encrypted you can do this
SELECT AES_DECRYPT(email, 'secrectkey' ) FROM table
where secrectkey is really a secret value that only authorized users should have access to
But if you are looking for hashing "a one way hash that can't be read back in plain text" you can use one of the following functions
MD5('myemail');
OR
PASSWORD('myemail');
OR
SHA1('myemail');
Then your table length depends on the encryption method you use. you can use VARCHAR() if the length of your encrypted message changes. If you know that you will be using fixed length I would use CHAR(exact_length)
Again the length will depend on the method you use.
You have the complete list of encryption functions supported by MySQL DBMS on this official documentation.

MySQL - How do I search for a GUID

I am using a 3rd party .NET library (Rhino Security) that stores it's identifiers as guids in binary(16) fields in my mysql db. Everything works perfectly from the application but when I attempt to manually run a query via a query editor (TOAD for mysql) no rows are returned for identifiers I know to exist. For instance, if i run the following query, i get no results:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = '02a36462-49b7-406a-a3b6-d5accd6695e5'
Running the same query with no filter returns many results, including one with the above GUID in the EntitySecurityKey field. Is there another way to write a query to search on a guid/binary field?
Thanks!
EDIT
I found it interesting that TOAD returned a string and not an ugly blob. Using a different editor to return the results (for the unfiltered query) I get the raw binary data. I would have assume that my query would work using the binary keyword but neither of the following worked:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = BINARY '02a36462-49b7-406a-a3b6-d5accd6695e5'
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where BINARY EntitySecurityKey = '02a36462-49b7-406a-a3b6-d5accd6695e5'
I am not familiar with Rhino Security, but it uses NHibernate to store to the database as far as I know.
The .net Guid uses 1 Int32, 2 Int16 and 8 Bytes internally to store the 128-bit Guid value.
When NHibernate (3.2) stores/retrieves a Guid value to/from a BINARY(16) column, it uses the .Net ToByteArray() method and Guid(Byte[] ...) constructor respectively. These methods basically swap the order of the bytes for the Int32 and Int16 values. You can reflect on the methods to see the exact code, but here is a simple example of the first 4 bytes:
guidBytes[0] = (byte)this._int32member;
guidBytes[1] = (byte)(this._int32member >> 8),
guidBytes[2] = (byte)(this._int32member >> 16),
guidBytes[3] = (byte)(this._int32member >> 24);
This may be the cause of your issue.
For example, your Guid is stored in MySql differently.
02a36462-49b7-406a-a3b6-d5accd6695e5 - Your Guid from Guid.ToString()
6264a302-b749-6a40-a3b6-d5accd6695e5 - Your Guid in MySql using MySql hex(Guid)
Note: hex(x) does not add the dashes; I added dashes manually for comparison purposes.
Notice that the order of the first 8 bytes is different.
To test if this is your issue, run the following query:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = UNHEX(REPLACE(6264a302-b749-6a40-a3b6-d5accd6695e5,'-',''));
If so, some other items I have noticed in my own travels:
Toad for MySql complicates this too, since it will display the Guid as the .net equivalent in a select statements output. It does not apply the same conversion if you include that Guid in a select's where clause (at least from my experience). You would need to use the Guid as stored by MySql.
You will either need to manually convert the Guids when performing DB queries or you may need to introduce a custom NHibernate type to convert the Guid differently.
You can refer to:
How to read a .NET Guid into a Java UUID
http://pastebin.com/M07wnK5K (I haven't used this code, but found it useful for reference)
Update:
I just tried the custom NH type on the pastebin link above. The byte order is incorrect, at least for my environment. I would need:
private static readonly int[] ByteOrder = new[] { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
to make by .Net output match the select hex(Guid column) from table output.
What strikes me as interesting is that you're storing your GUIDs in fields that are binary(16), emphasis on the 16. Per the manual, a binary field's length is in bytes and will truncate anything that goes over it (only in strict mode though). Is it possible that your GUID's are being truncated? With your sample GUID, 02a36462-49b7-406a-a3b6-d5accd6695e5, try querying the database with the first 16 characters:
WHERE EntitySecurityKey = '02a36462-49b7-40'
Per the accepted answer to this question, a field should be char(16) binary to store a GUID, not just binary(16). However, I couldn't get this to work in my sample table.
What did work for me were using char(36) and also binary(36). Try updating your field-lengths to 36 instead of 16 (I'd do this on a test-table first, just to be safe).
Here was my test table:
CREATE TABLE test_security_keys (
test_key1 char(16) not null,
test_key2 char(16) binary not null,
test_key3 char(36) not null,
test_key4 binary(16) not null,
test_key5 binary(36) not null
);
Then, I ran a script to insert numerous GUIDs (the same one for each column in a row). You can test it with your sample GUID:
INSERT INTO test_security_keys
VALUES ('02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5');
Using a simple SELECT * FROM test_security_keys will show all of the columns except the ones with size 36 to be truncated. Also, binary or not, I was able to successfully query the columns with a regular string-comparison:
SELECT * FROM test_security_keys WHERE test_key3 = '02a36462-49b7-406a-a3b6-d5accd6695e5';
SELECT * FROM test_security_keys WHERE test_key5 = '02a36462-49b7-406a-a3b6-d5accd6695e5';
If you've confirmed that your current columns with binary(16) aren't truncating, I would then-suggest to use a CAST() in your WHERE clause. The following should work (with your sample query):
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
WHERE
EntitySecurityKey = CAST('02a36462-49b7-406a-a3b6-d5accd6695e5' AS binary(16));
If you CAST(.. AS binary(16)) and the input-data is longer than 16 bytes, MySQL should issue a warning (should be unseen and not affect anything) stating that it had to truncated the data (try SHOW WARNINGS; if you get them). This is to be expected, but also means that you can't use binary(16) to store the GUIDs and you'll need to use binary(36) or char(36).
* I have not tried any of this using TOAD, but I've used both command-line MySQL and Navicat and have the same results for both.
I came across this question when I was evaluating how others "searched" for records where the primary key is a binary type (mainly the binary(16) indicative of GUID's) to compare it to my own. I must admit I was a little taken a back that the answers quickly diverged from the users desire to do manual searches based on the binary field but instead focused on changing the column types themselves to characters which, in this case, is murder on InnoDB storage.
An attempted solution for the lack of results would be to search against the Id column instead of the EntitySecurityKey column. Given JP's original query, the modified, working version would be:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where Id = UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));
Again, this assumes the Id field is the binary(16) type. (I wasn't able to determine which column: Id, EntitySecurityKey was the binary(16)). If the EntitySecurityKey is the binary(16) column, then:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));
If that failed to yield the desired results, per this question (How to read a .NET Guid into a Java UUID), it would be of interest to try the same query but with the endianness of the first three parts of the GUID reversed:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where Id = UNHEX(REPLACE('6264a302-b749-6a40-a3b6-d5accd6695e5', '-', ''));
The only other thing I can think of is that EntitySecurityKey is a virtual column based on Id, but I don't have enough information on the table set-up to validate this statement.
Cheers.
From your EDIT, which I've a bit modifed:
Please, try
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
WHERE EntitySecurityKey = X'02a3646249b7406aa3b6d5accd6695e5'
Since the strings have 16 hexadecimal byes each, it may works...
Have you checked what the GUID data actually looks like in the table? Since it's the primary key, it quite possibly isn't stored as a GUID string. If that's the case querying the string value clearly won't work.
Possibly it's in binary format? Or possibly it's a string but without the hyphens? Or maybe it's been converted some other way? I don't know without seeing your data. But whatever the case, if a GUID that you know exists isn't being found when you query it, then whatever format it is in, it clearly isn't stored in the format you're querying.
The way I would solve this would be by searching for a different value in a record that you know exists. Or even just query the top few records without a where clause at all.
This will show you what the data actually looks like. With any luck, that will be sufficient for you to work out how it's been formatted.
If that doesn't help, one possible clue might come from this question: Why are binary Guids different from usual representation
Hope that helps.
Not sure how your database is set up, but I would try these two variants:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = x'02a3646249b7406aa3b6d5accd6695e5'
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = x'6264a302b7496a40b6a3d5accd6695e5'

Converting a mySQL table with id's to a MongoDB table with _id's

I'm transferring a MySQL table to MongoDB. There is a primary key id in my MySQL table and I want this id to be converted to the _id in MongoDB.
I use php's MongoCollection::insert( $mysql_array );
However it doesn't work because if I set $mysql_array['_id'] it is seen by mongo as a String instead of a MongoId. I tried $mysql_array['_id'] = new MongoId( $id ) but it doesn't allow me to override the default _id value. I saw that all my MySQL's integer columns are converted to string by MongoCollection::insert(). If I could set MongoCollection::insert() to correctly transfer an integer it would maybe work.
typecast the _id to a integer value like this...
(int) $mysql_array['_id']
You'll find yourself doing this a lot in mongoDB
The ObjectId is a special type in Mongo, but the _id property doesn't have to be of this type. You can't coerce a string or number into an ObjectId, and you shouldn't.
I assume the problem as you perceive it is that your insert worked, but when you looked at the data in the database the _id property didn't look like _id: ObjectId("1234") (if the original ID was 1234). This is as it should be, and it's perfectly fine.
The idea with ObjectId is that it has a predefined structure that makes it guaranteed (mostly) to be unique across a Mongo cluster, but this also means that it has to have this structure, otherwise it is not an ObjectId.
You also mention that all your integer columns are converted to strings. PHP and PHP libraries, are notoriously sloppy when it comes to types, so make sure that it's not the case that the values are already strings when they come from the MySQL result set. Worst case you have to explicitly cast the values before inserting them into Mongo.
You won't be able to convert an arbitrary String value into an Mongo ObjectId due to its specific characteristics (12 bytes -> 24 chars generated from 4 bytes timestamp, 3 bytes client hostname, 2 bytes PID, 3 bytes inc value).
Either you abandon using the MongoId type in your collection's _id-fields and use your MySQL-ID as a string instead (which is not a problem and makes the most sense) or you let Mongo to generate the documents' _id for you, which is also a suitable solution if you want to be able to use the MongoId functions (assuming you're working with PHP):
The MongoId class
Choosing the second solution you still are able to store your MySQL-IDs in another field of the doc, like id or *mysql_id* to reference them later.
Concerning your question about (int) and (string) values: Are you sure they come as a PHP integer from your MySQL DB? If so, they usually should be stored as integers in Mongo. Check it with a var_dump() and in case of incompatibility cast it with an (int). Maybe it would be helpful if you post your select/insert code...
Use MongoCollection::save() and your array should work.

how to query a blob data

I have a table like :
------------------------------
Test_Id Test_data
(String) (blob)
------------------------------
I want a query to retrieve all the Test_Id's for a matching Test_data.
To achieve something like : select * from test_table where Test_data = blobObject;
How can we do above ??
First: there's no such thing as a string in MySQL. Only char/varchar/text.
Well you could cast it as char for comparison like this:
select * from test_table where Test_data = CAST( blobObject AS CHAR );
what's probably better is to convert your string to a binary string, but this might not give you the right comparison if you expect string comparison behaviour... well best you have a look at the char functions here:
http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html
You can use a hash function such as MD5
SELECT * FROM example_table WHERE MD5(blob_column) = 'a6a7c0ce5a93f77cf3be0980da5f7da3';
MySQL has data types which can store binary data. Not only char/varchar/text, but also BINARY/VARBINARY/BLOB.
See http://dev.mysql.com/doc/refman/5.5/en/blob.html
And it's usage is as simple as normal string type. But, escaping is required. and query length is must specified because binary data may contain NULL character in their contents.
Before MySQL 3.23 (I guess), There were only mysql_query(), mysql_escape_string(). Those function has no capability specifying query length. after BLOB has been introduced in MySQL, mysql_real_query() and mysql_real_escape_string() supported.
I found some examples for you. May this links help you!
http://zetcode.com/db/mysqlc/
http://bytes.com/topic/c/answers/558973-c-client-load-binary-data-mysql