Copying emojis in text from MySQL to SQL Server - mysql

I am copying data from MySQL to SQL Server using a linked server.
SELECT comment FROM openquery(my_linked_server, 'SELECT comment FROM search_data');
The text in the MySQL table column is xxx 🤘 xxx . By time I receive it in SQL Server it is xxx 🤘 xxx. The MySQL table is utf8mb4, and I have set up the ODBC config for the linked server to use this. I am using MySQL ODBC 5.3.13
Any advice would be appreciated. the SQL Server version is 2016, I have seen examples to put do
select N'🤘'
etc, but don't know how to apply this to the above query.
🤘 seems to be 4 characters
ð = u00f0 , dec = 240
Ÿ = u0178 , dec = 276
¤ = u00a4 , dec = 164
˜ = u02dc , dec = 732
🤘= ud83e, dec = 55358
funny enough this doesnt even work
select nchar(unicode(N'🤘')),unicode(N'🤘')
returning � symbol

The Unicode codepoint of the character 🤘 is U+1F918, which means it is outside the Basic Multilingual Plane (BMP) of Unicode, which covers codepoints up to U+FFFF.
To process Unicode characters outside the BMP, you need to apply collations supporting Supplementary Characters, named as *_SC:
SQL Server 2012 (11.x) introduced a new family of supplementary character (_SC) collations that can be used with the nchar, nvarchar, and sql_variant data types to represent the full Unicode character range (000000–10FFFF)
Compare the results of this SQL statement
select
nchar(unicode(N'🤘' collate Latin1_General_100_CI_AS_SC)) as EmojiSC,
unicode(N'🤘' collate Latin1_General_100_CI_AS_SC) as EmojiSCUnicode,
cast(N'🤘' as varbinary) as EmojiBinary,
cast(nchar(unicode(N'🤘')) as varbinary) as EmojiConvBinary,
unicode(N'🤘') as EmojiUnicode
as run against a database using Latin1_General_CI_AS
EmojiSC EmojiSCUnicode EmojiBinary EmojiConvBinary EmojiUnicode
NULL 129304 0x3ED818DD 0x3ED8 55358
versus a database set to Latin1_General_100_CI_AI_SC
EmojiSC EmojiSCUnicode EmojiBinary EmojiConvBinary EmojiUnicode
🤘 129304 0x3ED818DD 0x3ED818DD 129304
Why do you see "🤘"?
The UTF-8 encoding of U+1F918 is 0xF0 0x9F 0xA4 0x98, and the characters are the result of interpreting these codes as ANSI characters.
Why do you see "�"?
The character � is the Unicode REPLACEMENT CHARACTER and is
used to replace an unknown, unrecognized or unrepresentable character
and that's because U+D83E is not a valid Unicode codepoint, but the first word of the codepoint encoded as UTF-16 (0xD83E 0xDD18).
Check what is stored, not what is displayed
Displaying Unicode data can be tricky, and the most efficient way to find out what's going on under the hood is to look at the bytes. In TSQL, use cast(... as varbinary) to analyze where Unicode data manipulation goes wrong.

i made a solution and posting so others dont spend the day doing the same
select ab_test.dbo.GetEmojisInString('👌💖🤷â€â™‚ï¸ðŸ˜ŽðŸ±â€ðŸ’»ðŸ˜‰â¤ðŸ±â€ðŸ‘¤ðŸ¤žðŸ¤£ðŸ‘💕✌ðŸ±â€ðŸðŸ’‹ðŸŽ‚🎉🤦â€â™‚ï¸ðŸ˜ŠðŸŒ¹ðŸ‘ðŸ±â€ðŸ‰ðŸŽ¶ðŸ˜ðŸ¤¦â€â™€ï¸ðŸ˜ðŸ™ŒðŸ±â€ðŸš€ðŸ˜œðŸ˜˜ðŸ±â€ðŸ‘“😢😒🤳😂')
will return
👌💖🤷‍♂️😎🐱‍💻😉❤🐱‍👤🤞🤣👏💕✌🐱‍🏍💋🎂🎉🤦‍♂️😊🌹👍🐱‍🐉🎶😍🤦‍♀️😁🙌🐱‍🚀😜😘🐱‍👓😢😒🤳😂
there are 5 functions below, probrably not perfect and maybe shorter / better ways but this functions. if any bugs let me know.
NOTE : i had to split over two databases as for this to work the collation needs to have _CS, and the bi_library database below in my solution i could not change this as database was locked so for now just created a ab_test db.
USE [bi_library]
GO
CREATE FUNCTION [dbo].[GetDecimalFromOtherBase]
( #p_in_value varchar(100),
#p_from_base int -- ie 16 for hex, 8 for octal, 2 for bin
) returns int
as
begin
declare #l_in_value varchar(100) = reverse(#p_in_value) -- spin backwards as maths works in easier this way
declare #l_from_base varchar(100) = #p_from_base--#p_from_base --= #p_in_value
declare #l_pos int = 1
declare #l_char char(1)
declare #l_val int = 0
declare #l_total int = 0
while #l_pos<= len(#l_in_value)
begin
set #l_char = substring(#l_in_value,#l_pos,1)
if isnumeric(#l_char)=0
begin
set #l_val = ascii(#l_char)-55 -- convert A to 10, F to 15 etc
end
else
begin
set #l_val = #l_char
end
set #l_total = #l_total + (power(#l_from_base,#l_pos-1)*#l_val)
set #l_pos=#l_pos+1
end
return #l_total
end
GO
CREATE FUNCTION [dbo].[GetOtherBaseFromDecimal]
( #p_in_value int,
#p_to_base int -- ie 16 for hex, 8 for octal, 2 for bin
) returns varchar(100)
as
begin
-- convert decimal to other base
declare #l_dec int = #p_in_value
declare #l_ret_str varchar(100) = ''
declare #l_rem int = 0
declare #l_rem_char char(1) = '?'
while #l_dec > 0
begin
set #l_rem = #l_dec % #p_to_base
if #l_rem >= 10
begin
set #l_rem_char = char(55+#l_rem)
end
else
begin
set #l_rem_char = cast(#l_rem as varchar)
end
set #l_ret_str = #l_ret_str + #l_rem_char
set #l_dec = #l_dec / #p_to_base
end
return reverse(#l_ret_str)
end
GO
CREATE FUNCTION [dbo].[GetBaseFromOtherBase]
( #p_in_value varchar(100),
#p_in_base bigint, -- ie 16 for hex, 8 for octal, 2 for bin
#p_to_base bigint -- ie 16 for hex, 8 for octal, 2 for bin
) returns varchar(100)
as
begin
return bi_library.dbo.GetOtherBaseFromDecimal(bi_library.dbo.GetDecimalFromOtherBase(#p_in_value,#p_in_base),#p_to_base)
end
GO
USE [ab_test]
GO
ALTER function [dbo].[GetEmojisInString] (#p_in_string nvarchar(max)) returns nvarchar(max)
as
begin
declare #l_string varchar(1000) = #p_in_string --'✌ðŸ˜ðŸ’‹ðŸ¤·â€â™‚ï¸ðŸ¤³ðŸ±â€ðŸ‘“ðŸ±â€ðŸš€ðŸ±â€ðŸ‰ðŸ˜ŠðŸ’•ðŸ¤žðŸ˜‰ðŸ‘ŒðŸ¤¦â€â™€ï¸ðŸ±â€ðŸðŸ’–😒😘ðŸ˜ðŸ‘🤦â€â™‚ï¸ðŸ‘ðŸ±â€ðŸ‘¤ðŸ±â€ðŸ’»ðŸ™ŒðŸŽ‚😎😂😢😜🎶🌹🎉🤣â¤ðŸ¤·â€â™€ï¸'
declare #l_pos int = 1
declare #l_char varchar(1)
declare #l_cont_extended_ascii int = 0
declare #l_byte1_hex varchar(2)
declare #l_byte2_hex varchar(2)
declare #l_byte3_hex varchar(2)
declare #l_byte4_hex varchar(2)
declare #l_hex_char varchar(2)
declare #l_str nvarchar(max) = ''
declare #l_dec_value_found int
while #l_pos <= len(#l_string)
begin
set #l_char = substring(#l_string,#l_pos,1)
--print(ascii(#l_char))
if ascii(#l_char)>=128
begin
set #l_cont_extended_ascii = #l_cont_extended_ascii+1
--print(#l_char)
set #l_hex_char = bi_library.dbo.GetOtherBaseFromDecimal(ascii(#l_char),16)
if #l_cont_extended_ascii = 1
begin
set #l_byte1_hex = #l_hex_char
--print('set byte 1')
end
else if #l_cont_extended_ascii = 2
begin
--print('set byte 2')
set #l_byte2_hex = #l_hex_char
set #l_dec_value_found = bi_library.dbo.GetDecimalFromOtherBase(
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte1_hex,16,2)),1,6))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte2_hex,16,2)),1,6))
,2)
if #l_dec_value_found between 128/*U+0080*/ and 2047/*U+07FF */
begin
--print('2 byte emoji found')
set #l_str = #l_str+coalesce(nchar(#l_dec_value_found),'?')
set #l_cont_extended_ascii = 0
end
end
else if #l_cont_extended_ascii = 3
begin
--print('set byte 3')
set #l_byte3_hex = #l_hex_char
set #l_dec_value_found = bi_library.dbo.GetDecimalFromOtherBase(
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte1_hex,16,2)),1,4))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte2_hex,16,2)),1,6))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte3_hex,16,2)),1,6))
,2)
if #l_dec_value_found between 2048/*U+0800*/ and 65535/*U+FFFF*/
begin
--print('3 byte emoji found')
set #l_str = #l_str+coalesce(nchar(#l_dec_value_found),'?')
set #l_cont_extended_ascii = 0
end
--print(#l_str)
end
else if #l_cont_extended_ascii = 4 begin set #l_byte4_hex = #l_hex_char
set #l_dec_value_found = bi_library.dbo.GetDecimalFromOtherBase(
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte1_hex,16,2)),1,3))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte2_hex,16,2)),1,6))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte3_hex,16,2)),1,6))+
reverse(substring(reverse(bi_library.dbo.GetBaseFromOtherBase(#l_byte4_hex,16,2)),1,6))
,2)
if #l_dec_value_found between 65536/*U+10000*/ and 1114111/*U+10FFFF*/
begin
--print('4 byte emoji found')
set #l_str = #l_str+coalesce(nchar(#l_dec_value_found),'?')
set #l_cont_extended_ascii = 0
end
else
begin
--print('out of range byte emoji found')
set #l_str = #l_str+#l_char
end
--print(#l_str)
--end
set #l_cont_extended_ascii = 0
end
end
else
begin
--print('snapping')
set #l_str = #l_str+#l_char
set #l_cont_extended_ascii = 0
--print(#l_str)
end
set #l_pos = #l_pos+1
end
--print(#l_str)
return #l_str
end
CREATE function [dbo].[HasEmojisInString] (#p_in_string nvarchar(max)) returns int
as
begin
declare #l_string_emojified varchar(1000)
set #l_string_emojified = dbo.GetEmojisInString(#p_in_string)
if #l_string_emojified <> #p_in_string
begin
return 1
end
return 0
end
GO

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.

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

sql create function error code 1064

I need to create a function that I found here: http://vyaskn.tripod.com/code/propercase.txt It converts text to "ProperCase," first letter of every word to uppercase.
CREATE FUNCTION PROPERCASE
(
-- The string to be converted to proper case
#input VARCHAR( 8000 )
)
-- This function returns the proper case string of varchar type
RETURNS VARCHAR( 8000 )
AS
BEGIN
IF #input IS NULL
BEGIN
-- Just return NULL if input string is NULL
RETURN NULL
END
-- Character variable declarations
DECLARE #output VARCHAR( 8000 )
-- Integer variable declarations
DECLARE #ctr INT, #len INT, #found_at INT
-- Constant declarations
DECLARE #LOWER_CASE_a INT, #LOWER_CASE_z INT, #Delimiter CHAR(3), #UPPER_CASE_A INT, #UPPER_CASE_Z INT
-- Variable/Constant initializations
SET #ctr = 1
SET #len = LEN(#input)
SET #output = ''
SET #LOWER_CASE_a = 97
SET #LOWER_CASE_z = 122
SET #Delimiter = ' ,-'
SET #UPPER_CASE_A = 65
SET #UPPER_CASE_Z = 90
WHILE #ctr <= #len
BEGIN
-- This loop will take care of reccuring white spaces
WHILE CHARINDEX(SUBSTRING(#input,#ctr,1), #Delimiter) > 0
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
SET #ctr = #ctr + 1
END
IF ASCII(SUBSTRING(#input,#ctr,1)) BETWEEN #LOWER_CASE_a AND #LOWER_CASE_z
BEGIN
-- Converting the first character to upper case
SET #output = #output + UPPER(SUBSTRING(#input,#ctr,1))
END
ELSE
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
END
SET #ctr = #ctr + 1
WHILE CHARINDEX(SUBSTRING(#input,#ctr,1), #Delimiter) = 0 AND (#ctr <= #len)
BEGIN
IF ASCII(SUBSTRING(#input,#ctr,1)) BETWEEN #UPPER_CASE_A AND #UPPER_CASE_Z
BEGIN
SET #output = #output + LOWER(SUBSTRING(#input,#ctr,1))
END
ELSE
BEGIN
SET #output = #output + SUBSTRING(#input,#ctr,1)
END
SET #ctr = #ctr + 1
END
END
RETURN #output
END
I need a function to do that, but it's giving me error...
You are using MySQL, but the syntax you have is for SQL Server. Please read the documentation about MySQL's syntax and convert your procedure to use that syntax. The major constructs are the same, but the syntax is a little different. Here are some things to start with:
Local variables never start with #.
IF is condition followed by THEN followed by any number of lines of code, followed by END IF. The BEGIN...END construct is not used for IF statements in MySQL.
The functions are different. You won't use CHARINDEX but instead INSTR.
Here's the relevant MySQL documentation: http://dev.mysql.com/doc/refman/5.5/en/stored-routines-syntax.html and http://dev.mysql.com/doc/refman/5.5/en/sql-syntax-compound-statements.html.

Replace character in a string with space

I need the following output in SQL server 2008
problem is to remove numbers from string
i.e column name is associated_id which is nvarchar type and value could be (23,34,45)
But I want the following result (23,45).
May this help
CREATE FUNCTION [dbo].[GetAlphabetFromAlphaNumeric]
(#strAlphaNumeric VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[0-9]%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[0-9]%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END

Check constraint to validate IP address field

I'm working on a project involving C# and a SQL Server 2008 database.
In one of the tables, I have a field (nvarchar(15)) which will contain an IP address.
I'd like to add a check constraint which will validate that the input value is actually an IP address.
I wanted to use a regex to do that, but it seems that this feature is not supported by default. I saw things about writing a customm dll with UDF inside (MSDN tutorial), but I don't really understand how it works (i.e. where should I place the dll ?)
Is there a "simple" way to add such a constraint ?
Any solution is welcome.
Thanks in advance !
There are several way of doing this - the most performant one would probably be a CLR function in the database.
This is because SQL has fairly poor text manipulation tooling and no native RegEx in SQL Server.
As other have said, this is better handled by an application before insertion to the DB.
It shouldn't be handled in the database, it should be handled first and foremost in the application.
There's no harm in then adding a check to the database, but leaving it up to the DB to filter input is very sketchy.
The easiest way I can think of is to create a function like fnCheckIP and use this function in the constraint.
There's no need to use UDF.
create function fnCheckIP(#ip varchar(15)) returns bit
AS
begin
if (#ip is null)
return null
declare #num1 int
declare #num varchar(15)
declare #pos int
while (#ip is not null)
begin
set #pos = IsNull(NullIf(charindex('.', #ip), 0), Len(#ip) + 1)
set #num = substring(#ip, 1, #pos - 1)
if (isnumeric(#num) = 0) or (not cast(#num as int) between 0 and 255)
return cast(0 as bit)
if (len(#ip) - #pos <= 0)
set #ip = null
else
set #ip = NullIf(substring(#ip, #pos + 1, len(#ip) - #pos), '')
end
return cast (1 as bit)
end
go
select dbo.fnCheckIP('127.0.0.1')
select dbo.fnCheckIP('127.0.0.300')
This solution is similar to Paulo's but using either approach will require getting rid of the comma character because isnumeric allows commas which will throw a cast to int error.
CREATE FUNCTION fn_ValidateIP
(
#ip varchar(255)
)
RETURNS int
AS
BEGIN
DECLARE #Result int = 0
IF
#ip not like '%,%' and
len(#ip) <= 15 and
isnumeric(PARSENAME(#ip,4)) = 1 and
isnumeric(PARSENAME(#ip,3)) = 1 and
isnumeric(PARSENAME(#ip,2)) = 1 and
isnumeric(PARSENAME(#ip,1)) = 1 and
cast(PARSENAME(#ip,4) as int) between 1 and 255 and
cast(PARSENAME(#ip,3) as int) between 0 and 255 and
cast(PARSENAME(#ip,2) as int) between 0 and 255 and
cast(PARSENAME(#ip,1) as int) between 0 and 255
set #Result = 1
ELSE
set #Result = 0
RETURN #Result
END
select dbo.fn_ValidateIP('127.0.0.1')
This may not be entirely practical, but one way would be to store the converted string ###-###-###-### into a binary(4) data type. Let the interface fuss around with hyphens and deal with converting the four numbers to binary and back (and this could probably even be done by a caluclated column.) A bit extreme, yes, but with binary(4) you will always be able to turn it into an IP address.
At last about 10 yrs after Oracle, sqlserver got native compilation (with limitations)
ALTER function fn_ValidateIPv4
(
#ip varchar(255)
)
RETURNS int
--WITH EXECUTE AS OWNER, SCHEMABINDING, NATIVE_COMPILATION
AS
BEGIN
--ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
/* only sql2016 native Compilation **/
DECLARE #len_ip as int;
SET #len_ip = len(#ip);
DECLARE #firstBlock varchar(4) = '';
DECLARE #secondBlock varchar(4) = '';
DECLARE #thirdBlock varchar(4) = '';
DECLARE #fourthBlock varchar(4) = '';
DECLARE #countDot as smallint = 0;
DECLARE #l_i as smallint = 0;
DECLARE #l_curChar varchar(1) = 'X';
DECLARE #Result int = 0
IF (#len_ip <= 15)
BEGIN
WHILE (#l_i < #len_ip)
BEGIN
set #l_i += 1;
set #l_curChar = substring(#ip,#l_i,1);
if #l_curChar = '.'
SET #countDot += 1
ELSE
BEGIN
IF #l_curChar IN ( '0','1','2','3','4','5','6','7','8','9' )
BEGIN
IF #countDot = 0
SET #firstBlock = #firstBlock + #l_curChar;
IF #countDot = 1
SET #secondBlock = #secondBlock + #l_curChar;
IF #countDot = 2
SET #thirdBlock = #thirdBlock + #l_curChar;
IF #countDot = 3
SET #fourthBlock = #fourthBlock + #l_curChar;
IF #countDot > 3
set #firstBlock = 'AAA'; -- force error
END
ELSE set #firstBlock = 'AAA'; -- force error
END;
END;
IF ( #countDot = 3 and
cast(#fourthBlock as int) between 1 and 255 and
cast(#thirdBlock as int) between 0 and 255 and
cast(#secondBlock as int) between 0 and 255 and
cast(#firstBlock as int) between 0 and 255
)
set #Result = 1;
END;
/*
select dbo.fn_ValidateIPv4( '127.0.0.258' );
*/
RETURN #Result
END;
I had to remove not de-supported built functions isnumeric etc...