Is it possible to execute SQL procedure in Delphi 6 BDE TUpdateSQL? - sql

Is it possible to write execute procedure (...with arguments...) (for Firebird 2.1 database) in Delphi 6 BDE TUpdateSQL.ModifySQL? I have tried, but I am receiving error message Update failed even for the simplest SQL procedure. So - are there some restrictions on the statement that can be executed in ModifySQL? I know (and I am using extensively), that it is possible to write execute procedure (and more complex statements) in the Delphi IBX TIBDataSet.ModifySQL (or in other similar properties of IBX components). Should I avoid execute procedure in TUpdateSQL or are there tricks that I should take into consideration?

René Hoffmann's idea was right - it is possible to execute procedure from the update statement. E.g. the following statement is accepted in ModifySQL:
update target_table set
id = :id,
field1 = :field1,
field2 = (select proc.field2 from additional_actions_procedure(:field2, :param1, :param2) proc)
where
id = :old_id
Delphi Unit DBTables contains the code that explains why exception is raised:
procedure TUpdateSQL.ExecSQL(UpdateKind: TUpdateKind);
begin
with Query[UpdateKind] do
begin
Prepare;
ExecSQL;
if RowsAffected <> 1 then DatabaseError(SUpdateFailed);
end;
end;
So affected rows counting happens. But apparently DBTables accounting of affected rows differs from the affected rows count in IBExpert. IBExpert counts the real affected rows be they affected by execute procedure, update statement or other statements. But DBTables counts (my guess) only rows of the target table that are updated by update statement. More investigation of Delphi code can reveal the more exact explanation.
This comment http://www.delphisources.ru/pages/faq/base/tupdatesql_many_queries.html suggest editing DBTables file.

Related

How to get last query id in a Snowflake transaction

This is about Snowflake database.
I want to know if there is a way to get the last query id in current transaction.
I have a SQL stored procedure with a couple of delete and insert steps. In the beginning of the procedure, I start a transaction and I commit it at the end if no errors occur.
But to be certain that the procedure calculated the result and inserted some new rows in results table, I want to use the query id with result_scan to get the actual number of rows inserted.
I create a Snowflake connection from Python code and parallelly call stored proc with different input parameters.
The function last_query_id works at the session level, so it might return the query id from other parallel stored proc execution. For sequential execution, it works fine. But for multiple parallel executions this results in unexpected behavior.
I have also looked at the QUERY_HISTORY* functions but I don't see a relation between query history based on transaction id. There are only options to find queries for user/session etc.
In SQL Script stored procedures, there is a global variable named SQLID that holds the query ID of the last executed query in the procedure.
https://docs.snowflake.com/en/developer-guide/snowflake-scripting/query-id.html#getting-the-query-id-of-the-last-query
execute immediate $$
declare
query_id_1 varchar;
query_id_2 varchar;
begin
select 1;
query_id_1 := sqlid;
select 2;
query_id_2 := sqlid;
return [query_id_1, query_id_2];
end;
$$
;

How to return the number of affected rows in a HANA stored procedure?

How does one return the number of rows affected from an insert or update statement while inside a SAP HANA stored procedure?
In oracle one would use sql%rowcount but I can't find an equivalent for HANA in their documentation?
For example:
CREATE PROCEDURE procedure_name (p_input)
LANGUAGE SQLSCRIPT AS
BEGIN
define c integer;
insert into some_table values (somevalues);
c := sql%rowcount;
END
UPDATE:
I found the answer on an SAP thread finally. You can run this statement after the insert or update to get the rowcount:
SELECT ::ROWCOUNT into L_C FROM DUMMY;
Not trying to steal internet points, but this should have an answer that's not just in the description of the question. To select row count, use the following:
SELECT ::ROWCOUNT INTO someVariable FROM DUMMY;
DUMMY is a special keyword in HANA and is used to select variables vs selecting from a table. You cannot simply SELECT ::ROWCOUNT INTO someVariable in HANA.
Isn't there a problem with ::ROWCOUNT since it is a system variable, so using it by several sessions simultaneously could produce incorrect results?

Oracle - Output number of rows affected from dynamic merge statement

I have a dynamic sql in a stored procedure with a MERGE statement and and executing it using EXECUTE IMMEDIATE <dynamic_sql>. When I run the merge query through the sql worksheet, it tells me the number of rows merged. How do I retrieve the same information through a dynamic sql?
I would appreciate any efforts towards this question.
After running any SQL statement (static or dynamic), the SQL%ROWCOUNT variable will tell you how many rows were affected.
EXECUTE IMMEDIATE l_sql_stmt;
l_rows_affected := SQL%ROWCOUNT;

Dynamic Cursors

I use a cursor for the statement:
SELECT NAME FROM STUDENT WHERE ROLL = 1;
I used:
CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL = roll;
--roll is a variable I receive via a procedure, and the procedure works fine for the received parameter.
Upon executing this, I am able to retrieve all records with roll = 1.
Now, I need to retrieve the records of a group (possibly via a cursor), just like:
SELECT NAME FROM STUDENT WHERE ROLL IN (2, 4, 6);
But the values in the IN clause are known only at run time. How should I do this? That is, is there any way I could assign parameters to the WHERE clause of the cursor?
I tried using an array in the declaration of the cursor, but an error pops up telling something like: standard types cannot be used.
I used:
CURSOR C IS SELECT NAME FROM STUDENT WHERE ROLL IN (rolls);
--rolls is an array initialized with the required roll numbers.
First, I assume that the parameter to your procedure doesn't actually match the name of a column in the STUDENT table. If you actually coded the statement you posted, roll would be resolved as the name of the column, not the parameter or local variable so this statement would return every row in the STUDENT table where the ROLL column was NOT NULL.
CURSOR C
IS SELECT NAME
FROM STUDENT
WHERE ROLL = roll;
Second, while it is possible to use dynamic SQL as #Gaurav Soni suggests, doing so generates a bunch of non-sharable SQL statements. That's going to flood the shared pool, probably aging other statements out of cache, and use a lot of CPU hard-parsing the statement every time. Oracle is built on the premise that you are going to parse a SQL statement once, generally using bind variables, and then execute the statement many times with different values for the bind variables. Oracle can go through the process of parsing the query, generating the query plan, placing the query in the shared pool, etc. only once and then reuse all that when you execute the query again. If you generate a bunch of SQL statements that will never be used again because you're using dynamic SQL without bind variables, Oracle is going to end up spending a lot of time caching SQL statements that will never be executed again, pushing useful cached statements that will be used again out of the shared pool meaning that you're going to have to re-parse those queries the next time they're encountered.
Additionally, you've opened yourself up to SQL injection attacks. An attacker can exploit the procedure to read any data from any table or execute any function that the owner of the stored procedure has access to. That is going to be a major security hole even if your application isn't particularly security conscious.
You would be better off using a collection. That prevents SQL injection attacks and it generates a single sharable SQL statement so you don't have to do constant hard parses.
SQL> create type empno_tbl is table of number;
2 /
Type created.
SQL> create or replace procedure get_emps( p_empno_arr in empno_tbl )
2 is
3 begin
4 for e in (select *
5 from emp
6 where empno in (select column_value
7 from table( p_empno_arr )))
8 loop
9 dbms_output.put_line( e.ename );
10 end loop;
11 end;
12 /
Procedure created.
SQL> set serveroutput on;
SQL> begin
2 get_emps( empno_tbl( 7369,7499,7934 ));
3 end;
4 /
SMITH
ALLEN
MILLER
PL/SQL procedure successfully completed.
create or replace procedure dynamic_cur(p_empno VARCHAR2) IS
cur sys_refcursor;
v_ename emp.ename%type;
begin
open cur for 'select ename from emp where empno in (' || p_empno || ')';
loop
fetch cur into v_ename;
exit when cur%notfound;
dbms_output.put_line(v_ename);
end loop;
close cur;
end dynamic_cur;
Procedure created
Run the procedure dynamic_cur
declare
v_empno varchar2(200) := '7499,7521,7566';
begin
dynamic_cur(v_empno);
end;
Output
ALLEN
WARD
JONES
Note:As mentioned by XQbert,dynamic cursor leads to SQL injection ,but if you're not working on any critical requirement ,where security is not involved then you can use this .
Maybe you can pass rolls as a set of quoted comma separated values.
e.g. '1', '2' etc
If this value is passes into the procedure in a varchar input variable, the it can be used to get multiple rows as per the table match.
Hence the cursor
SELECT NAME FROM STUDENT WHERE ROLL IN (rolls);
will be evaluated as
SELECT NAME FROM STUDENT WHERE ROLL IN ('1','2');
Hope it helps

Solution to "cannot perform a DML operation inside a query"?

I am using a Data Analysis tool and the requirement I have was to accept a value from the user, pass that as a parameter and store it in a table. Pretty straighforward so I sat to write this
create or replace
procedure complex(datainput in VARCHAR2)
is
begin
insert into dumtab values (datainput);
end complex;
I executed this in SQL Developer using the following statement
begin
complex('SomeValue');
end;
It worked fine, and the value was inserted into the table. However, the above statements are not supported in the Data Analysis tool, so I resorted to use a function instead. The following is the code of the function, it compiles.
create or replace
function supercomplex(datainput in VARCHAR2)
return varchar2
is
begin
insert into dumtab values (datainput);
return 'done';
end supercomplex;
Once again I tried executing it in SQL Developer, but I got cannot perform a DML operation inside a query upon executing the following code
select supercomplex('somevalue') from dual;
My question is
- I need a statement that can run the mentioned function in SQL Developer or
- A function that can perform what I am looking for which can be executed by the select statement.
- If it is not possible to do what I'm asking, I would like a reason so I can inform my manager as I am very new (like a week old?) to PL/SQL so I am not aware of the rules and syntaxes.
P.S. How I wish this was C++ or even Java :(
EDIT
I need to run the function on SQL Developer because before running it in DMine (which is the tool) in order to test if it is valid or not. Anything invalid in SQL is also invalid in DMine, but not the other way around.
Thanks for the help, I understood the situation and as to why it is illegal/not recommended
You could use the directive pragma autonomous_transaction. This will run the function into an independant transaction that will be able to perform DML without raising the ORA-14551.
Be aware that since the autonomous transaction is independent, the results of the DML will be commited outside of the scope of the parent transaction. In most cases that would not be an acceptable workaround.
SQL> CREATE OR REPLACE FUNCTION supercomplex(datainput IN VARCHAR2)
2 RETURN VARCHAR2 IS
3 PRAGMA AUTONOMOUS_TRANSACTION;
4 BEGIN
5 INSERT INTO dumtab VALUES (datainput);
6 COMMIT;
7 RETURN 'done';
8 END supercomplex;
9 /
Function created
SQL> SELECT supercomplex('somevalue') FROM dual;
SUPERCOMPLEX('SOMEVALUE')
--------------------------------------------------------------------------------
done
SQL> select * from dumtab;
A
--------------------------------------------------------------------------------
somevalue
Tom Kyte has a nice explanation about why the error is raised in the first place. It is not safe because it may depend upon the order in which the rows are processed. Furthermore, Oracle doesn't guarantee that the function will be executed at least once and at most once per row.
Just declare a variable to accept the return value, for example:
declare
retvar varchar2(4);
begin
retvar := supercomplex('somevalue');
end;
The select doesn't work because the function is performing an insert, if all it did was return a value then it would work.
Just execute the function in a dummy if ... end if; statement to ignore the return value:
exec if supercomplex('somevalue') then null; end if;
Or execute it as a parameter for put_line procedure to output the return value:
exec dbms_ouput ('result of supercomplex='||supercomplex('somevalue'));
result of supercomplex=done