Passing variable to SYSPROC.ADMIN_CMD - db2

I have a bit of a problem whereby I need to pass a variable value to SYSPROC.ADMIN_CMD.
Here is the deal:
DECLARE vDate TIMESTAMP;
SET vDate = timestamp_iso (MyDateFunctionGoesHere());
CALL SYSPROC.ADMIN_CMD ('LOAD FROM (select vDate...) OF CURSOR insert into .. (DateColumn...) NONRECOVERABLE');
The problem is that the upon running the Stored Procedure, I get error:
ERROR [42703] [IBM][DB2/AIX64] SQL0206N "VDATE" is not valid in the context where it is used. SQLSTATE=42703
I tried modifying it to:
CALL SYSPROC.ADMIN_CMD ('LOAD FROM (select'''||vDate||'''...) OF CURSOR insert into .. (DateColumn...) NONRECOVERABLE');
Then I get "no authorized routine named || of type FUNCTION...
I need my date variable to passed to the LOAD from because this is a dynamic value that changes all the time. How can I handle that?

Because ADMIN_CMD takes one argument that is a string, it's easiest to build your SQL statement as a VARCHAR, and then pass that variable to ADMIN_CMD. Setting vDate is unnecessary.
DECLARE vCMD VARCHAR(1024);
SET vCMD = 'LOAD FROM (select ' || CHAR(MyDateFunctionGoesHere()) || '...) OF CURSOR insert into .. (DateColumn...) NONRECOVERABLE';
CALL SYSPROC.ADMIN_CMD (vSQL);
Note that I'm assuming MyDateFunctionGoesHere() returns a TIMESTAMP, and using CHAR() to convert it to CHAR(26) to concatenate it.

Related

Scalar-valued function does not return NULL but a 'NULL' string

I need to import data from Excel into a ms sql database and I thought using the OPENROWSET would be a good idea... well, it is not bad but has some side effects.
The data I'm receiving is not allways 100% correct. By correct I mean cells that should be NULL (and in Excel empty) sometimes contain the string "NULL" or some other junk like whitespaces. I tried to fix it with this script:
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[NullIfEmpty](#input nvarchar)
RETURNS nvarchar(max)
AS
BEGIN
if (#input = '' or #input = 'NULL')
begin
return NULL
end
return #input
END
But strange things happen. This gives me a string with the text "NULL" instead of a real NULL so the grid cell after querying the database isn't yellow but contains normal text even though the target column allows NULL.
A simple test with:
select dbo.nullifempty('NULL')
or
select dbo.nullifempty(null)
also yields a string.
Do you know why this is happening and how I can fix it?
To get null for empty strings or strings that are the word NULL, you could just use coalesce and nullif:
COALESCE(NULLIF(#input, 'NULL'), NULLIF(#Input, ''), #input)
Please note that the problem in your original code is because you didn't specify the length of the #input parameter - so SQL Server created it as varchar(1).
You should always specify length for char/varchar/nchar and nvarchar.
From nchar and nvarchar page remarks:
When n is not specified in a data definition or variable declaration statement, the default length is 1. When n is not specified with the CAST function, the default length is 30.
(n referring to the n in nchar(n) or nvarchar(n))
repleace lines with 'ALTER"
ALTER FUNCTION [dbo].[NullIfEmpty](#input nvarchar(max))
and with line with 'if'
if (LTRIM(RTRIM(#input)) = '' or #input IS NULL)
you should reassign value for declared variable using set
''''
BEGIN
if (#input = '' or #input = 'NULL')
begin
set #input = NULL
end
select #input
END
test

IN clause has parameter doesn't work in stored procedure

I have "Name IN (#NameList))" inside of the stored procedure as one of the conditions.
The parameter is declared as #NameList varchar(100) = ''
When I execute the procedure with string like EXEC myProc #NameList='''a'''
the procedure returns nothing, but when I replaced the parameter with hard coded value like 'a' it succeeded.
I put a PRINT statement inside the SP and it prints exactly 'a'
What's wrong here?
Thanks,
Please read my comments as well but one solution would be to split your comma separated string into a table and then join onto that:
declare #list varchar(max) = '13,14,15,13,14,15,13'
SELECT
LTRIM(RTRIM(a.b.value('.[1]','VARCHAR(255)'))) Parish
FROM
(
SELECT CAST('<XMLRoot><RowData>' + REPLACE(#list,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) x)t
CROSS APPLY x.nodes('/XMLRoot/RowData')a(b)

Use variable from a previous DO clause

I have a .sql script like this:
DO $$
DECLARE
prev_count := (SELECT count(*) FROM ...);
END$$;
UPDATE [...]
DO $$
DECLARE
cur_count := (SELECT count(*) FROM ...);
BEGIN
ASSERT cur_count = prev_count, 'Mismatch';
END$$;
In which I get some value, modify the database, and expect a new value to match an old value. However, I get errors like this:
psql:migration.sql:163: ERROR: column "prev_count" does not exist
LINE 1: SELECT cur_count = prev_count
^
QUERY: SELECT cur_count = prev_count
CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT
I can't tell if this is a scoping issue because of the anonymous block, and why it's attempting to treat my variables like columns. Any ideas?
According to the manual DO executes an anonymous code block that:
... is treated as though it were the body of a function with no parameters, returning void. It is parsed and executed a single time...
So it is a function that returns VOID. In that sense prev_count only exists in the first DO.
To avoid this, you could create a TEMP table and insert the prev_count in the first DO so you can use it anywhere in the transaction.

postgres check if null then cast to numeric

I am trying check if a value is null if so the select null else cast to numeric, but it throws an error. This is actually part of an insert statement
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power ='null' then NULL else cast(:power as numeric) end from abc
error that I get is
Error: ERROR: invalid input syntax for type numeric: "null"
:power is a variable that can be given any value using java code. If I give a value of null it give an error.
In code I get the following error from the java stack trace
org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to numeric
Error:
SELECT CASE WHEN 'null' = 'null' THEN NULL ELSE cast('null' AS numeric) END
No error:
DO $$
DECLARE
power text := 'null';
BEGIN
PERFORM CASE WHEN power = 'null' THEN NULL ELSE cast(power AS numeric) END;
END;
$$
Explanation:
If you build a query string, the expression cast('null' AS numeric) or simply 'null'::numeric always raises an exception, even in an ELSE block that is never executed, because it is invalid input syntax and the exception is raised during the syntax check (like the error message implies), not during execution.
A CASE statement like you display only makes sense with a parameter or variable not with literals. The second instance of the literal has no connection to the first instance whatsoever after the query string has been assembled.
For dynamic SQL like that, you need to check the value before you build the query string. Or you use a function or prepared statement and pass the value as parameter. That would work, too.
More advice after comment:
In your particular case you could check the value in the app and build a query string like this:
INSERT INTO tbl(name, abc_id, created, power)
SELECT 'xyz'
, abc_id
, now()
, <insert_value_of_power_or_NULL_here> -- automatically converted to numeric
FROM abc
You may be interested in a different approach to INSERT data from a file conditionally.
Use COPY for files local to the server or psql's meta-command \copy for files local to the client.
if the field value is null, and you want in this case to map it to some value you can use coalesce(field_name, 'Some value') or coalesce(field_name, 123).
For full documentation see here.
You have to check with the IS operator, and not with the equal when you dealing with NULL :
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power IS null then NULL else cast(:power as numeric) end from abc
INSERT into someTable(name,created,power) SELECT 'xyz',now(),
case :power when 'null' then NULL else :power end::numeric from abc
I was trying to do something similar in order to update/insert some records where a numeric value can be null or not.
You can validate a variable before you send it to the function or inside the function depending the value passed
(For me using a variable is better than use CASE WHEN THEN ELSE END CASE every time you need to validate the value)
So to work with the NULL values using a regular comparison operand in order to find a record to update can be done by turning transform_null_equals to ON
I hope this help someone
CREATE OR REPLACE FUNCTION update_insert_transaction(vcodaccount integer, vcodaccountaux text,
vdescription text, vcodgroup integer)
RETURNS integer AS $$
DECLARE
n integer = 0;
vsql text = 'NULL';
BEGIN
IF vcodaccountaux <> '' THEN
vsql = vcodaccountaux;
END IF;
SET LOCAL transform_null_equals TO ON;
EXECUTE 'UPDATE account_import_conf SET (codaccount, codaccountaux, description, codgroup) =
('||vcodaccount||','||vsql||',trim('||quote_literal(vdescription)||'),'||vcodgroup||')
WHERE codaccount='||vcodaccount||' AND codaccountaux = '||vsql||' RETURNING * ';
GET DIAGNOSTICS n = ROW_COUNT;
IF n = 0 THEN
EXECUTE 'INSERT INTO account_import_conf (codaccount, codaccountaux, description, codgroup)
SELECT '||vcodaccount||','||vsql||' ,trim('||quote_literal(vdescription)||'),'||vcodgroup||';';
END IF;
RETURN n;
END;$$
LANGUAGE plpgsql;

What is the actual data type of the #cleartext (2nd) param of SQL Server's EncryptByKey(..) function?

Pointedly what I'm asking below is: What is the actual data type of the #cleartext parameter of this SQL function? >> ENCRYPTBYKEY (..) -
http://msdn.microsoft.com/en-us/library/ms174361.aspx
(If you read below this line you can follow the history and reasoning. I think it's trickier than it first appears.)
The SQL Server documentation states the #cleartext (2nd) parameter of the EncryptByKey(..) function can accept a number of various types:
EncryptByKey (#key_GUID , #cleartext [, #add_authenticator, #authenticator] )
#cleartext Is a variable of type
nvarchar, char, varchar, binary,
varbinary, or nchar that contains data
that is to be encrypted with the key.
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - - - - - But what is its actual declared data type? ...
If I were to create a custom function (totally separate from the EncryptByKey example given above) what actual data type do I give a custom parameter so it will accept all those same types in the same manner?
Edit 1: I'm actually wrapping the SQL EncryptByKey function in a custom
UDF function and I want to recreate the same
parameter types to pass through to
it. This is the reasoning behind my want to create exact same params by type.
Edit 2: If I try using sql_variant it results in the error
Msg 8116, Level 16, State 1, Procedure
EncryptWrapper, Line 17 Argument data
type sql_variant is invalid for
argument 2 of EncryptByKey function.
Edit 3:
Here's my custom wrapper function - and the direct problem. What should the data type of #cleartext be for direct pass through to EncryptByKey?
ALTER FUNCTION [dbo].[EncryptWrapper]
(
#key_GUID uniqueidentifier,
#cleartext -- ??????????? <<< WHAT TYPE ????????????????
#add_authenticator int = 0,
#authenticator sysname = NULL
)
RETURNS varbinary(8000)
AS
BEGIN
-- //Calling a SQL Server builtin function.
-- //Second param #cleartext is the problem. What data type should it be?
Return EncryptByKey(#key_GUID, #cleartext, #add_authenticator, #authenticator)
END
Note: I shouldn't have to use CAST or CONVERT - I only need to use the proper data type for my #cleartext param.
Edit 4: Discovered the EncryptByKey(..) #cleartext parameter is not the following types:
sql_variant- raises error when passed
varbinary- too restrictive- doesn't allow passing of the text types otherwise accepted by EncryptByKey(..)
sysname, nvarchar, varchar- weird behaviour- tends to take only the first character of the argument text or something
try sql_variant:
CREATE FUNCTION [dbo].[yourFunction]
(
#InputStr sql_variant --can not be varchar(max) or nvarchar(max)
)
returns
varchar(8000)
BEGIN
--can use SQL_VARIANT_PROPERTY(#InputStr,'BaseType') to determine given datatype
--do whatever you want with #inputStr here
RETURN CONVERT(varchar(8000),#InputStr) --key is to convert the sql_varient to something you can use
END
GO
the key is to convert the sql_varient to something you can use within the function. you can use IF statements and check the BaseType and convert the sql_varient back into the native data type
EDIT
here is an example of how to get the original datatype:
CREATE FUNCTION [dbo].[yourFunction]
(
#InputStr sql_variant --can not be varchar(max) or nvarchar(max)
)
returns
varchar(8000)
BEGIN
DECLARE #Value varchar(50)
--can use SQL_VARIANT_PROPERTY(#InputStr,'BaseType') to determine given datatype
--do whatever you want with #inputStr here
IF #InputStr IS NULL
BEGIN
SET #value= 'was null'
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='char'
BEGIN
--your special code here
SET #value= 'char('+CONVERT(varchar(10),SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='datetime'
BEGIN
--your special code here
SET #value= 'datetime - '+CONVERT(char(23),#InputStr,121)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='nvarchar'
BEGIN
--your special code here
SET #value= 'nvarchar('+CONVERT(varchar(10),CONVERT(int,SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))/2)+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE
BEGIN
--your special code here
set #value= 'unknown!'
END
RETURN #value
END
GO
test it out:
DECLARE #x char(5), #z int, #d datetime, #n nvarchar(27)
SELECT #x='abc',#d=GETDATE(),#n='wow!'
select [dbo].[yourFunction](#x)
select [dbo].[yourFunction](#d)
select [dbo].[yourFunction](#z)
select [dbo].[yourFunction](#n)
test output:
-------------------------------------
char(5) - abc
(1 row(s) affected)
-------------------------------------
datetime - 2010-02-17 15:10:44.017
(1 row(s) affected)
-------------------------------------
was null
(1 row(s) affected)
-------------------------------------
nvarchar(27) - wow!
(1 row(s) affected)
ENCRYPTBYKEY() almost certainly isn't written in vanilla T-SQL. It doesn't need to follow T-SQL data typing rules.
That said, if you want to write a wrapper for it, use SQL_VARIANT for the #cleartext parameter, just as KM suggested.
If ENCRYPTBYKEY() is not sensitive to the max length of #cleartext, you could munge all CHAR/VARCHARs to VARCHAR(8000), and all NCHAR/NVARCHARs to NVACHAR(4000).
Otherwise you may be SOL: any data type conversion that respects maximum length--eg, CHAR(10) vs CHAR(20)--will require dynamic SQL, so you would have to write it as a stored procedure, rather than a function. At that point, it's not really a wrapper anymore.