APEX - passing substitution string in PL/SQL - oracle

I'm having some difficulty passing session substitution strings in APEX 5.
I have a process in my APEX application which will change the password of a user on a remote database. The username is set from the local LDAP authentication so is held in the APEX substitution string :APP_USER
I'd like to pass this string to the remote database so that I can change the password of the currently logged in user (hope that makes sense!)
I have this process which is executed when the "change password" button is pressed on the APEX page:
DECLARE
cursor_handle INTEGER;
cursor_handle_tmp INTEGER;
BEGIN
-- Open a Cursor on the REMOTE database
cursor_handle := DBMS_SQL.OPEN_CURSOR#remote_db;
-- Parse the "change password"
DBMS_SQL.PARSE#remote_db(cursor_handle,'ALTER USER MYUSER IDENTIFIED BY mypassword',DBMS_SQL.NATIVE);
-- Execute the cursor
cursor_handle_tmp := DBMS_SQL.EXECUTE#remote_db(cursor_handle);
-- Close the cursor
DBMS_SQL.CLOSE_CURSOR#remote_db(cursor_handle);
END;
This executes fine as a proof of concept is both the user and password are hard-coded amd the password of the remote user is changed as expected.
However, if I then use the substitution strings :APP_USER and :P111_PASSWORD I get the error message:
missing user or role name
Which implies that the string isn't being passed correctly to the remote DB.
If I use the v function V('APP_USER'), my code will not validate correctly in the editor as APEX flags it with the followng error:
DBMS_SQL.PARSE#passman_bandev(cursor_handle,'ALTER USER v('APP_USER') IDENTIFIED BY P111_RE_PASSWORD',DBMS_SQL.NATIVE);
ORA-06550: line 11, column 63: PLS-00103: Encountered the symbol "APP_USER" when expecting one of the following: ) , * & = - + < / > at in is mod remainder not rem => <an exponent (**)> <> or != or ~= >= <= <> and or like like2 like4 likec between || multiset member submultiset The symbol ", was inserted before "APP_USER" to continue.
So it looks like I might not be escaping the necessary characters correctly?
However, I've tried many different possible combinations without success.

How about this? Prepare ALTER USER statement in a local variable, and then use it in DBMS_SQL.PARSE.
declare
l_str varchar2(100);
l_my_password varchar2(30) := 'pwd';
begin
l_str := 'alter user ' || :APP_USER || ' identified by ' || l_my_password;
DBMS_SQL.PARSE#remote_db(cursor_handle, l_str, DBMS_SQL.NATIVE);
...
end;

Related

PL/SQL DB Deployment Script

I'm trying to write a deployment script to run with SQL*Plus in a CI/CD pipeline but I can't find my way around what seems to be a very basic issue.
Here's a shortened version of the script release.sql:
DECLARE
vnum NUMBER;
BEGIN
SELECT COUNT(tname) INTO vnum FROM tab WHERE tname = 'DA_VERSION';
IF vnum = 0 THEN -- run create scripts
#ddl/da_001.sql
#ddl/da_002.sql
#dml/version.sql -- set initial version
END IF;
END;
da_001.sql looks like this:
CREATE TABLE TABLE_NAME
(
COLUMN1 NUMBER NOT NULL
, CONSTRAINT TABLE_NAME_PK PRIMARY KEY
(
COLUMN1
)
ENABLE
);
When I run
sqlplus.exe connection_string #release.sql
I get
CREATE TABLE DA_PRODUCTS
*
ERROR at line 6:
ORA-06550: line 6, column 1:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:...
So it doesn't like the CREATE statement at the beginning of da_001.sql but I don't know why. What am I missing here?
Create an installation file for your scripts, ie
install.sql
===========
#ddl/da_001.sql
#ddl/da_002.sql
#dml/version.sql -- set initial version
and then selectively call it via a wrapper in SQL Plus
set feedback off
set pages 0
spool /tmp/runme.sql
select
case when COUNT(tname) = 0 then '##install.sql' else 'pro Skipped install.sql' end
FROM tab WHERE tname = 'DA_VERSION';
spool off
#/tmp/runme.sql
As others have said there is a strict separation between what SQL*PLus understands/is capable of and what is within the SQL and PLSQL languages.
Script handling is one of these distinct areas, which means you cannot executing a SQL script within a PLSQL block.
Also, SQL*Plus does not have any understanding of PLSQL logic constructs.
However, looking at your requirement there might be a way. The answer from #"Connor McDonald" should work. Here is my attempt using a more PLSQL based approach.
This approach uses SQLPLus variables, which can be referenced and amended in both PLSQL and SQLPlus.
First you need a 'No Op' script since when using the SQL*PLus '#' you must specify a valid script name:
noop.sql:
PROMPT No Op
Now your controller script:
-- Declare your variables
VAR script1 VARCHAR2(256)
VAR script2 VARCHAR2(256)
VAR script3 VARCHAR2(256)
DECLARE
vnum NUMBER;
BEGIN
:script1 := 'noop.sql';
:script2 := 'noop.sql';
:script3 := 'noop.sql';
SELECT COUNT(tname) INTO vnum FROM tab WHERE tname = 'DA_VERSION';
IF vnum = 0 THEN -- run create scripts
-- Set variables
:script1 := 'ddl/da_001.sql';
:script2 := 'ddl/da_002.sql';
:script3 := 'dml/version.sql'; -- set initial version
END IF;
END;
/
-- Make variables referencable as SQLPLus defines
COLUMN s1 NEW_VALUE s1
COLUMN s2 NEW_VALUE s2
COLUMN s3 NEW_VALUE s3
SELECT :script1 s1, :script2 s2, :script3 s3
FROM dual;
-- RUN !!
# &&s1
# &&s2
# &&s3
The 3 scriptn variables can be used in PLSQL.
To use as SQL_PLus substition variable (&) we use the COL ... NEW_VALUE command to map a SELECT list column alias to a substituion variable. So we will effectively map scriptn to subs variabl;e sn.
After the PLSQL blocks completes the scriptn variables will have teh value 'noop.sql' or the name of teh script to run.
Then at the end, reference the subs variables in the '#' commands.
Anything with 'noop.sql' will execute a blank script.
I haven't used this myself but you might try something like this (simplified demo):
declare
vnum number := 0;
begin
--select count(tname) into vnum from tab where tname = 'DA_VERSION';
if vnum = 0 then -- include create script
execute immediate q'[
#callthis.sql
]';
end if;
end;
To work within execute immediate, the called script must contain a single statement and no semicolon.

How to test if a variable contains special characters other than numbers , letters and # and _ and $?

There is a variable representing a string :
create or replace procedure create_user(login varchar2)
is
begin
execute immediate 'create user "' || login || '" identified by 1 default tablespace tbs_sse';
execute immediate 'grant create session to "' || login || '"';
execute immediate 'grant select any sequence to "' || login || '"';
end;
How to test if the variable login contains special characters other than numbers , letters and # and _ and $ ?
Just you can check using regexp. Create your regexp based on what you are expecting.
SELECT count(1) into counter
FROM dual
WHERE NOT REGEXP_LIKE (LOGIN_VAR , '^[a-zA-Z0-9_$#]+$');
IF (counter != 0) THEN
--Invalid Login alert, error return
END IF;
For more idea about oracle object naming rules please check link
You test - probably - in an IF statement. If so, you can use something like
........
if regexp_like(login, '[^a-zA-Z0-9_#$]')
then ........
........
Since your actual purpose is to check that the name is suitable as an Oracle identifier, you're probably better off using the built-in function dbms_assert.simple_sql_name, e.g.:
dbms_assert.simple_sql_name(login);
execute immediate 'create user "' || login ...
This will raise ORA-44003 string is not a simple SQL name if login has an unsuitable value.

How to execute same stored procedure multiple times in Oracle SQL Developer?

I have a stored procedure and I need to call it several times with different sets of input. It looks like a script could do the job. How to generate the script? Ask Oracle SQL Developer (Version 4.2.0.17.089)! , So I bring up the following:
(By the way, what is this window called? And can this be accessible from menu bar?)
I select the right stored procedure, specify the correct parameter and click 'Save File'. Here is the saved SQL file:
DECLARE
I_MENU VARCHAR2(200);
ERRMSG VARCHAR2(200);
P_RETURNCUR SYS_REFCURSOR;
BEGIN
I_MENU := '4';
CMS_ACCESS_CONTROL.GETCMSMENUITEMINFO(
I_MENU => I_MENU,
ERRMSG => ERRMSG,
P_RETURNCUR => P_RETURNCUR
);
/* Legacy output:
DBMS_OUTPUT.PUT_LINE('ERRMSG = ' || ERRMSG);
*/
:ERRMSG := ERRMSG;
/* Legacy output:
DBMS_OUTPUT.PUT_LINE('P_RETURNCUR = ' || P_RETURNCUR);
*/
:P_RETURNCUR := P_RETURNCUR; --<-- Cursor
--rollback;
END;
I want to copy and paste the lines between BEGIN and END several times. Each time specify its set of input parameters (different I_MENU). But before doing so, I try to execute the file in SQL Developer. I have not made any modification but get this error:
Error report -
ORA-06550: Line 20, column 22
PLS-00382: expression is of wrong type
ORA-06550: Line 20, column 4
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Questions: 1) Why do I get this error? 2) How to call same stored procedure multiple times with different set of parameters? I want a script to do so as I have to pass the script to another person to execute.
Here is the stored procedure (there is absolutely no / after end getCMSMenuItemInfo)
Procedure getCMSMenuItemInfo(
i_menu in varchar2,
ERRMSG out varchar2,
P_RETURNCUR out SYS_REFCURSOR)
as
begin
open P_RETURNCUR for
SELECT menu_item, menu_url
FROM someMenu
WHERE menuID = i_menu;
end getCMSMenuItemInfo;
Using :ERRMSG and :P_RETURNCUR leading to your error. You're trying to assign value to variable with syntax :variable_a := variable_b which is not allowed in query worksheet. Uncomment legacy output DBMS_OUTPUT.PUT_LINE(); to testing your procedure.
Looking for another answer.
I am giving this solution based on a assumption that you have certain criteria of selecting different menuid's which you will be passing to your procedure "CMS_ACCESS_CONTROL.GETCMSMENUITEMINFO". I will tell you my approach both theoretically and will provide you the code as well.
Solution:
Theoretical Approach:
You can store your menuid's in a different table and loop over that table while calling the procedure. Now you have to decide how many and what different values you want to pass to your procedure.
Sampe Code:
create table store_id
(
id number
);
insert into store_id select distinct menuID from someMenu;
You can insert the different menuid's based on your business requirement.
DECLARE
I_MENU VARCHAR2(200);
ERRMSG VARCHAR2(200);
P_RETURNCUR SYS_REFCURSOR;
BEGIN
for i in (select id from store_id ) loop
I_MENU := i.id;
GETCMSMENUITEMINFO(
I_MENU => I_MENU,
ERRMSG => ERRMSG,
P_RETURNCUR => P_RETURNCUR
);
/* Legacy output:
DBMS_OUTPUT.PUT_LINE('ERRMSG = ' || ERRMSG);
*/
ERRMSG := ERRMSG;
/* Legacy output:
DBMS_OUTPUT.PUT_LINE('P_RETURNCUR = ' || P_RETURNCUR);
*/
P_RETURNCUR := P_RETURNCUR; --<-- Cursor
--rollback;
end loop;
END;
I hope this helps.

Get input from user in PL/SQL procedure

I'm building a procedure which would require to get an input from user to print few details. But when I use & to get values it fails with errors. the logic is as follows..
DBMS_OUTPUT.PUT_LINE('Enter Y to display Unauthorized records OR N to skip the display');
--SELECT &1 INTO lv_choice FROM DUAL;
IF NOT ('&lv_choice'='Y') THEN
DBMS_OUTPUT.PUT_LINE ('RECORDS WILL NOT BE DISPLAYED');
ELSE
DBMS_OUTPUT.PUT_LINE ('RECORDS TO BE DISPLAYED ARE:');
......
I have tried using &1 into dual or directly calling &lv_choice which is failing with PLSQL internal errors.
Any methods to get input from user to proceed further in the procedure?
This isn't possible in PL/SQL - PL/SQL doesn't have access to the terminal(unless you do something like plug in Java or call your program from a something like SQL*Plus(in which you can use commands like ACCEPT/PROMPT before you run the procedure).
The & variables are substitution variables, and are specific to SQL*Plus, not PL/SQL
If you are using some UI Terminal like SQLDeveloper or TOAD, you can achieve it using below code:
CREATE OR REPLACE INPUTPROCEDURE (LV_CHOICE IN VARCHAR2)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Enter Y to display Unauthorized records OR N to skip the display');
--SELECT &1 INTO lv_choice FROM DUAL;
IF lv_choice <> 'Y' THEN
DBMS_OUTPUT.PUT_LINE ('RECORDS WILL NOT BE DISPLAYED');
ELSE
DBMS_OUTPUT.PUT_LINE ('RECORDS TO BE DISPLAYED ARE:');
END INPUTPROCEDURE;
And Invoke the above Procedure like below:
DECLARE
dyn_stmt VARCHAR2(200);
b BOOLEAN := TRUE;
BEGIN
dyn_stmt := 'BEGIN INPUTPROCEDURE(:LV_CHOICE); END;';
EXECUTE IMMEDIATE dyn_stmt USING b;
END;

execute_update pl/sql in jruby fails

I am trying to connect to the oracle db using jdbc driver and that part works fine. After that I want to execute some pl/sql bolck and this is where I am having problem which seems to be syntactical. Can somebody please help me in fixing this? I tried to figure out but could not. Below is the code snippet.
userlist = ['John', 'Sam', 'Lucia']
$userlist.each do|usr|
puts "Working on #{usr}"
stmt = <<-EOF
DECLARE
CURSOR cur IS
SELECT sid, serial#
FROM v$session WHERE username = upper('#{usr}');
BEGIN
FOR rec IN cur
LOOP
EXECUTE IMMEDIATE 'ALTER USER #{usr} IDENTIFIED BY chng';
dbms_output.put_line('Killing sessions which belong to #{usr}...');
EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION '''||session_rec.sid||','||session_rec.serial#'''';
END LOOP;
END;
EOF
#in the initialize method I established the connection and it is working fine.
plsql_stmt = #conn.create_statement
plsql_stmt.execute_update(stmt)
end
Error message:
Working on John
NativeException: java.sql.SQLException: ORA-06550: line 10, column 100:
PLS-00103: Encountered the symbol "'" when expecting one of the following:
. ( * # % & = - + ; < / > at in is mod remainder not rem
return returning <an exponent (**)> <> or != or ~= >= <= <>
and or like like2 like4 likec between into using || bulk
member submultiset
The symbol "*" was substituted for "'" to continue.
Thank you.
Refactor the big bad plsql into a plsql procedure and use https://github.com/rsim/ruby-plsql to call it from ruby
That error message suggest that you have a SQL syntax error, specifically around an apostrophe. I would puts stmt and inspect it.

Resources