I need a stored procedure with dynamic select statement, in my case only adding desired column names in select. This is what I created, but I'm not sure If It's safe for SQL injections:
CREATE OR REPLACE PROCEDURE MySchema.Search(
columns IN VARCHAR2,
res_out OUT SYS_REFCURSOR)
IS
BEGIN
OPEN res_out FOR
'SELECT ' || columns ||' FROM MySchema.Table1';
END Search;
Is this fine or is It not safe ? When reading all examples I haven't noticed anything easy as this, but It works. If It's not safe for SQL injections, please show me how I should do It. Thanks for help in advance !
I will suggest to you use your PL/SQL like this: in the below PL/SQL it ensures that, if any of the SQL Injection statement is trying to invoke it will stop.
CREATE OR REPLACE PROCEDURE MySchema.Search(
columns IN VARCHAR2,
res_out OUT SYS_REFCURSOR)
IS
v_columns VARCHAR2(4000);
BEGIN
select listagg(column_name,',') within group(order by 1)
INTO v_columns
from all_tab_columns
where owner = 'MYSCHEMA'
and table_name = 'TABLE1'
and column_name in (select regexp_substr(columns,'[^,]+', 1, level)
from dual
connect by regexp_substr(columns, '[^,]+', 1, level) is not null
);
OPEN res_out FOR
'SELECT ' || v_columns ||' FROM MySchema.Table1';
END Search;
Related
create or replace procedure create_src_table()
returns table (name varchar, age number(10,0),dob date)
language sql as
$$
declare
create_query varchar;
res resultset;
begin
create_query := `CREATE TEMPORARY TABLE SRC_TEMP_TBL AS SELECT * FROM
(WITH CTE_1 AS (SELECT * FROM "DB"."DW"."USER_TBL" WHERE name='rahul'),
CTE_2 AS (SELECT * FROM CTE_1 WHERE CAST(DOB AS DATE)<2000-05-01)
SELECT name,age,dob FROM CTE_2 limit 10)`;
res := (execute immediate : create_query);
return table(res);
end;
$$;
call create_src_table();
Could someone please help in how to write multiline sql query. I found few answers that indicate using backtick in javascript but not sure how to achieve it in sql.
Multi-statement SQL should be wrapped inside BEGIN ... END block:
EXECUTE IMMEDIATE 'BEGIN
statement1;
statement2;
...
END;';
Sample:
I have a stored procedure in Informix that uses external tables to unload data to a disk file from a select statement. Is it possible to give the DISK file name as a parameter to the stored procedure? My stored procedure is as follows:
create procedure spUnloadData(file_name_param varchar(64))
create temp table temp_1(
col_11 smallint
) with no log;
INSERT INTO temp_1 select col1 from data_table;
CREATE EXTERNAL TABLE temp1_ext
SAMEAS temp_1
USING (
--DATAFILES ("DISK:/home/informix/temp.dat")
DATAFILES("DISK:" || file_name_param )
);
INSERT INTO temp1_ext SELECT * FROM temp_1;
DROP TABLE temp1_ext ;
DROP TABLE temp_1;
END PROCEDURE;
I am trying to pass in the DISK filename as a parameter(from my shell script, timestamped).
Any help is appreciated.
NH
You would have to use Dynamic SQL in the stored procedure — for example, the EXECUTE IMMEDIATE statement.
You create a string containing the text of the SQL and then execute it. Adapting your code:
CREATE PROCEDURE spUnloadData(file_name_param VARCHAR(64))
DEFINE stmt VARCHAR(255); -- LVARCHAR might be safer
CREATE TEMP TABLE temp_1(
col_11 SMALLINT
) WITH NO LOG;
INSERT INTO temp_1 select col1 from data_table;
LET stmt = 'CREATE EXTERNAL TABLE temp1_ext ' ||
'SAMEAS temp_1 USING DATAFILES("DISK:' ||
TRIM(file_name_param) ||
'")';
EXECUTE IMMEDIATE stmt;
INSERT INTO temp1_ext SELECT * FROM temp_1;
DROP TABLE temp1_ext;
DROP TABLE temp_1;
END PROCEDURE;
Untested code — the concept should be sound, though.
This assumes you are using a reasonably current version of Informix; the necessary feature is in 12.10, and some version 11.70 versions too, I believe.
I made slight changes to my code to unload data(as Informix default '|' separated fields). Instead of using a temp table, I was able to select columns directly into an external table dynamically.
I want to create a stored procedure where I can pass in variable to the WHERE clause below.
DROP TABLE fan0ia_mstr.Store_List;
CREATE TABLE fan0ia_mstr.Store_List AS(
SELECT
a11.ANA_Code,
a11.Premise_Name_Full,
a11.Store_Code,
a11.Estates_Segment,
a12.Post_Code
FROM Store_Dimension_Hierarchy a11
JOIN Location a12
ON a11.ANA_Code = a12.ANA_Code
WHERE a11.Area_Desc = 'VARIABLE' ) WITH DATA
PRIMARY INDEX (ANA_Code)
The VARIABLE will be a character string. I don't need to display the results, I just want the table to be created.
Also how do I trap any errors e.g. if the table doesn't exist for some reason I still want it to be created
thanks
As you don't have variable database/table/column names you simply need to wrap your existing code (slightly modified) into a Stored Procedure:
replace procedure myproc(IN variable varchar(100))
begin
BEGIN
-- simply try dropping the table and ignore the "table doesn't exist error"
DECLARE exit HANDLER FOR SQLEXCEPTION
BEGIN -- 3807 = table doesn't exist
IF SQLCODE <> 3807 THEN RESIGNAL; END IF;
END;
DROP TABLE fan0ia_mstr.Store_List;
END;
CREATE TABLE fan0ia_mstr.Store_List AS(
SELECT
a11.ANA_Code,
a11.Premise_Name_Full,
a11.Store_Code,
a11.Estates_Segment,
a12.Post_Code
FROM Store_Dimension_Hierarchy a11
JOIN Location a12
ON a11.ANA_Code = a12.ANA_Code
WHERE a11.Area_Desc = :variable ) WITH DATA
PRIMARY INDEX (ANA_Code);
end;
Of course a DELETE/INSERT or a Temporary table might be more efficient.
Edited... second option needs a execute immediate too...
CREATE PROCEDURE PROCEDURE1(
V_AREA_DESC IN VARCHAR2 )
AS
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE fan0ia_mstr.Store_List';
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
EXECUTE IMMEDIATE 'CREATE TABLE fan0ia_mstr.Store_List AS
(SELECT a11.ANA_Code,
a11.Premise_Name_Full,
a11.Store_Code,
a11.Estates_Segment,
a12.Post_Code
FROM Store_Dimension_Hierarchy a11
JOIN Location a12
ON a11.ANA_Code = a12.ANA_Code
WHERE a11.Area_Desc = ''' || v_area_desc || '''
) WITH DATA PRIMARY INDEX (ANA_Code)';
END PROCEDURE1;
but you can avoid drop / create with truncate / insert
CREATE PROCEDURE PROCEDURE1(
V_AREA_DESC IN VARCHAR2 )
AS
BEGIN
execute immediate 'truncate TABLE fan0ia_mstr.Store_List';
insert into fan0ia_mstr.Store_List (SELECT a11.ANA_Code,
a11.Premise_Name_Full,
a11.Store_Code,
a11.Estates_Segment,
a12.Post_Code
FROM Store_Dimension_Hierarchy a11
JOIN Location a12
ON a11.ANA_Code = a12.ANA_Code
WHERE a11.Area_Desc = v_area_desc
);
commit;
END PROCEDURE1;
I have a stored procedure for Oracle 10g that needs to create a new row in the table and not create duplicates.
The table allows duplicates, so long as all columns are not the same. This is because the last two columns can differ in values.
With that being said, when I try to store my procedure I get the following flags:
Line # = 10 Column # = 1 Error Text = PL/SQL: SQL Statement ignored
Line # = 13 Column # = 3 Error Text = PL/SQL: ORA-00933: SQL command not properly ended
The procedure looks fine [given I haven't added a WHERE clause for an insert before like this].
So either my format isn't what it should be or my logic is off.
Whatever the case may be, I have tried finding examples online and on stackoverflow and have fallen short.
Any suggestions on how I should tweak this?
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
VALUES (val_ID, val_cat, val_sub)
WHERE ((column1 != val_ID) and (column2 != val_cat) and (column3 != val_sub));
COMMIT;
END;
I have even removed the "(" in WHERE clause and nothing changed.
UPDATE:
tried the suggestion and all errors are gone [however the record didn't create]
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
SELECT val_ID, val_cat, val_sub
FROM dual
MINUS
SELECT val_ID, val_cat, val_sub
FROM tablename;
The insert statement doesn't have a where clause. You could emulate it, though, by using an insert-select statement:
INSERT INTO tablename (column1, column2, column3)
SELECT val_ID, val_cat, val_sub
FROM dual
MINUS
SELECT column1, column2, column3
FROM tablename;
#Mureinik 's example did negate all my errors; however, when tested it didn't create the new row.
So my current work around will be a query in VB.net checking if the value exists and then implementing a simple insert stored procedure:
//Make select statement and look at table for whether more than 0 rows shows up. If 0 rows, then execute stored procedure
If DsAds1.Tables(0).Rows.Count = 0 Then
...do stored procedure
End If
Stored Procedure
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
VALUES( val_ID, val_cat, val_sub);
COMMIT;
END;
i'm creating a procedure to update/insert a table using merge statement(upsert).now i have a problem: using procedure parameters i have to do this upsert.
procedure xyz( a in table.a%type,b in table.b%type,....)
is
some local variables;
begin
merge into target_table
using source_table --instead of the source table, i have to use procedure parameters here
on (condition on primary key in the table)
when matched then
update the table
when not matched then
insert the table ;
end xyz;
so how to use procedure parameters instead of source table in merge statement?? or
suggest me a query to fetch the procedure parameters and use it as source table values.
help me please.
Thanks in advance.
I know that I'm eight years late to the party, but I think that I was trying to do something similar to what you were doing, but trying to Upsert based on parameters passed into a stored procedure that returns an empty string on success and an error on failure back to my VB Code. Below is all of my code along with comments explaining what I did, and why I did it. Let me know if this helps you or anyone else. This is my first time answering a post.
PROCEDURE UpsertTSJobData(ActivitySeq_in IN NUMBER,
Owner_in In VARCHAR2,
NumTrailers_in IN NUMBER,
ReleaseFormReceived_in IN NUMBER,
Response_out OUT VARCHAR2) AS
err_num NUMBER;
err_msg VARCHAR2(4000);
BEGIN
--This top line essentially does a "SELECT *" from the named table
--and looks for a match based on the "ON" statement below
MERGE INTO glob1app.GFS_TS_JOBDATA_TAB tsj
--This select statement is used for the INSERT when no match
--is found and the UPDATE when a match is found.
--It creates a "pseudo-table"
USING (
SELECT ActivitySeq_in AS ActSeq,
Owner_in As Owner,
NumTrailers_in As NumTrailers,
ReleaseFormReceived_in As ReleaseFormReceived
FROM DUAL) input
--This ON statement is what we're doing the match on to find
--matching records. This decides whether it will be an
--INSERT or an UPDATE
ON (tsj.Activity_seq = ActivitySeq_in)
WHEN MATCHED THEN
--Here we UPDATE based on the passed in input table
UPDATE
SET OWNER = input.owner,
NUMTRAILERS = input.NumTrailers,
RELEASEFORMRECEIVED = input.releaseformreceived
WHEN NOT MATCHED THEN
--Here we INSERT based on the passed in input table
INSERT (
ACTIVITY_SEQ,
OWNER,
NUMTRAILERS,
RELEASEFORMRECEIVED
)
VALUES (
input.actseq,
input.owner,
input.numtrailers,
input.releaseformreceived
);
Response_out := '';
EXCEPTION
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 3900);
Response_out := TO_CHAR (err_num) || ': ' || err_msg;
END;
Maby something like
DECLARE V_EXISTS NUMBER;
BEGIN SELECT COUNT(*) INTO V_EXISTS FROM TARGET_TABLE WHERE PK_ID = :ID;
IF V_EXISTS > 0 THEN
-- UPDATE
ELSE
-- INSERT
END IF;
END;
Also, you may try to use so-called tempotary table (select from DUAL)
CREATE TABLE TEST (N NUMBER(2), NAME VARCHAR2(20), ADRESS VARCHAR2(100));
INSERT INTO TEST VALUES(1, 'Name1', 'Adress1');
INSERT INTO TEST VALUES(2, 'Name2', 'Adress2');
INSERT INTO TEST VALUES(3, 'Name3', 'Adress3');
SELECT * FROM TEST;
-- test update
MERGE INTO TEST trg
USING (SELECT 1 AS N, 'NameUpdated' AS NAME,
'AdressUpdated' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
-- test insert
MERGE INTO TEST trg
USING (SELECT 34 AS N, 'NameInserted' AS NAME,
'AdressInserted' AS ADRESS FROM Dual ) src
ON ( src.N = trg.N )
WHEN MATCHED THEN
UPDATE
SET trg.NAME = src.NAME,
trg.ADRESS = src.ADRESS
WHEN NOT MATCHED THEN
INSERT VALUES (src.N, src.NAME, src.ADRESS);
SELECT * FROM TEST;
DROP TABLE TEST;
see here
Its very difficult to tell from you question exactly what you what, but I gather you want the table that you are merging into ( or on ) to be dynamic. In that case, what you should be using is the DBMS_SQL package to create dynamic SQL