Is there any function to encode HTML strings in T-SQL? I have a legacy database which contains dodgey characters such as '<', '>' etc. I can write a function to replace the characters but is there a better way?
I have an ASP.Net application and when it returns a string it contains characters which cause an error. The ASP.Net application is reading the data from a database table. It does not write to the table itself.
We have a legacy system that uses a trigger and dbmail to send HTML encoded email when a table is entered, so we require encoding within the email generation. I noticed that Leo's version has a slight bug that encodes the & in < and > I use this version:
CREATE FUNCTION HtmlEncode
(
#UnEncoded as varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #Encoded as varchar(500)
--order is important here. Replace the amp first, then the lt and gt.
--otherwise the < will become <
SELECT #Encoded =
Replace(
Replace(
Replace(#UnEncoded,'&','&'),
'<', '<'),
'>', '>')
RETURN #Encoded
END
GO
It's a bit late, but anyway, here the proper ways:
HTML-Encode (HTML encoding = XML encoding):
DECLARE #s NVARCHAR(100)
SET #s = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>'
SELECT (SELECT #s FOR XML PATH(''))
HTML-encode in a query:
SELECT
FIELD_NAME
,(SELECT FIELD_NAME AS [text()] FOR XML PATH('')) AS FIELD_NAME_HtmlENcoded
FROM TABLE_NAME
HTML-Decode:
SELECT CAST('<root>' + '<root>Test&123' + '</root>' AS XML).value(N'(root)[1]', N'varchar(max)');
If you want to do it properly, you can use a CLR-stored procedure.
However, it gets a bit complicated, because you can't use the System.Web-Assembly in CLR-stored-procedures (so you can't do System.Web.HttpUtility.HtmlDecode(htmlEncodedStr);). So you have to write your own HttpUtility class, which I wouldn't recommend, especially for decoding.
Fortunately, you can rip System.Web.HttpUtility out of the mono sourcecode (.NET for Linux). Then you can use HttpUtility without referencing system.web.
Then you write this CLR-Stored-Procedure:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
//using Microsoft.SqlServer.Types;
namespace ClrFunctionsLibrary
{
public class Test
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString HtmlEncode(SqlString sqlstrTextThatNeedsEncoding)
{
string strHtmlEncoded = System.Web.HttpUtility.HtmlEncode(sqlstrTextThatNeedsEncoding.Value);
SqlString sqlstrReturnValue = new SqlString(strHtmlEncoded);
return sqlstrReturnValue;
}
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString HtmlDecode(SqlString sqlstrHtmlEncodedText)
{
string strHtmlDecoded = System.Web.HttpUtility.HtmlDecode(sqlstrHtmlEncodedText.Value);
SqlString sqlstrReturnValue = new SqlString(strHtmlDecoded);
return sqlstrReturnValue;
}
// ClrFunctionsLibrary.Test.GetPassword
//[Microsoft.SqlServer.Server.SqlFunction]
//public static SqlString GetPassword(SqlString sqlstrEncryptedPassword)
//{
// string strDecryptedPassword = libPortalSecurity.AperturePortal.DecryptPassword(sqlstrEncryptedPassword.Value);
// SqlString sqlstrReturnValue = new SqlString(sqlstrEncryptedPassword.Value + "hello");
// return sqlstrReturnValue;
//}
public const double SALES_TAX = .086;
// http://msdn.microsoft.com/en-us/library/w2kae45k(v=vs.80).aspx
[SqlFunction()]
public static SqlDouble addTax(SqlDouble originalAmount)
{
SqlDouble taxAmount = originalAmount * SALES_TAX;
return originalAmount + taxAmount;
}
} // End Class Test
} // End Namespace ClrFunctionsLibrary
And register it:
GO
/*
--http://stackoverflow.com/questions/72281/error-running-clr-stored-proc
-- For unsafe permission
EXEC sp_changedbowner 'sa'
ALTER DATABASE YOUR_DB_NAME SET TRUSTWORTHY ON
GO
*/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[HtmlEncode]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[HtmlEncode]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[HtmlDecode]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[HtmlDecode]
GO
IF EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'ClrFunctionsLibrary' and is_user_defined = 1)
DROP ASSEMBLY [ClrFunctionsLibrary]
GO
--http://msdn.microsoft.com/en-us/library/ms345101.aspx
CREATE ASSEMBLY [ClrFunctionsLibrary]
AUTHORIZATION [dbo]
FROM 'D:\username\documents\visual studio 2010\Projects\ClrFunctionsLibrary\ClrFunctionsLibrary\bin\Debug\ClrFunctionsLibrary.dll'
WITH PERMISSION_SET = UNSAFE --EXTERNAL_ACCESS --SAFE
;
GO
CREATE FUNCTION [dbo].[HtmlDecode](#value [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
-- [AssemblyName].[Namespace.Class].[FunctionName]
EXTERNAL NAME [ClrFunctionsLibrary].[ClrFunctionsLibrary.Test].[HtmlDecode]
GO
CREATE FUNCTION [dbo].[HtmlEncode](#value [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
-- [AssemblyName].[Namespace.Class].[FunctionName]
EXTERNAL NAME [ClrFunctionsLibrary].[ClrFunctionsLibrary.Test].[HtmlEncode]
GO
/*
EXEC sp_CONFIGURE 'show advanced options' , '1';
GO
RECONFIGURE;
GO
EXEC sp_CONFIGURE 'clr enabled' , '1'
GO
RECONFIGURE;
GO
EXEC sp_CONFIGURE 'show advanced options' , '0';
GO
RECONFIGURE;
*/
Afterwards, you can use it like normal functions:
SELECT
dbo.HtmlEncode('helloäÖühello123') AS Encoded
,dbo.HtmlDecode('helloäÖühello123') AS Decoded
Anybody who just copy-pastes, please note that for efficiency reasons, you would use
public const double SALES_TAX = 1.086;
// http://msdn.microsoft.com/en-us/library/w2kae45k(v=vs.80).aspx
[SqlFunction()]
public static SqlDouble addTax(SqlDouble originalAmount)
{
return originalAmount * SALES_TAX;
}
if you'd use this function in production.
See here for the edited mono classes:
http://pastebin.com/pXi57iZ3
http://pastebin.com/2bfGKBte
You need to define NET_2_0 in the build options
You shouldn't fix the string in SQL. A better way is to use a function in ASP.net called HtmlEncode, this will cook the special characters that cause the issues you're seeing see the example below. I hope this helps.
string htmlEncodedStr = System.Web.HttpUtility.HtmlEncode(yourRawStringVariableHere);
string decodedRawStr = System.Web.HttpUtility.HtmlDecode(htmlEncodedStr);
Edit:
Since you're data binding this from a datatable. Use an inline expression to call HTMLEncode in the markup of the GridView or whatever control your using and this will still satisfy your data binding requirement. See example below. Alternativly you can loop every record in the data table object and update each cell with the html encoded string prior to data binding.
<%# System.Web.HttpUtility.HtmlEncode(Eval("YourColumnNameHere")) %>
I don't think data in a database should know or care about the user interface. Display issues should be handled by the presentation layer. I wouldn't want to see any HTML mingled into the database.
You can simply use 'XML PATH in your query'. For example;
DECLARE #encodedString VARCHAR(MAX)
SET #encodedString = 'give your html string you want to encode'
SELECT #encodedString
SELECT (SELECT #encodedString FOR XML PATH(''))
Now as your wish you can you this in your own sql function. Hope this will help.
If you're displaying a string on the web, you can encode it with Server.HTMLEncode().
If you're storing a string in the database, make sure the database field is "nchar", instead of "char". That will allow it to store unicode strings.
If you can't control the database, you can "flatten" the string to ASCII with Encoding.ASCII.GetString.
I haven't tried this solution myself but what I would try is utilise the sql server / .NET CLR integration and actually call the C# HTMLEncode function from the T-SQL.
This may be inefficient but I suspect it would give you the most accurate result.
My starting point for working out how to do this would be http://msdn.microsoft.com/en-us/library/ms254498%28VS.80%29.aspx
I've been trying to do this today in T-SQL, mostly for fun at this point since my requirements changed, but i figured one way out. You can use a table of unicode characters, built from the NCHAR() function or just import it, iterating from 0 to 65535 (or less if you just need the first 512 or something). Then rebuild the string. There are probably better ways to rebuild the string, but this works in a pinch.
---store unicode chars into a table so you can replace those characters withthe decimal value
`
CREATE TABLE #UnicodeCharacters(
DecimalValue INT,
UnicodeCharacter NCHAR
)
;
--loop from 0 to highest unicode value you want and dump to the table you created
DECLARE #x INT = 0;
WHILE #x <= 65535
BEGIN
BEGIN
INSERT INTO #UnicodeCharacters(DecimalValue, UnicodeCharacter)
SELECT #x,NCHAR(#x)
END
;
SET #x = #x + 1
;
END
;
--index for fast retrieval
CREATE CLUSTERED INDEX CX_UnicodeCharacter_DecimalValue ON #UnicodeCharacters(UnicodeCharacter, DecimalValue);
--this is the string that you want to html-encode...
DECLARE #String NVARCHAR(100) = N'人This is a test - Ñ';
--other vars
DECLARE #NewString NVARCHAR(100) = '';
DECLARE #Word TABLE(Character NCHAR(1));
DECLARE #Pos INT = 1;
--run through the string and check each character to see if it is outside the regex expression
WHILE #Pos <= LEN(#String)
BEGIN
DECLARE #Letter NCHAR(1) = SUBSTRING(#String,#Pos,1);
PRINT #Letter;
--rebuild the string replacing each unicode character outside the regex with &#[unicode value];
SELECT #NewString = #NewString +
CASE
WHEN #Letter LIKE N'%[0-9abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-!##$%^&*()_+-= ]%' THEN #Letter
ELSE '&#' + CAST(uc.DecimalValue AS VARCHAR(10)) + ';'
END
FROM #UnicodeCharacters uc
WHERE #Letter = uc.UnicodeCharacter COLLATE JAPANESE_UNICODE_BIN
SET #Pos += 1
END
--end result
SELECT #NewString
;
`
I know typically you would use [0-9A-Za-z], but for some reason, it considered accented characters within the scope of that expression when I did that. So I explicitly used every character that i didn't want to convert to Unicode in the expression.
Last note, I had to use a different collation to do matches on Unicode characters, because the default LATIN collation (CI or otherwise) seemed to incorrectly match on accented characters, much like the regex in the LIKE.
assign it to Text Property of label, it will be auto encoded by .NET
OK here is what I did. I created a simple function to handle it. Its far from complete but at least handles the standard <>& characters. I'll just add to it as I go along.
CREATE FUNCTION HtmlEncode
(
#UnEncoded as varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #Encoded as varchar(500)
SELECT #Encoded = Replace(#UnEncoded,'<','<')
SELECT #Encoded = Replace(#Encoded,'>','>')
SELECT #Encoded = Replace(#Encoded,'&','&')
RETURN #Encoded
END
I can then use:
Select Ref,dbo.HtmlEncode(RecID) from Customers
This gives me a HTML safe Record ID. There is probably a built in function but I can't find it.
Related
I'm running some queries, that print runtime stats from their execution.
It's done through
print('message')
used within the sql script.
I would want to see these messages while calling the procedures/scripts through pymssql.
conn = pymssql.connect(server, user, password, "tempdb")
cursor = conn.cursor()
cursor.execute("print('message')")
conn.commit()
Above script doesn't return anything, and I can't find any tips on how to get that print to show up in the console output.
Found a solution that let's me still use pymssql and get the print messages.
pymssql.Connection actually uses _mssql.MSSQLConnection internally.
This means that you can use this example by accessing that internal object.
connection = pymssql.connect(server='server_address', database='db_name')
connection._conn.set_msghandler(my_msg_handler) # Install our custom handler
where the my_msg_handler is the same type of object as in pmssql wiki.
Accessing internal objects is not ideal, but it's the only way I've found if you don't want to use a different library and need to get the SQL prints.
I don't believe there is a way, but you can refactor your SQL. For example:
DECLARE #my_var AS VARCHAR(200)
PRINT 'Setting a variable...'
SET #my_var = 'this'
PRINT 'I am some output'
SELECT * FROM my_table WHERE this = #my_var
Could be refactored to be something like this:
DECLARE #my_var AS VARCHAR(200)
DECLARE #messages AS VARCHAR(MAX)
SET #messages = #messages + 'Setting a variable...'
SET #my_var = 'this'
SET #messages = #messages + 'I am some output'
SELECT #messages, * FROM my_table WHERE this = #my_var
Good luck!
In order to print something into the console in pymssql, you don't need to put the print inside the execute function. you can simply use
print("message")
so your code will be
conn = pymssql.connect(server, user, password, "tempdb")
cursor = conn.cursor()
print("message")
conn.commit()
I have a Sqllite query
SELECT * FROM m_table WHERE LOWER(fName) = LOWER('yui!"'':;/?') AND account = '100' ORDER BY fName COLLATE NOCASE ASC ;
Above returns 0 rows; But when I use the same as below , it Works
update m_table set fName = 'yui!"'':;/? renamed' where fname='yui!"'':;/?' AND account = '100';
Any clues ?
PS: I am using LOWER to ignore case sensitive. I am using this via an android client. Hence
I am also doing StringEscapeUtils.escapeSql("folderName")
This is most likely related to the fact, that your input string contains characters in a non-ASCII-charset. from the documentation of SQLlite:
lower(X) -- The lower(X) function returns a copy of string X with all
ASCII characters converted to lower case. The default built-in lower()
function works for ASCII characters only. To do case conversions on
non-ASCII characters, load the ICU extension.
http://www.sqlite.org/lang_corefunc.html
Try to run the following statement and see what it returns.
SELECT LOWER(fName) FROM m_table WHERE fname='yui!"'':;/?' AND account = '100';
I've been evaluating Delphi XE4 (compiling against win32, but final platform will be iOS) and I need to create SQLite database (no problem with that) and make some queries. This is one query I'd like to use:
select id as _id, name, note as description from notes
And this is my code:
q := TSQLQuery.Create(nil);
try
q.SQLConnection := MainForm.sqlite1;
q.SQL.Text := sql;
q.Open;
finally
q.Free;
end;
The problem is that query returns original field names (id, name, note), not the one I used (_id, name, description).
q.Fields[0].FieldName = 'id' //it should be _id
q.Fields[2].FieldName = 'note' //it should be description
That makes all sorts of problems. Using
count(*) as myfield
returns
q.Fields[0].FieldName = Column0 //it should be myfield
that is not acceptable.
Anybody had same problems?
In order to get the proper alias names of the fields, you must add the ColumnMetaDataSupported param to the Params property of the TSQLConnectioncomponent with the False value.
In SQLite it is possible to change the case sensitive behaviour of 'LIKE' by using the commands:
PRAGMA case_sensitive_like=ON;
PRAGMA case_sensitive_like=OFF;
However in my situation I would like to execute a query, part of which is case sensitive and part of which isn't. For example:
SELECT * FROM mytable
WHERE caseSensitiveField like 'test%'
AND caseInsensitiveField like 'g2%'
Is this possible?
You can use the UPPER keyword on your case insensitive field then upper-case your like statement. e.g.
SELECT * FROM mytable
WHERE caseSensitiveField like 'test%'
AND UPPER(caseInsensitiveField) like 'G2%'
Use plain comparisons, which are case sensitive by default (unless you have declared the column COLLATE NOCASE):
SELECT *
FROM mytable
WHERE caseSensitiveField >= 'test'
AND caseSensitiveField < 'tesu'
AND caseInsensitiveField LIKE 'g2%'
This works only if the original LIKE is searching for a prefix, but allows using an index.
In SQLite you can use GLOB instead of LIKE for pattern search. For example:
SELECT * FROM mytable
WHERE caseSensitiveField GLOB 'test*'
AND caseInsensitiveField LIKE 'g2%'
With this approach you don't have to worry about PRAGMA.
I know this is an old question, but if you are coding in Java and have this problem this might be helpful. You can register a function that handles the like checking. I got the tip form this post: https://stackoverflow.com/a/29831950/1271573
The solution i dependent on sqlite jdbc: https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc
In my case I only needed to see if a certain string existed as part of another string (like '%mystring%'), so I created a Contains function, but it should be possible to extend this to do a more sql-like check using regex or something.
To use the function in SQL to see if MyCol contains "searchstring" you would do:
select * from mytable where Contains(MyCol, 'searchstring')
Here is my Contains function:
public class Contains extends Function {
#Override
protected void xFunc() throws SQLException {
if (args() != 2) {
throw new SQLException("Contains(t1,t2): Invalid argument count. Requires 2, but found " + args());
}
String testValue = value_text(0).toLowerCase();
String isLike = value_text(1).toLowerCase();
if (testValue.contains(isLike)) {
result(1);
} else {
result(0);
}
}
}
To use this function you must first register it. When you are done with using it you can optionally destroy it. Here is how:
public static void registerContainsFunc(Connection con) throws SQLException {
Function.create(con, Contains.class.getSimpleName(), new Contains());
}
public static void destroyContainsFunc(Connection con) throws SQLException {
Function.destroy(con, Contains.class.getSimpleName());
}
I used a regular expression to do what I needed. I wanted to identify all the occurrences of the word "In" that was not all lower case.
select [COL] from [TABLE] where [COL] REGEXP '\bIn\b';
Example:
with x as (select 'in' Diff_Ins union select 'In' Diff_Ins)
select Diff_Ins from x where Diff_Ins REGEXP '\bIn\b';
As others mention, SQLite also offers the GLOB function which is case-sensitive.
Assume g2* is text entered by the user at the application-level. To simplify application-side grammar and make GLOB case-insensitive, the text needs to be normalised to a common case:
SELECT * FROM mytable WHERE LOWER(caseInsensitiveField) GLOB LOWER('g2*');
If UNICODE is required, carefully test LOWER and UPPER to confirm they operate as expected. GLOB is an extension function specific to SQLite. Building a general grammar engine supporting multiple database vendors is non-trivial.
I'm attempting to setup a search function from a string a user types. (ex: "John Doe" or "Doe, John")
I was thinking I would use Replace(SearchString, ",", "") to get rid of the commas the user might enter, and then use Split(SearchString, " ") to get all the words into an array. Once they're in the array I would execute a Stored Procedure on each of the terms and build a DataTable with the results.
Below is what I'm wanting to use for executing my stored procedure.
oCommand = DataAccess.GetSQLCommand("MyStoredProcedure", CommandType.StoredProcedure, SourceServer.ConnectionLocal)
oCommand.Parameters.AddWithValue("#MySearchString", SearchString)
oAdapter = New SqlDataAdapter(oCommand)
oAdapter.Fill(MyDataTable)
Now I'm thinking the "SearchString" I will assign while looping through my array of words... but this doesn't seem like the right way to do this. Maybe it is but I don't know how to append my next result to the previous DataTable either.
There are some great ideas for using arrays and Lists in SQL Server on this page - http://www.sommarskog.se/arrays-in-sql-2005.html
I personally find the XML method the most useful;
http://www.sommarskog.se/arrays-in-sql-2005.html#XML
An example of how I've used this in the past is;
DECLARE #indata nvarchar(max)
DECLARE #hDoc int
SET #indata = '
<ROOT>
<SearchTerm code="Test search term"></SearchTerm>
<SearchTerm code="Other search term"></SearchTerm>
<SearchTerm code="Next search term"></SearchTerm>
</ROOT>'
CREATE TABLE #searchTerm (
code varchar(40)
)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #indata
INSERT Into #searchTerm
SELECT code
FROM OPENXML(#hDoc, '/ROOT/SearchTerm',1)
WITH (code varchar(50))
EXEC sp_xml_removedocument #hDoc
-- Use the data in #searchTerm as needed in your query
SELECT * FROM MyTable WHERE searchValue IN (SELECT code FROM #searchTerm)
DROP TABLE #searchTerm
Try passing in the comma separated values in as a single string of nvarchar. Then use the SELECT FROM WHERE IN structure within your stored procedure. Create the sql command wwithin your stored procedure by concatenation and then call EXEC #sql
Declare #sql =
'SELECT * FROM tbl
WHERE person IN(' + #Application + ')'
exec(#sql)
Beware, if your list of search criteria is large this may not be the best solution for you.
Note that the commas should be between the full names not the first name and last name.