N prefix and parameter - sql

I have some stored procedure
CREATE PROC MyProc ( #FullName NVARCHAR(200) = NULL )
AS --.............
When I call this proc as exec MyProc 'Some english text' it works good.
But if call it as exec MyProc 'Русский текст' that is using Russian alphabet, it doesn't work properly.
And the call exec MyProc N'Русский текст' works good again.
I have a client application and... I need to add N prefix to parameters? If yes, how do I do it?

The N would only be needed (manually) if you are concatenating a string in the .net code.
It's automatic if you declare a SQLParameter as nvarchar: the framework takes care of it for you.
So your client code is incorrect and opens you up to SQL injection
Anyway, the N says that the string literal is unicode.

N means National language character set also well known as Unicode.
See on Microsoft KB: You must precede all Unicode strings with a prefix N when you deal with Unicode string constants in SQL Server

What kind of client application do you use?
if it is .Net - just specify that parameter has Unicode type.
If it is OLEDB/ADO - the same thing, param must be marked as unicode, and pass wchar_t instead of char
Update
Sample from msdn (pay attention to NVarChar):
yourCommand.Parameters.Add(
"#FullName", SqlDbType.NVarChar, 80).Value = "toasters";
And please avoid SQL injection ;)

Related

Character with Bit/hex confusion in DB2

This works:
SELECT TASKT_ID FROM DATA . TASKT WHERE TASK_WEB_IDENTIFIER = CAST ( HEXTORAW ( '0213725501A421D384233E5001' ) AS CHAR ( 26 ) ) ;
Since that work I put it into the procedure:
BEGIN
DECLARE GET_TASKT_ID_BY_TASK_WEB_IDENTIFIER_C1 CURSOR WITH RETURN FOR
SELECT TASKT_ID FROM DATA . TASKT WHERE TASK_WEB_IDENTIFIER = CAST ( HEXTORAW ( P_WEB_IDENTIFIER ) AS CHAR ( 26 ) ) ;
OPEN GET_TASKT_ID_BY_TASK_WEB_IDENTIFIER_C1 ;
END
The procedure has the one parameter P_WEB_IDENTIFIER which is a CHAR(26) for bit data with the CCSID 65535
However, when I now call it with the string like so:
call PROGRAM . GET_TASKT_ID_BY_TASK_WEB_IDENTIFIER ('0213725501A421D384233E5001');
I get back that the argument for VARBINARY_FUNCTION is invalid by length or data type.
Also, this works:
call PROGRAM . GET_TASKT_ID_BY_TASK_WEB_IDENTIFIER (CAST('0213725501A421D384233E5001' as char(26)));
What Can I do to make sure that string converts with only the string being passed?
What are you using to call the stored procedure?
What version and release of Db2 for i?
In the Run SQL Scripts component of IBM ACS or the older Access for Windows, string literals in your statements are treated as varchar.
Thus the CAST('0213725501A421D384233E5001' as char(26)) makes sense. What doesn't is the error message. Normally, you'd get a procedure not found error as the Db is looking for a procedure named PROGRAM.GET_TASKT_ID_BY_TASK_WEB_IDENTIFIER that takes a varchar parameter and the only thing that exists is a procedure that takes a char(26).
IBM's tools have gotten better at implicitly converting when needed. But I usually go with an explicit conversion when testing manually (as you've done here). Or I just make the parms varchar to start. And convert to character within the procedure if needed.
The char/varchar difference doesn't usually matter to the client code, as it can be specific in it's type definitions. It's only a factor for interactive tools like ACS that are executing dynamic statements.

How to add comments to SQL CLR function?

I want to add comments to my SQL CLR functions (as I do to other SQL objects I am creating or editing - functions, procedures and views). Unfortunately, I am not able to do this for the SQL CLR objects.
For example, the following code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =================================================================================================================================
-- Author: gotqn
-- Create date: 2015-03-25
-- Description: Converts a string that has been encoded for transmission in a URL into a decoded string.
-- Usage Example:
/*
SELECT [dbo].[fn_UrlDecode]('http://stackoverflow.com/search?q=tql+sql+server');
*/
-- =================================================================================================================================
CREATE FUNCTION [dbo].[fn_UrlDecode] (#value NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS EXTERNAL NAME [Utils].[Utils].[UrlDecode]
GO
when the function is script from the SQL Management studio is going to produce this:
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE FUNCTION [dbo].[fn_UrlDecode](#value [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [Utils].[Utils].[UrlDecode]
GO
I try to fix this moving the comments part after the AS as this is the way comments are added for views, but it fails again. Then I try to put the comments after the CREATE clause, after the EXTERNAL NAME ... clause, but nothing changed.
Is there a way to fix this behaviour?
While #Damien is correct as to why the comments are not saved, there is still a somewhat work-around to store comments: Extended Properties.
For example:
EXEC sys.sp_addextendedproperty #name = N'comments', #value = N'
-- =================================================================================================================================
-- Author: gotqn
-- Create date: 2015-03-25
-- Description: Converts a string that has been encoded for transmission in a URL into a decoded string.
-- Usage Example:
/*
SELECT [dbo].[fn_UrlDecode](''http://stackoverflow.com/search?q=tql+sql+server'');
*/
-- =================================================================================================================================
', #level0type = 'SCHEMA', #level0name = N'dbo',
#level1type = 'FUNCTION', #level1name = N'fn_UrlDecode';
You just need to escape your embedded single-quotes.
Then you can retrieve them via:
SELECT [value]
FROM sys.fn_listextendedproperty(N'comments', 'SCHEMA', N'N'dbo',
'FUNCTION', N'fn_UrlDecode', NULL, NULL);
Minor additional note: if you won't ever decode URLs that are more than 4000 characters long (and I am pretty sure that you won't run into many that are even over 2048 characters), then you would be better served to use NVARCHAR(4000) for both input and output datatypes as that will be quite a bit faster than if either, or both, are NVARCHAR(MAX).
Basically, if it's a type not listed as having data stored in sys.sql_modules then the original text that created the object is not retained and so comments aren't retained. No CLR object stores such text.
This is the expected behavior. Even when you write a native TSQL script adding comments right before the routine signature, build it against the DBMS and do right click/edit to see the code the comments won't be there. Go ahead and give a try to this approach:
CREATE FUNCTION [dbo].[fn_UrlDecode]
(
#value [nvarchar](max)
)
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
/*
***All the comments goes here***
*/
EXTERNAL NAME [Utils].[Utils].[UrlDecode]
GO
Hope it helps!
Committing Necromancy.
Solution first...reason second...
Simply wrap the CLR function in a standard function and put the comments in the standard function.
Overkill? Perhaps, but as I said, I have a reason and need in my current situation.
As an employee of a contractor to a large organization, there are tons of fingers in every pie, and the hands attached to those fingers are usually long gone and forgotten...but maintenance of their legacy code lives on. This is especially noticeable when external assemblies are loaded in a database, and there is a need to change or augment existing libraries...somewhere...but where. The coder is gone, and the documentation of the process is buried somewhere long forgotten.
So, for me, the primary reason for commenting such functions is to make it easy to identify the name of the Assembly and the Repository for the Source Code. Other than starting out with properly documented code in the first place (remember this is legacy cruft) I am open to other suggestions about better places/ways to do this...I am all ears.
Providing this information in a comment, somewhere in or related to the function or assembly, is very helpful. The simple wrapper noted above is a very KISS method to achieve this goal.

In T-SQL under MS SQL Server 2008, what does '#' mean in front of a parameter *value* that's a string literal?

I have come across the following example code:
EXECUTE msdb.dbo.sysmail_add_profileaccount_sp
#profile_name = #'SQL mail profile',
#account_name = #'account name',
#sequence_number = 1 ;
What does '#' mean in front of the string literal, as in the example above:
#account_name=#'account name'
I understand that my question may stand true for any executable module's parameters in T-SQL, or maybe for any string literal in T-SQL in general, so the above is just a concrete example of what I'm looking at.
I do not think that this is valid T-SQL. This may be an artifact of replacing variables with values somewhere in a script and not trimming the leading #.
I get a syntax error with that, so I don't think it means anything except that it's not valid syntax. Did you pull that from valid T-SQL that is being called using just T-SQL, or perhaps this is parameterized stuff coming from some other language or program?

Common Table Expression Error

I have to use old school ADODB (not ADO.NET) to execute a statement that contains a Common Table Expression.
I am using (have to use) the SQLOLEDB provider.
The DML statement works fine when executing from a Windows 7 / Windows Server 2008 client but not from WinXP or Win2K3 server.
I have profiled the routine and found that the old OSes send a slightly different SQL statement.
Win7 + 2008 = exec sp_executesql N'WITH source(Vsl, Cpt, SrcTyp, SrcNum, Opn, JobNum, Qty, Cst, Vry, Reg, Vnt, Sbk) AS ...'
WinXP + Win2K3 = exec sp_executesql N'exec WITH source(Vsl, Cpt, SrcTyp, SrcNum, Opn, JobNum, Qty, Cst, Vry, Reg, Vnt, Sbk) AS ...'
Notice the extra 'exec' slipped into the command text.
It appears as if the verions of SQLOLEDB.1 on the old OSs mis-treats the WITH statement and sees it as needing a prepending 'exec'.
Can anyone shed some light on this. Is there an SQLOLEDB driver update that I can apply to the old OSes? or some other workaround.
(FYI, You should really revisit some of your existing questions, as most of them seem to have helpful answers that appear to address the question; your comments even suggest this is so. If they have an answer, please accept it).
If you really need to use a CTE here (meaning you're doing something recursive and aren't just using it for convenience instead of inline-selecting or inline-joining), the simplest and fastest workaround would probably be to include your SQL within your own call to sp_executesql. You'll end up nesting calls to it (which will look silly), but it shouldn't cause any actual problems.
Wrapping the query up in an sp_executesql statement works great if you don't have parameters in the query, otherwise the parameters aren't parsed because they're in a quoted string by the time they get to ADO, and this results in a syntax error.
What I did to resolve this was to create a TADOQuery descendant, which overrides the constructor, as follows:
constructor TCPADOQuery.Create(AOwner: TComponent);
begin
inherited;
TWideStringList(SQL).OnChange := LocalQueryChanged;
end;
LocalQueryChanged then checks if the query starts with a common table expression, and inserts a dummy declaration at the beginning of the query, which the XP ADO parser does understand. A CTE must be preceded by a semicolon if it is not the first statement in the query, so we have to fix that first:
procedure TCPADOQuery.LocalQueryChanged(Sender: TObject);
var
a: WideString;
begin
if not (csLoading in ComponentState) then
Close;
a := Trim(SQL.Text);
if Uppercase(copy(a, 1, 4)) = 'WITH' then a := ';' + a;
if Uppercase(copy(a, 1, 5)) = ';WITH' then
a := 'DECLARE #DummyForADO_XP BIT'#13#10 + a;
CommandText := a;
end;
This has resolved the problem, and has saved me having to rework all of my code where I use both CTEs and parameters.

tsql : best way to cast variables to string type?

I wrote a tsql procedure which inserts a string into a text file which means it requires all variables be converted to a string. Instead of using a case by case statement, is there an easier to do this which encompasses all cases and forces whatever type to a string type?
thanks in advance
All-caps is literal text, lower-case is something into which you should interpolate a value:
CAST(expression AS CHAR)
-- or:
CAST(expression AS VARCHAR)
You can optionally specify a length.
Unfortunately, it's a bit harder with datetimes, if you want to format it in any way that's different from the default representation.
My info came from the MSDN site. I think you'll have to read that site, and others, carefully and play around with it a bit, but hopefully the CAST function will be a good start.
Good luck!
you need to convert/cast it each time. I made a function to use:
CREATE FUNCTION QuoteNull
(
#InputStr varchar(8000) --value to force to string
)
RETURNS
varchar(8000)
AS
BEGIN
RETURN COALESCE(''''+#InputStr+'''','null')
END
it puts single quotes around the value or just the word null if it is null, but you can customize it as necessary.
here is a version that handles formatting dates automatically:
CREATE FUNCTION QuoteNull
(
#InputStr sql_variant --value to force to string
)
RETURNS
varchar(8000)
AS
BEGIN
DECLARE #String varchar(8000)
SET #String=COALESCE(''''+ CASE SQL_VARIANT_PROPERTY(#InputStr,'BaseType')
WHEN 'datetime' THEN CONVERT(varchar(23),#InputStr,121)
ELSE CONVERT(varchar(8000),#InputStr)
END
+'''','null')
RETURN #String
END
I do not think there is--and if there were, I wouldn't trust it, as there'd probably be very little control over the overall formatting. Build your own, and it'll look exactly as it needs to look. Factors to contemplate:
Date formatting
Numbers, leading zeros, decimal places, +/- signs
Line length, word wrap
Leading spaces, trailing spaces, spaces between strings that don't have leading or trailing spaces
...it goes on and on.