check whether linked server is valid in function - sql-server

We have a function that needs to select items from a linked server, like
Alter function dbo.ttest1()
returns int
as
begin
SELECT * FROM LINKED_SERVER.Database.Schema.Table
WHERE .....
RETURN 0
end
In case of the remote server is not available, the function will throw out the error saying connection failed, but I want to cover it and let it return a default value.
Since this is compile-level error, BEGIN TRY --- END TRY is not able to cover it, what's worse, since we are in function, EXEC(#string),sp_executesql, SELECT * FROM OPENQUERY('...'), sp_testlinkedserver none of them works. the worst, I cannot afford to take risk of changing it into an SP (the grammar SELECT dbo.ttest1() is penetrated everywhere in the project).
Are there any good solutions?

Related

How to check for error handling using UDF's?

I created an UDF in SQL Server but I want to check for null or invalid values. For example If Inserted an ID '333sd' I want my UDF to return a error message or print a message but in accordance to the documentation I have read the THROW or CATCH and TRY block cannot be used.
if object_id('dbo.fnDiscountPrice') is not null
drop function dbo.fnDiscountPrice;
go
create function fnDiscountPrice(#ItemID int)
returns money
as
begin
return (select sum(ItemPrice - DiscountAmount) as DiscountPrice
from OrderItems
where ItemID = #ItemID)
end
go
I have tried to declare a new variable and set it to 0 if It's null but If did it that way I will not express specifically the error. I want to be specific with that.

Why does Snowflake LAST_QUERY_ID returns NULL?

Given the following procedure
CREATE OR REPLACE PROCEDURE z()
RETURNS STRING NOT NULL
LANGUAGE JAVASCRIPT
AS
$$
const what = snowflake.execute( {sqlText: "select $1 as value from values (1), (2), (3);"} )
return 'Hi There';
$$
;
If I run the 2 statements below
CALL z();
select * from table(result_scan(-2));
I get an error "SQL Error [709] [02000]: Statement 01912a0c-01c1-0574-0000-4de50036137e not found"
If I run the 2 statements below
CALL z();
SELECT LAST_QUERY_ID(), LAST_QUERY_ID(-1), LAST_QUERY_ID(-2);
It shows me that LAST_QUERY_ID() and LAST_QUERY_ID(-1) are identical but also that LAST_QUERY_ID(-2) returns NULL...
Any idea why it returns NULL rather than something that would allow me to retrieve the result of my query "select $1 as value from values (1), (2), (3);"?
Thanks
It shows me that LAST_QUERY_ID() and LAST_QUERY_ID(-1) are identical but also that LAST_QUERY_ID(-2) returns NULL
It's expected to see the same query ID for LAST_QUERY_ID() and LAST_QUERY_ID(-1), because they are same (the default value for LAST_QUERY_ID is "-1").
On the other hand, the LAST_QUERY_ID(-2) should not returns NULL, and the "select * from table(result_scan(-2))" query should not fail.
If you define the function with CALLER rights, you can see that the LAST_QUERY_ID(-2) returns the query ID and the "select * from table(result_scan(-2))" query works:
CREATE OR REPLACE PROCEDURE z()
RETURNS STRING NOT NULL
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS
$$
const what = snowflake.execute( {sqlText: "select $1 as value from values (1), (2), (3);"} )
return 'Hi There';
$$
;
As a workaround, you can use the history window to check the latest queries. You should be able to see your query (01912a0c-01c1-0574-0000-4de50036137e) in the history tab. You will notice that these queries are executed by same user, and in the same session. Therefore, there shouldn't be a restriction on listing these queries.
I am able to reproduce the issue on my test environment, and I will report the issue to the development team. If you have access to Snowflake support, it could be better to submit a support case regarding to this issue, so you can easily follow the process.
For the record, here is the answer I got from Snowflake Support
This is an expected behavior because of security limitation placed on purpose (queries from inside a SP do not have access to queries outside and vice versa).
We put a blanket limitation that the world of inside and outside such SPs should be separated. From one the "result" of a query from another cannot be accessed.
Yes, If we declare the procedure with caller rights, everything (LAST_QUERY_ID and RESULT_SCAN) works fine in this case.
There is a discussion going on to implement this use-case when the owner and caller of an "owner's right" SP are the same, all this limitations should be lifted. But we cannot be sure about the timeline, I can link this case with that discussion so that we have this record.
Sounds good to me :-)

How to install PL/CTL language into PostgreSQL database 8.1.22

Hi I am using postgresql 8.1.22, I am trying to setup postgresql auditing using the following function.
CREATE OR REPLACE FUNCTION audit.if_modified_func() RETURNS TRIGGER AS $body$
DECLARE
v_old_data TEXT;
v_new_data TEXT;
BEGIN
/* If this actually for real auditing (where you need to log EVERY action),
then you would need to use something like dblink or plperl that could log outside the transaction,
regardless of whether the transaction committed or rolled back.
*/
/* This dance with casting the NEW and OLD values to a ROW is not necessary in pg 9.0+ */
IF (TG_OP = 'UPDATE') THEN
v_old_data := ROW(OLD.*);
v_new_data := ROW(NEW.*);
INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,original_data,new_data,query)
VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_old_data,v_new_data, current_query());
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
v_old_data := ROW(OLD.*);
INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,original_data,query)
VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_old_data, current_query());
RETURN OLD;
ELSIF (TG_OP = 'INSERT') THEN
v_new_data := ROW(NEW.*);
INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,new_data,query)
VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_new_data, current_query());
RETURN NEW;
ELSE
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - Other action occurred: %, at %',TG_OP,now();
RETURN NULL;
END IF;
EXCEPTION
WHEN data_exception THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
WHEN unique_violation THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
WHEN OTHERS THEN
RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [OTHER] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM;
RETURN NULL;
END;
$body$
LANGUAGE plpgsql
SECURITY DEFINER
But if you observe in the above function current_query() is not coming with the mentioned language plpgsql. It throws some error. When I googled I found that in order to use current_query() function PL/CTL language must be installed. I tried to install as mentioned below. It throws an error. So kindly help me how to install PL/CTL language into my database so that current_query() function should work
-bash-3.2$ createlang -d dbname pltcl
createlang: language installation failed: ERROR: could not access file "$libdir/pltcl": No such file or directory
Okay as you suggested I created that current_query() function,but this time I got some thing like this , What i did is ,
CREATE TABLE phonebook(phone VARCHAR(32), firstname VARCHAR(32), lastname VARCHAR(32), address VARCHAR(64));
CREATE TRIGGER phonebook_auditt AFTER INSERT OR UPDATE OR DELETE ON phonebook
FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func();
INSERT INTO phonebook(phone, firstname, lastname, address) VALUES('9966888200', 'John', 'Doe', 'North America');
for testing the function i created a table named phonebook and created a trigger so that the function mentioned above audit.if_modified_func() will be executed after any insert or update or delete.the row is getting inserted but I am getting a error reg the audit.if_modified_func() function .the error is as follows
WARNING: [AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [OTHER] - SQLSTATE: 42703, SQLERRM: column "*" not found in data type phonebook
Query returned successfully: 1 rows affected, 10 ms execution time.
Kindly tell me what can i do to get rid of the above error.
Not sure where you found the information about current_query and pltcl. These are unrelated. The reason why you can't find pltcl is simply because you're using too old PostgreSQL. current_query() has been added to Pg in version 8.4.
Is there any particular reason why you're using such old version? It is no longer supported, and it lacks almost 8 years of added features!
If you have to use 8.1, you might want to define:
create function current_query() returns text as '
select current_query from pg_stat_activity where procpid = pg_backend_pid();
' language sql;
But it is much better idea just to upgrade.
As for edited and added second question - it's very likely that Pg 8.1 cannot use "row.*" construct. Find who wrote the original code with the "dance comments", and ask about it. Perhaps it was meant to work in newer Pgs.

Return implicit string from oracle SP

Hi i have the following stored procedure...
CREATE OR REPLACE PROCEDURE DB.INSERTGOOD
(
--CapRefCursor OUT Cap_Cur_Pkg.CapCur,
p_APPLIANT_TLT IN GOODRIGHT_MANUAL.APPLICANT_TLT%TYPE,
p_APPLIANT_NME IN GOODRIGHT_MANUAL.APPLICANT_NME%TYPE,
p_APPLICANT_SURNME IN GOODRIGHT_MANUAL.APPLICANT_SURNME%TYPE,
p_COMPANY_NME IN GOODRIGHT_MANUAL.COMPANY_NME%TYPE,
p_ID_CDE IN GOODRIGHT_MANUAL.ID_CDE%TYPE,
p_ADD1 IN GOODRIGHT_MANUAL.ADD1%TYPE,
p_OCCUPATION1 IN GOODRIGHT_MANUAL.OCCUPATION1%TYPE,
p_REMARK1 IN GOODRIGHT_MANUAL.REMARK1%TYPE,
p_SOURCE IN GOODRIGHT_MANUAL.SOURCE%TYPE
)
IS
BEGIN
INSERT
INTO GOODRIGHT_MANUAL
(
SEQ_ID,
APPLICANT_TLT,
APPLICANT_NME,
APPLICANT_SURNME,
COMPANY_NME,
ID_CDE,
ADD1,
OCCUPATION1,
REMARK1,
GOODRIGHT_MANUAL.SOURCE
)
VALUES
(
goodright_seq.nextval,
p_APPLIANT_TLT,
p_APPLIANT_NME,
p_APPLICANT_SURNME,
p_COMPANY_NME,
lower(p_ID_CDE),
p_ADD1,
p_OCCUPATION1,
p_REMARK1,
p_SOURCE
);
COMMIT;
-- OPEN CapRefCursor FOR
--select 'True';
EXCEPTION
WHEN DUP_VAL_ON_INDEX
THEN ROLLBACK;
-- select 'False';
END DB.INSERTGOOD;
/
Here i want to return a string TRUE if the transaction commit successfully and FALSE if transaction rollback.
An Output Variable CapRefCursor is defined but i don't know how to assign true false to that variable and return it.
Thanks in advance.
You have defined a procedure with no OUT parameter, therefore it can not return anything.
You have several options to return success information:
define a function instead of a procedure. A function always returns something, you can define a string to be returned as VARCHAR2 for example in your case.
add an OUT parameter to the procedure. OUT parameters are logically equivalent to function returned values. You can have more than one such parameters.
modify your logic so that the procedure returns nothing when it works and throws an exception when it fails.
I would go with solution (3) because:
solution (1) and (2) are bug-prone: you may easily forget to check the return code in which case your program will continue as if no error had happened in case of failure. Ignoring error is the surest way to transform a benign bug into a monstrosity because it can lead to extensive data corruption. Your program may go on for months without you realising that it is intermittently failing!
Exception logic is designed to overcome this problem and makes the code cleaner and clearer. No more ugly if-then-else after every single procedure call. For this reason alone, solutions (1) and (2) are considered code-smell (anti-pattern) when used extensively to return success/error state.
Less code is involved, just remove the EXCEPTION block and let the error propagate.
procedures that fail will undo their work without rolling back the whole transaction if you let the exception propagate (and don't issue intermediate commits).
Finally, in general you should not control transaction logic in your sub-procedures. A procedure that does a single insert is probably part of a larger transaction. You should not let this procedure either commit or rollback. Your calling code, be it PL/SQL, GUI or script should decide if the transaction should move forward and complete or be rolled back.

SDAC -RecordCount and FetchAll

I am using SDAC components to query a SQL Server 2008 database. It has a recordcountproperty as all datasets do and it also has the FetchAll property (which I think it is called packedrecords on clientdatasets). Said that, I got a few questions:
1 - If I set FetchAll = True the recordcount property returns ok. But in this case, when I have a large database and my query returns a lot of lines, sometimes the memory grows a lot (because it is fetching all data to get the recordcount of course).
2 - If I set FetchAll = False, the recordcount returns -1 and the memory does not grow. But I really need the recordcount. And I also wanna create a generic function for this, so I dont have to change all my existent queries.
What can I do to have the recordcount working and the memory usage of the application low in this case?
Please, do not post that I dont need recordcount (or that I should use EOF and BOF) because I really do and this is not the question.
I thought about using a query to determine the recordcount, but it has some problems since my query is going to be executed twice (1 for recordcount, 1 for data)
EDIT
#Johan pointed out a good solution, and it seems to work. Can anybody confirm this? I am using 1 TMSCconnection for every TMSQuery (because i am using threads), so I dont think this will be a problem, will it?
MSQuery1.FetchAll := False;
MSQuery1.FetchRows := 10;
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
ShowMessage(IntToStr(MSQuery1.RecordCount)); //returns 10
MSQuery1.Close;
MSQuery2.SQL.Text := 'SELECT ##rowcount AS num_of_rows';
MSQuery2.Open;
ShowMessage(MSQuery2.FieldByName('num_of_rows').AsString); //returns 289
EDIT 2*
MSQuery1 must be closed, or MSQuery2 will not return the num_of_rows. Why is that?
MSQuery1.FetchAll := False;
MSQuery1.FetchRows := 10;
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
ShowMessage(IntToStr(MSQuery1.RecordCount)); //returns 10
//MSQuery1.Close; <<commented
MSQuery2.SQL.Text := 'SELECT ##rowcount AS num_of_rows';
MSQuery2.Open;
ShowMessage(MSQuery2.FieldByName('num_of_rows').AsString); //returns 0
Run your query as normal, than close the query
MSQuery1.SQL.Text := 'select * from cidade';
MSQuery1.Open;
MSQuery1.Close;
You need the close otherwise SQL-server has not closed the cursor yet, and will not register the query as 'completed'.
and run the following query right afterwards:
SELECT ##rowcount AS num_of_rows
This will select the total number of rows your last select read.
It will also select the number of rows your update/delete/insert statement affected.
See: http://technet.microsoft.com/en-us/library/ms187316.aspx
Note that this variable is per connection, so queries in other connections do not affect you.
I use ODAC and I believe SDAC inherits from the same base classes and works the same way as ODAC. In ODAC, there is an option called QueryRecCount under Options in your query component. Look for TCustomDADataSet.Options.QueryRecCount in your help file.
Setting QueryRecCount = True and FetchAll = False will reduce your memory usage and give you the record count. But SDAC will run a second query in the background to get the record count so it does add a little bit of extra time to your query.
Take a look at the Devart forum entry at http://www.devart.com/forums/viewtopic.php?t=8143.

Resources