Bitwise operating issue - mysql

I'm trying to rebuild a function for generating GUIDs used by the game Arma 3 in a MySQL function. There are some examples in various languages over here: https://gist.github.com/Fank/11127158
Basicially this is what I tried so far:
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(playerid varchar(17)) RETURNS varchar(32) CHARSET latin1
BEGIN
DECLARE temp bigint;
DECLARE i int;
SET i = 0;
SET temp = 0;
WHILE i < 8 DO
SET temp = temp + CHAR(playerid & 0xFF);
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5("BE" + temp);
END
What I understood so far orientating at the php example:
First of all we have the steamid given as a varchar.
Afterwards we are iterating 8 times and adding the char of the bitwise addition of the playerid and the value of 0xFF. Then we are performing a 8 bitwise right shift on the playerid.
Afterwards the string "BE" is added the temp result and a md5 is generated of this and returned.
However I'm facing the issue that this function always returns 0. I tried many things so far, like using a blob instead of an int for the temp var.
Edit: After the hint to use CONCAT instead of the + operator the result is still not matching with the guid calculators out there.
The code looks now like this:
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(playerid varchar(17)) RETURNS varchar(32) CHARSET latin1
BEGIN
DECLARE temp bigint;
DECLARE i int;
SET i = 0;
SET temp = "";
WHILE i < 8 DO
SET temp = CONCAT(temp, CHAR(playerid & 0xFF));
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5(CONCAT("BE", temp));
END
However, using the playerid 76561197996545192 the function returns 2a0f7ebed67e04afaf7ea032e1ed22e3 instead of cd97cc68c1038b485b081ba2aa3ea6fa which should be the expected output.

This works for me:
DROP FUNCTION IF EXISTS `generateGUID`;
DELIMITER //
CREATE DEFINER=`root`#`localhost` FUNCTION `generateGUID`(`playerid` BIGINT UNSIGNED) RETURNS varchar(32) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE temp text CHARSET ascii;
DECLARE i int;
SET i = 0;
SET temp = "";
WHILE i < 8 DO
SET temp = CONCAT(temp, CHAR(playerid & 0xFF));
SET playerid = playerid >> 8;
SET i = i + 1;
END WHILE;
RETURN MD5(CONCAT("BE", temp));
END//
DELIMITER ;
http://sqlfiddle.com/#!9/6cc709/1

Related

MySQL CHAR variable type in routine not recognizing SPACE character

Can someone please tell me why MySQL does not recognize SPACE character when assigned to a CHAR variable?
The sample code below simply echoes back the input char by char into a variable just to demonstrate.
When called using my_test('1 2'), which is 6 chars total input, containing 4 spaces, the result is only '12' two chars returned.
If I change the definition of ch to CHAR(2), CHAR(4), CHAR(10) ...same result.
If I change the definition of ch to VARCHAR(1) it works as I would expect, returning all 6 original chars.
To me, this seems like a bug with CHAR handling on MySQL. The same code using CHAR(1), with minor syntax changes, works fine on Oracle and SQL Server.
Using MySQL 5.7.21
/*
select my_test('1 2'); -- Total len = 6 (contains 4 spaces)
*/
DROP FUNCTION IF EXISTS my_test;
DELIMITER //
CREATE FUNCTION my_test( original VARCHAR(100) )
RETURNS VARCHAR(100)
DETERMINISTIC
BEGIN
DECLARE ch CHAR(1); -- Will only recognize SPACE if changed to VARCHAR!
DECLARE idx INT DEFAULT 1;
DECLARE len INT;
DECLARE result VARCHAR(100) DEFAULT '';
SET len = CHAR_LENGTH(original);
WHILE idx <= len DO
SET ch = SUBSTR(original, idx, 1);
SET result = CONCAT(result, ch);
SET idx = idx + 1;
END WHILE;
RETURN result;
END;
The issue you are seeing can be resolved by ensuring that the function was created with sql_mode set to pad_char_to_full_length.
Solution
DROP FUNCTION IF EXISTS my_test;
SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
DELIMITER //
CREATE FUNCTION my_test( original VARCHAR(100) )
RETURNS VARCHAR(100)
DETERMINISTIC
BEGIN
DECLARE ch CHAR(1);
DECLARE idx INT DEFAULT 1;
DECLARE len INT;
DECLARE result VARCHAR(100) DEFAULT '';
SET len = CHAR_LENGTH(original);
WHILE idx <= len DO
SET ch = SUBSTR(original, idx, 1);
SET result = CONCAT(result, ch);
SET idx = idx + 1;
END WHILE;
RETURN result;
END//
delimiter ;
SET sql_mode = '';
Result
select my_test('1 2');
+-----------------+
| my_test('1 2') |
+-----------------+
| 1 2 |
+-----------------+
Explanation
If you have CHAR(5) and you stored A in it, MySQL will internally store it as A. However, when you select from that CHAR(5), right-padded spaces aren't retrieved UNLESS pad_char_to_full_length sql_mode is set. By default, your sql_mode is likely to not have pad_char_to_full_length.
According to documentation:
The length of a CHAR column is fixed to the length that you declare
when you create the table. The length can be any value from 0 to 255.
When CHAR values are stored, they are right-padded with spaces to the
specified length. When CHAR values are retrieved, trailing spaces are
removed unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.
So, SET ch = SUBSTR(original, idx, 1); stores empty space in ch. However, when extracting information from ch MySQL will truncate spaces from the end resulting in empty string.
sql_mode
You can see your current sql_mode by typing select ##sql_mode. Also see How can I see the specific value of the sql_mode?.
You can review documentation of sql_mode in MySQL's doc.
Solution without using sql_mode of pad_char_to_full_length
Instead of doing this:
SET ch = SUBSTR(original, idx, 1);
SET result = CONCAT(result, ch);
Do this instead:
SET result = CONCAT(result, SUBSTR(original, idx, 1));
This single line assignment will avoid the CHAR trickery and give you intended results even with sql_mode does not have pad_char_to_full_length.

It is possible to sort particular cell value in mySql?

I need to sort below cell values using mysql
Example:
cell contain red,blue,green
But I want that in alphabetic order.
Steps to do this,
1.First you need to make a procedure call for sorting values
2.Call your procedure then
Here is the code to create mysql procedure
-- sort comma separated substrings with unoptimized bubble sort
DROP FUNCTION IF EXISTS sortString;
DELIMITER |
CREATE FUNCTION sortString(inString TEXT) RETURNS TEXT
BEGIN
DECLARE delim CHAR(1) DEFAULT ','; -- delimiter
DECLARE strings INT DEFAULT 0; -- number of substrings
DECLARE forward INT DEFAULT 1; -- index for traverse forward thru substrings
DECLARE backward INT; -- index for traverse backward thru substrings, position in calc. substrings
DECLARE remain TEXT; -- work area for calc. no of substrings
-- swap areas TEXT for string compare, INT for numeric compare
DECLARE swap1 TEXT; -- left substring to swap
DECLARE swap2 TEXT; -- right substring to swap
SET remain = inString;
SET backward = LOCATE(delim, remain);
WHILE backward != 0 DO
SET strings = strings + 1;
SET backward = LOCATE(delim, remain);
SET remain = SUBSTRING(remain, backward+1);
END WHILE;
IF strings < 2 THEN RETURN inString; END IF;
REPEAT
SET backward = strings;
REPEAT
SET swap1 = SUBSTRING_INDEX(SUBSTRING_INDEX(inString,delim,backward-1),delim,-1);
SET swap2 = SUBSTRING_INDEX(SUBSTRING_INDEX(inString,delim,backward),delim,-1);
IF swap1 > swap2 THEN
SET inString = TRIM(BOTH delim FROM CONCAT_WS(delim
,SUBSTRING_INDEX(inString,delim,backward-2)
,swap2,swap1
,SUBSTRING_INDEX(inString,delim,(backward-strings))));
END IF;
SET backward = backward - 1;
UNTIL backward < 2 END REPEAT;
SET forward = forward +1;
UNTIL forward + 1 > strings
END REPEAT;
RETURN inString;
END |
DELIMITER ;
To make procedure call just you have to use,
-- example call:
SET #Xstr = "red,blue,green"; // for query purpose only you need to write within (SQL Query here for that row)
SELECT sortString(#Xstr) AS s1
Please see the documentation guide map
Click here to read
Also there is an alternative way to do if you are interested to study is that about FIND_IN_SET, please you can find some idea from one of the question from stackoverflow. Click here to read
You can create a function which sorts the items in the column:
create function f_comma_list_order ( t text )
returns text
begin
declare v_c int;
drop temporary table if exists tmp;
create temporary table tmp ( v text );
set v_c = 1;
while( v_c > 0 ) do
select locate(',', t) into v_c;
if (v_c>0) then
insert into tmp select substr(t, 1, v_c-1);
set t = substr(t, v_c+1);
else
insert into tmp values (t);
end if;
end while;
select group_concat(v order by v) into t
from tmp;
return t;
end
and then call the function:
select f_comma_list_order('red,green,blue')

Convert CAP_FIRST Function from MySQL to PostgreSQL

A couple of years ago, I found this CAP_FIRST function for MySQL:
CREATE FUNCTION CAP_FIRST (input VARCHAR(255))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE len INT;
DECLARE i INT;
SET len = CHAR_LENGTH(input);
SET input = LOWER(input);
SET i = 0;
WHILE (i < len) DO
IF (MID(input,i,1) = ' ' OR MID(input,i,1) = '-' OR i = 0) THEN
IF (i < len) THEN
SET input = CONCAT(
LEFT(input,i),
UPPER(MID(input,i + 1,1)),
RIGHT(input,len - i - 1)
);
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN input;
END;
I have used this function extensively to convert people's names to proper capitalization for reports, like so:
UPDATE DataImport
SET FirstName = CAP_FIRST(FirstName);
UPDATE DataImport
SET LastName = CAP_FIRST(LastName);
Now I am migrating to PostgreSQL, and need this same functionality there. Do I need to convert this function to PostgreSQL, or is there a better way to get the same results? If I do need to convert, can someone help me?
It turns out that PostgreSQL has a builtin function initcap() that does this for me like so:
UPDATE DataImport
SET FirstName = initcap(FirstName);
UPDATE DataImport
SET LastName = initcap(LastName);

How do I get an alphanumeric / alphaid (base 62) youtube-like id string from an integer column in SQL?

How do I convert an id INT column in MySQL to a base 62 alphanumeric string?
Basically I really need a MySQL implementation of the following:
http://kvz.io/blog/2009/06/10/create-short-ids-with-php-like-youtube-or-tinyurl/
I needed to do this for larger-than-INT binary data (base-256 UUIDs specifically), so I created this stored function:
DELIMITER //
CREATE FUNCTION base62(x VARBINARY(16)) RETURNS VARCHAR(22) DETERMINISTIC
BEGIN
DECLARE digits CHAR(62) DEFAULT "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
DECLARE n NUMERIC(39) DEFAULT 0;
DECLARE s VARCHAR(22) DEFAULT "";
DECLARE i INT DEFAULT 1;
WHILE i <= LENGTH(x) DO
SET n = n * 256 + ORD(SUBSTR(x, i, 1));
SET i = i + 1;
END WHILE;
WHILE n > 0 DO
SET s = CONCAT(SUBSTR(digits, (n MOD 62) + 1, 1), s);
SET n = FLOOR(n / 62);
END WHILE;
RETURN s;
END//
You can remove the first loop if you've already got a numeric type. You may also wish to adjust the alphabet; for instance, the base-64 puts letters before numbers.
It is better to generate id using uuid or use auto increment to store in mysql but encode decode it while using it for front end. You can use this library to generate non sequential unique id's from numbers.
http://hashids.org
I've been using this MySQL function successfully. It takes a BIGINT parameter as input and returns BASE62 Youtube style ID's.
CREATE FUNCTION `i2s`(
`_n` BIGINT
)
RETURNS tinytext CHARSET latin1 COLLATE latin1_general_cs
LANGUAGE SQL
NOT DETERMINISTIC
NO SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
declare d char(62) default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
declare s tinytext default '0';
declare i int default 1;
if _n>0 then set s=''; end if;
while _n > 0 do
set s = concat(substr(d, (_n mod 62) + 1, 1),s);
set _n = floor(_n / 62);
end while;
return s;
END

MySQL: Change case for compound names

I have a dataset where names are in all uppercase, and need to convert them to proper case for reports. I found here in Stackoverflow the following code:
SET LastName = CONCAT(UPPER(SUBSTRING(LastName, 1, 1)),LOWER(SUBSTRING(LastName, 2)));
This works great for simple last names:
SMITH --> Smith
JONES --> Jones
But not so good for compound names:
VAN DYKE --> Van dyke
CARTER-SMITH --> Carter-smith
Has anyone developed some MySQL code that can do the following:
VAN DYKE --> Van Dyke
CARTER-SMITH --> Carter-Smith
I know that we will not be able to catch every possible situation, but I hope someone has at least tackled converting names that are separated by dashes or spaces.
I saw this problem on another site, check it out: http://www.thingy-ma-jig.co.uk/blog/30-09-2010/mysql-how-upper-case-words
He uses a function. So I hope you have the rights to create one.
You guys are so helpful! The answer I came up with was:
CREATE FUNCTION CAP_FIRST (input VARCHAR(255))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE len INT;
DECLARE i INT;
SET len = CHAR_LENGTH(input);
SET input = LOWER(input);
SET i = 0;
WHILE (i < len) DO
IF (MID(input,i,1) = ' ' OR MID(input,i,1) = '-' OR i = 0) THEN
IF (i < len) THEN
SET input = CONCAT(
LEFT(input,i),
UPPER(MID(input,i + 1,1)),
RIGHT(input,len - i - 1)
);
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN input;
END;
And it works beautifully!
You would think that the world’s most popular open source database, as MySQL like to call itself, would have a function for making items title case (where the first letter of every word is capitalized). Sadly it doesn’t.
This is the best solution i found Just create a stored procedure / function that will do the trick
mysql>
DROP FUNCTION IF EXISTS proper;
SET GLOBAL log_bin_trust_function_creators=TRUE;
DELIMITER |
CREATE FUNCTION proper( str VARCHAR(128) )
RETURNS VARCHAR(128)
BEGIN
DECLARE c CHAR(1);
DECLARE s VARCHAR(128);
DECLARE i INT DEFAULT 1;
DECLARE bool INT DEFAULT 1;
DECLARE punct CHAR(17) DEFAULT ' ()[]{},.-_!#;:?/';
SET s = LCASE( str );
WHILE i <= LENGTH( str ) DO
BEGIN
SET c = SUBSTRING( s, i, 1 );
IF LOCATE( c, punct ) > 0 THEN
SET bool = 1;
ELSEIF bool=1 THEN
BEGIN
IF c >= 'a' AND c <= 'z' THEN
BEGIN
SET s = CONCAT(LEFT(s,i-1),UCASE(c),SUBSTRING(s,i+1));
SET bool = 0;
END;
ELSEIF c >= '0' AND c <= '9' THEN
SET bool = 0;
END IF;
END;
END IF;
SET i = i+1;
END;
END WHILE;
RETURN s;
END;
|
DELIMITER ;
then
update table set LastName = properword(LastName)
or
select proper( LastName ) as properLastName
from table