How to get the sql print message using pymssql - python-3.4

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()

Related

Problem passing parameters to stored procedure

I don't know how to pass the custom table names (eg prepAccTN) so that the SP gets executed w/o ERROR 1146 (42S02): Table '[my-schema-name].prepacctn' doesn't exist
Tried executing it like so
CALL Bill_UpdateUnbalancedPrepaidAccounts('correct_table_name', 'boo', 3);
CALL Bill_UpdateUnbalancedPrepaidAccounts("correct_table_name", 'boo', 3);
CALL Bill_UpdateUnbalancedPrepaidAccounts("my-schema-name.correct_table_name", 'boo', 3);
All failed!
CREATE PROCEDURE Bill_UpdateUnbalancedPrepaidAccounts(prepAccTN VARCHAR(100), wtrTN VARCHAR(100), prepAccId INT)
BEGIN
UPDATE prepAccTN SET `unbalanced` = (
SELECT SUM(`chargesecs`) FROM wtrTN WHERE `prepaidaccount` = prepAccId AND `prepaccsettl` = 0
) WHERE `id` = prepAccId;
END//
DELIMITER ;
If you really need to pass ther table name as a parameter to a stored procedure you need to use prepared statements. You cannot use the parameter directly in a query.
Tried out the prepared statements but unfortunately they cannot be used within the stored procedure! So I do not use a SP now and put the whole necessary code (back) into the trigger. Not the best and preferred way but it's ok to me and my little sql code I have to execute

Classic ASP + ADODB -- how to see actual query being run (for testing)

I'm using Classic ASP. I have a wrapper function for database queries that accepts a query string and an array of parameters, and auto-creates the proper query object and runs the query. Very handy and has been working great.
Here's my problem: When testing, I often want to see the exact text of the query being passed to SQL. Back in the "bad old days" of assembling queries through concatenation I could just write out the string. Now that I'm using parameterization it's a bit more tricky.
How do I take a peek at the fully-assembled query string just before it's passed to the database connection?
Here is the function I'm using, simplified. (The actual function doesn't assume string, for example.)
Public Function pquery( strQuery, params )
Dim cmd, param, thisParam, rs
Set cmd = Server.CreateObject( "ADODB.Command" )
cmd.ActiveConnection = MyConn
cmd.CommandText = strQuery
If IsArray( params ) then
Dim adVarChar : adVarChar = 200
For Each param In params
Set thisParam = cmd.CreateParameter( "#p", adVarChar, , len( param ), param )
cmd.Parameters.Append thisParam
Next
End If
Set rs = cmd.Execute
Set pquery = rs
End Function
I would consider using Sql Query Profiler, as it'll allow you to view the sql text as well as the values being passed in. It'll allow you to set breakpoints, as well as see how long it takes to run a query. However, this requires the query to be sent to the actual database (you had asked for before).
To do it beforehand, you would need to loop through the parameters collection in the command object, then do a find/replace with the key/value pairs in the command text property. it would be hackish at best, if you can use Profiler, go with that.

How to pass parameters for OPENDATASOURCE

I can connect to a linked server with this:
SELECT testNo, soruTuruId, soruNo, cevap , degerlendirenTcNo, degerlendirilenTcNo
FROM OPENDATASOURCE('SQLOLEDB', 'Data Source=192.168.150.42;User ID=readerUser;Password=1').akreditasyon.dbo.tblPerfCevap
But I have to pass the password as parameter. and I try like this:
SET #connectionString = 'Data Source=192.168.150.42;User ID=readerUser;Password='+#pw
SELECT testNo, soruTuruId, soruNo, cevap , degerlendirenTcNo, degerlendirilenTcNo
FROM OPENDATASOURCE('SQLOLEDB', #connectionString ).akreditasyon.dbo.tblPerfCevap
and
SELECT testNo, soruTuruId, soruNo, cevap , degerlendirenTcNo, degerlendirilenTcNo
FROM OPENDATASOURCE('SQLOLEDB', 'Data Source=192.168.150.42;User ID=readerUser;Password='+#pw ).akreditasyon.dbo.tblPerfCevap
but didnt work:S
does anyone have an idea?
use exec () function to run the query. Wrap your query into a valid string.
Here's my working implementation of Ojulari's answer. I wanted the same stored proc to run from our Prod, QA, etc.. environment, each one needs to access a different BizTalk Server's database.
declare #DataSource varchar(100)
set #DataSource =
case
when dbo.GetEnvironment() = 'PROD' then 'Data Source=ProdBizTalkServer;UID=test;PWD=test'
when dbo.GetEnvironment() = 'QA' then 'Data Source=QABizTaklServer;UID=test;PWD=test'
ELSE null
end
declare #myExec varchar(max)
set #myExec = 'select
nvcMessageType,
COUNT(*)
from OpenDataSource(''SQLNCLI10'',''' + #DataSource + ''').BizTalkMsgBoxDb.dbo.Spool
where nvcMessageType like ''%#FlightMessageReceivedEvent''
group BY nvcMessageType
order by nvcMessageType
'
print '#myExec=' + IsNull(#myExec,'null')
EXEC(#myExec)

INSERT stored procedure does not work?

I'm trying to make an insertion from one database called suspension to the table called Notification in the ANimals database. My stored procedure is this:
ALTER PROCEDURE [dbo].[spCreateNotification]
-- Add the parameters for the stored procedure here
#notRecID int,
#notName nvarchar(50),
#notRecStatus nvarchar(1),
#notAdded smalldatetime,
#notByWho int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO Animals.dbo.Notification
(
NotRecID,
NotName,
NotRecStatus,
NotAdded,
NotByWho
)
values (#notRecID, #notName, #notRecStatus, #notAdded, #notByWho);
END
The null inserting is to replenish one column that otherwise will not be filled, I've tried different ways, like using also the names for the columns after the name of the table and then only indicate in values the fields I've got. I know it is not a problem of the stored procedure because I executed it from the sql server management studio and it works introducing the parameters. Then I guess the problem must be in the repository when I call the stored procedure:
public void createNotification(Notification not)
{
try
{
DB.spCreateNotification(not.NotRecID, not.NotName, not.NotRecStatus,
(DateTime)not.NotAdded, (int)not.NotByWho);
}
catch
{
return;
}
}
And I call the method here:
public void createNotifications(IList<TemporalNotification> notifications)
{
foreach (var TNot in notifications)
{
var ts = RepositoryService._suspension.getTemporalSuspensionForNotificationID(TNot.TNotRecID);
Notification notification = new Notification();
if (ts.Count != 0)
{
notification.NotName = TNot.TNotName;
notification.NotRecID = TNot.TNotRecID;
notification.NotRecStatus = TNot.TNotRecStatus;
notification.NotAdded = TNot.TNotAdded;
notification.NotByWho = TNot.TNotByWho;
if (TNot.TNotToReplace != 0)
{
var suspensions = RepositoryService._suspension.getSuspensionsAttached((int)TNot.TNotToReplace);
foreach (var sus in suspensions)
{
sus.CtsEndDate = TNot.TNotAdded;
sus.CtsEndNotRecID = TNot.TNotRecID;
DB.spModifySuspensionWhenNotificationIsReplaced((int)TNot.TNotToReplace, (int)sus.CtsEndNotRecID, (DateTime) sus.CtsEndDate);
}
DB.spReplaceNotification((int)TNot.TNotToReplace, DateTime.Now);
createNotification(notification);
}
else
{
createNotification(notification);
}
}
}
deleteTemporalNotifications(notifications);
}
It does not record the value in the database. I've been debugging and getting mad about this, because it works when I execute it manually, but not when I automatize the proccess in my application. Does anyone see anything wrong with my code?
Thank you
EDIT: Added more code. It still doesn't work changing that, I mean, the procedure works if I execute it, so I don't know what could be the error. In fact, I don't get any error. Could it be a matter of writin in a table that is not in the database where you have your stored procedure?
I would specify your column names and DONT incude the NULL at all for that column. Just let SQL Server deal with it.
INSERT INTO Animals.dbo.Notification
(
RecID,
[Name],
RecStatus,
Added,
ByWho
)
values (#notRecID, #notName, #notRecStatus, #notAdded, #notByWho);
Run profiler when you try to run it from the application and see what values it realy is sending. That will tell you if the application is creating the correct exec statment to exec the proc.
Also it may be a permissions problem.
Specify your column names:
INSERT INTO Animals.dbo.Notification
(RecID, Name, RecStatus, Added, ByWho)
VALUES
(#notRecID, #notName, #notRecStatus, #notAdded, #notByWho);
"Could it be a matter of writin in a table that is not in the database where you have your stored procedure?"
That may be the problem. You could try adding the "WITH EXECUTE AS OWNER" clause to your stored procedure so that it executes as the owner of the stored procedure. Or grant write permissions for the executing user to the table.
http://msdn.microsoft.com/en-us/library/ms188354.aspx
ok, I finally found out what noone realized lol. It was a very stupid error but got me really mad till I found the problem. It wasn't a problem of permissions, the problem was that I was not executing the procedure from my application, so where I wrote this:
DB.spCreateNotification(not.NotRecID, not.NotName, not.NotRecStatus,
(DateTime)not.NotAdded, (int)not.NotByWho);
When I had to write:
DB.spCreateNotification(not.NotRecID, not.NotName, not.NotRecStatus,
(DateTime)not.NotAdded, (int)not.NotByWho).Execute();
so as you see I was focusing my efforts in much more complex things and I wasn't even executing it...lol.
Thank you all for your answers anyway:)

HTML Encoding in T-SQL?

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 &lt will become &lt;
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.

Resources