MySQL: how uuid_short is generated? - mysql

I have MySQL 5.5.24. Сalling uuid_short() few times I get just an incremented values:
mysql> select uuid_short();
+-------------------+
| uuid_short() |
+-------------------+
| 22851044396498953 |
+-------------------+
1 row in set (0.00 sec)
mysql> select uuid_short();
+-------------------+
| uuid_short() |
+-------------------+
| 22851044396498954 |
+-------------------+
1 row in set (0.00 sec)
But manual says:
The UUID_SHORT() return value is constructed this way:
(server_id & 255) << 56
+ (server_startup_time_in_seconds << 24)
+ incremented_variable++;
Seems like neither "server_startup_time_in_seconds" or "server_id" changes. (I changed ##global.server_id system variable and it took no effect).
Does anyone know why?

Seems to me like the function does return what's specified. The server Id and the start up time are server specific values and (usually) do not change as long as the server is running. The least significant part of the short UUID is an incremented value.
So I assume after the server start up a seed for UUIDs is created taken the left-shifted server id and the left-shifted start up time. This value is incremented and returned on every creation of an UUID. This explains why changing the ##global.server_id variable has no effect.

Related

MySQL Point precision for large numbers (eg IPv6)

I am trying to do IPv6 address range matching using LineString and Point. But I believe this question is more related to how MySQL handles large number precision in general. (Note the following works without issues with smaller numbers, i.e. IPv4 decimal representation).
For IPv4, the ranges are defined as LineString(Point(-1, ip_start), Point(1, ip_end)).
I seem to be losing precision when creating a Point() with a large number, therefore my range matching is flawed.
MariaDB [mydb]> set #point=Point(0, 42541828702485584113142439188939931648);
Query OK, 0 rows affected (0.00 sec)
MariaDB [mydb]> select astext(#point);
+-------------------------------+
| astext(#point) |
+-------------------------------+
| POINT(0 4.254182870248558e37) |
+-------------------------------+
1 row in set (0.00 sec)
MariaDB [mydb]> select st_y(#point);
+----------------------+
| st_y(#point) |
+----------------------+
| 4.254182870248558e37 |
+----------------------+
1 row in set (0.00 sec)
MariaDB [mydb]> select cast(st_y(#point) as Decimal(39));
+----------------------------------------+
| cast(st_y(#point) as Decimal(39)) |
+----------------------------------------+
| 42541828702485580000000000000000000000 |
+----------------------------------------+
1 row in set (0.00 sec)
MariaDB [mydb]>
How can I use a Point() with large numbers accurately?
SPATIAL functions (st_x, point, etc) work with DOUBLEs, not DECIMAL.
DECIMAL(39) should work. But why not just keep it as a string or hex string (VARCHAR(39) CHARACTER SET ascii) or BINARY(16)?
Please explain your ultimate goal that led you to want to use Spatial tools.
Here is code to handle ranges of IP addresses (separate code for IPv4 or IPv6): http://mysql.rjweb.org/doc.php/ipranges

Check if IP is in CIDR netmask (range)

I have 2 tables ip and cidr.
In the first one I store IP's. (2 column table, id, ip), here is an example (the values are fictional):
id | ip
---+-------------
1 | 172.922.2.10
---+-------------
2 | 194.22.10.13
In the second one I store CIDR netmask's (2 column table, id, cidr), here is an example (the values are fictional):
id | cidr
---+-------------
1 | 26.232.49.0/20
---+---------------
2 | 14.44.182.0/24
Is there any way to make a mysql query to check whether the ip's from the first table are in the range of any of my cidr netmasks?
Note: To convert a cidr netmask to a range of ip's click here
I'd personally recommend using postgres as it has a CIDR data-type and powerful functions to go with it, but there's an interesting discussion on doing similar things in MySQL, too.
http://planet.mysql.com/entry/?id=29283
This has come up in a related project of mine, and this appears to be the top google result for the question, so, you get the answer!
create function get_lowest_ipv4(cidr char(18)) returns bigint deterministic return INET_ATON(SUBSTRING_INDEX(cidr, '/', 1));
create function get_highest_ipv4(cidr char(18)) returns bigint deterministic return get_lowest_ipv4(cidr) + (0x100000000 >> SUBSTRING_INDEX(cidr,'/', -1)) - 1;
You can then do ... from ip_map where INET_ATON("ip.add.re.ss") between get_lowest_ipv4(ip) AND get_highest_ipv4(ip)
Because you declare the functions as deterministic, it'll get cached inside mysql and the calculation will only need to be run once. Then it'll just be 'is integer greater than y and less than x', which will be effectively instant.
MySQL [astpp]> set #cidr="10.11.0.0/16";
Query OK, 0 rows affected (0.00 sec)
MySQL [astpp]> select get_lowest_ipv4(#cidr), get_highest_ipv4(#cidr), INET_NTOA(get_lowest_ipv4(#cidr)), INET_NTOA(get_highest_ipv4(#cidr));
+------------------------+-------------------------+-----------------------------------+------------------------------------+
| get_lowest_ipv4(#cidr) | get_highest_ipv4(#cidr) | INET_NTOA(get_lowest_ipv4(#cidr)) | INET_NTOA(get_highest_ipv4(#cidr)) |
+------------------------+-------------------------+-----------------------------------+------------------------------------+
| 168493056 | 168558591 | 10.11.0.0 | 10.11.255.255 |
+------------------------+-------------------------+-----------------------------------+------------------------------------+
1 row in set (0.00 sec)
MySQL [astpp]> set #cidr="10.11.12.1/32";
Query OK, 0 rows affected (0.00 sec)
MySQL [astpp]> select get_lowest_ipv4(#cidr), get_highest_ipv4(#cidr), INET_NTOA(get_lowest_ipv4(#cidr)), INET_NTOA(get_highest_ipv4(#cidr));
+------------------------+-------------------------+-----------------------------------+------------------------------------+
| get_lowest_ipv4(#cidr) | get_highest_ipv4(#cidr) | INET_NTOA(get_lowest_ipv4(#cidr)) | INET_NTOA(get_highest_ipv4(#cidr)) |
+------------------------+-------------------------+-----------------------------------+------------------------------------+
| 168496129 | 168496129 | 10.11.12.1 | 10.11.12.1 |
+------------------------+-------------------------+-----------------------------------+------------------------------------+
1 row in set (0.01 sec)
MySQL [astpp]>
The only important caveat is that you insert VALID CIDRs. For example, 10.11.12.13/24 is not valid. That's an IP Address INSIDE the 10.11.12.0/24 network.
If you are unable to validate the CIDRs before inserting them (for some crazy reason), you could change get_lowest_ipv4 to do a bitwise comparison on the source, but that's much less elegant.
INET_ATON(SUBSTRING_INDEX(`ip`, '/', 1)) & 0xffffffff ^((0x1 <<(32 - SUBSTRING_INDEX(`ip`, '/', -1))) -1 )
Is an (untested) stab at matching invalid CIDRs.

mysql converting bit to char not working

I was trying to feed a result of a query as a parameter for another query and all was working fine except this field that has a datatype of bit. so i tried to convert the value of the field using convert() and cast() but it seems to be not working as its returning this wierd symbol of a small rectange which hava three 0's and a 1. so can anyone tell me why this is happening and how to fix it , here is my query
select CONVERT(isMale , char(5)) from person;
and the thing is it gives me the correct answer when i dont use the convert but since am giving this result to another query as a parameter it causing me the problem.
you can use BIN function like this:
SELECT BIN(isMale +0) from person;
sample
MariaDB [yourschema]> SELECT BIN(b'1001' +0) ;
+-----------------+
| BIN(b'1001' +0) |
+-----------------+
| 1001 |
+-----------------+
1 row in set (0.00 sec)
MariaDB [yourschema]>
Here some stuff from MariaDB Manual:
Description
Converts numbers between different number bases. Returns a
string representation of the number N, converted from base from_base
to base to_base.
Returns NULL if any argument is NULL, or if the second or third
argument are not in the allowed range.
The argument N is interpreted as an integer, but may be specified as
an integer or a string. The minimum base is 2 and the maximum base is
36. If to_base is a negative number, N is regarded as a signed number. Otherwise, N is treated as unsigned. CONV() works with 64-bit
precision.
Some shortcuts for this function are also available: BIN(), OCT(),
HEX(), UNHEX(). Also, MariaDB allows binary literal values and
hexadecimal literal values.
BIN is a short form from CONV(value,from,to) where you can convert from base to base
so binary 1001 = 9 as int
here i give the value in decimal (14) and convert it from base 10 to base 2
MariaDB [yourschema]> SELECT CONV(14,10 ,2);
+-----------------+
| CONV(14,10 ,2) |
+-----------------+
| 1110 |
+-----------------+
1 row in set (0.00 sec)
so, if you want to have 0 on the left you can add a value like this
MariaDB [yourschema]> SELECT CONV(8192 + 14,10 ,2);
+------------------------+
| CONV(8192 + 14,10 ,2) |
+------------------------+
| 10000000001110 |
+------------------------+
1 row in set (0.00 sec)
and then you can get n chars from right:
MariaDB [yourschema]> SELECT RIGHT(CONV(8192 + 14,10 ,2),8);
+---------------------------------+
| RIGHT(CONV(8192 + 14,10 ,2),8) |
+---------------------------------+
| 00001110 |
+---------------------------------+
1 row in set (0.40 sec)
MariaDB [yourschema]>
I think you want to use CAST
select CAST(isMale as CHAR) from person;
seeing #Bernd Buffen answer i tried using the convert with +0 and it works , eventhough i dont know why
select CONVERT(isMale +0, char(5)) from person;

MySQL always returning BIT values as blank

From my create table script, I've defined the hasMultipleColors field as a BIT:
hasMultipleColors BIT NOT NULL,
When running an INSERT, there are no warnings thrown for this or the other BIT fields, but selecting the rows shows that all BIT values are blank.
Manually trying to UPDATE these records from the command line gives odd effect - shows that the record was match and changed (if appropriate), but still always shows blank.
Server version: 5.5.24-0ubuntu0.12.04.1 (Ubuntu)
mysql> update pumps set hasMultipleColors = 1 where id = 1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
mysql> select hasMultipleColors from pumps where id = 1;
+-------------------+
| hasMultipleColors |
+-------------------+
| |
+-------------------+
1 row in set (0.00 sec)
mysql> update pumps set hasMultipleColors = b'0' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select hasMultipleColors from pumps where id = 1;
+-------------------+
| hasMultipleColors |
+-------------------+
| |
+-------------------+
1 row in set (0.00 sec)
Any thoughts?
You need to cast the bit field to an integer.
mysql> select hasMultipleColors+0 from pumps where id = 1;
This is because of a bug, see: http://bugs.mysql.com/bug.php?id=43670. The status says: Won't fix.
You can cast BIT field to unsigned.
SELECT CAST(hasMultipleColors AS UNSIGNED) AS hasMultipleColors
FROM pumps
WHERE id = 1
It will return 1 or 0 based on the value of hasMultipleColors.
You need to perform a conversion as bit 1 is not printable.
SELECT hasMultipleColors+0 from pumps where id = 1;
See more here:
http://dev.mysql.com/doc/refman/5.0/en/bit-field-literals.html
The actual reason for the effect you see, is that it's done right and as expected.
The bit field has bits and thus return bits, and trying to output a single bit as a character will show the character with the given bit-value – in this case a zero-width control character.
Some software may handle this automagically, but for command line MySQL you'll have to cast it as int in some way (e.g. by adding zero).
In languages like PHP the ordinal value of the character will give you the right value, using the ord() function (though to be really proper, it would have to be converted from decimal to binary string, to work for bit fields longer than one character).
EDIT:
Found a quite old source saying that it changed, so a MySQL upgrade might make everything work more as expected: http://gphemsley.wordpress.com/2010/02/08/php-mysql-and-the-bit-field-type/

mysql, prepared statements, and automatic type conversion

I am getting different results performing the exact same query using regular statements and prepared statements, and I think it's a type conversion bug.
mysql> show columns from server where field = "vlan";
+-------------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------+------+-----+---------+-------+
| vlan | int(5) | YES | MUL | NULL | |
+-------------+--------+------+-----+---------+-------+
mysql> select hostname from server where `vlan` = '184.182' limit 1;
Empty set (0.00 sec)
mysql> prepare stupid from "select hostname from server where `vlan` = ? limit 1";
Query OK, 0 rows affected (0.00 sec)
Statement prepared
mysql> set #vlan = '184.182';
Query OK, 0 rows affected (0.00 sec)
mysql> execute stupid using #vlan;
+-------------------+
| hostname |
+-------------------+
| web20.servers.com |
+-------------------+
1 row in set (0.00 sec)
the real value of vlan is 184
it looks like the way mysql is handling type conversions is different for prepared statements and regular statements? does that make sense? how do i fix this?
The expected data type of prepared statement parameters is determined upon statement preparation, and type conversion to that data type takes place prior to statement execution.
In your example, an integer parameter is expected; therefore the provided string is cast to an integer (184) before the statement is executed, and the comparison between the integer column vlan and the parameter is successful for the matching record.
The "regular" statement, by contrast, compares the integer column with a string; therefore the arguments are compared as floating point numbers, and no record has a matching vlan.
To avoid this situation, ensure that the data type cannot be determined upon preparation (or that the determined data type does not lose any information) - for example:
prepare not_so_stupid from
"select hostname from server where `vlan` = CAST(? AS CHAR) limit 1"
;