Oracle Apex - include user on execute immediate query - oracle

I have the following query for an execute immediate:
begin
execute immediate q'['delete from MY_TABLE where USER_EMAIL =
lower(v('APP_USER'))]';
end;
But I'm getting this error:
ora_sqlcode: 00900 Invalid SQL Statement
Anybody knows how can I include the user in there?
Thanks

That's completely invalid statement, it isn't application user that makes it wrong.
Try
execute immediate q'[delete from MY_TABLE where USER_EMAIL = lower(v('APP_USER'))]';
or
execute immediate 'delete from my_table where user_email = ' || lower(:APP_USER);
On the other hand, why do you want to use dynamic SQL? There's nothing dynamic here, so ordinary
delete from my_table where user_email = lower(:APP_USER);
would do.

Related

Dynamic SQL syntax using EXECUTE IMMEDIATE

Dynamic SQL update statement as below:
EXECUTE IMMEDIATE 'UPDATE '||l_prefix||'CRS_CUSTOMERS SET CUSTOMER_SOURCE_REF_ID = '||i.CUSTOMER_REF_ID||' WHERE CUSTOMER_ID = '||i.CUSTOMER_ID;
l_prefix is the parameter hold the prefix of table name, the value assigned is T_
i.CUSTOMER_REF_ID and i.CUSTOMER_ID are the fields fetched from cursor.
The dynamic statement encounter error ORA-00904: invalid identifier when data fetched.
When rewrite it in static way, the query is working fine:
UPDATE T_CRS_CUSTOMERS SET CUSTOMER_SOURCE_REF_ID = i.CUSTOMER_REF_ID WHERE
CUSTOMER_ID = i.CUSTOMER_ID;
I know it must be something wrong on the concatenation of dynamic SQL, just couldn't pinpoint any as the compilation is fine.
WARNING: Dynamic SQL like this is susceptible to SQL Injection attacks. Wherever possible rewrite your dynamic SQL to use bind variables instead.
Instead of constructing your dynamic SQL like this:
L_SQL := 'UPDATE '||l_prefix||'CRS_CUSTOMERS SET CUSTOMER_SOURCE_REF_ID = '||i.CUSTOMER_REF_ID||' WHERE CUSTOMER_ID = '||i.CUSTOMER_ID;
EXECUTE IMMEDIATE L_SQL;
Use this:
L_SQL := 'UPDATE '||l_prefix||'CRS_CUSTOMERS SET CUSTOMER_SOURCE_REF_ID = :REF_ID WHERE CUSTOMER_ID = :CUST_ID';
EXECUTE IMMEDIATE L_SQL USING i.CUSTOMER_REF_ID, i.CUSTOMER_ID;
This is still subject to SQL injection at the l_prefix, but if you control that value programatically it may be OK. Also splitting the construction of the SQL and execution of the SQL into two steps allows you to more easily replace the EXECUTE IMMEDIATE with DBMS_OUTPUT.PUT_LINE(SQL); to check your query for syntax errors. You can also want to DBMS_OUTPUT.PUT_LINE your parameters i.CUSTOMER_REF_ID and i.CUSTOMER_ID to check their values.

How to assign variables using dynamic sql

I need to assign two variables(tmp_x and tmp_y) with dynamic sql, because I need to select the right table at runtime. the sql as below:
updateSql:= 'select p.gis_x,p.gis_y into tmp_x,tmp_y from publish_'
||splitCollection(indexs).city_no ||'.t_customer p
where p.customer_id=:1 and p.gis_x is not null and p.gis_y is not null';
execute immediate updateSql using splitCollection(indexs).CUSTOMER_ID;
the compilation is OK ,but occur the runtime error about "lack of keyword", how can I fix that?
So, following the comments:
This:
updateSql:= 'select p.gis_x,p.gis_y into tmp_x,tmp_y from publish_'
||splitCollection(indexs).city_no ||'.t_customer p
where p.customer_id=:1 and p.gis_x is not null and p.gis_y is not null';
execute immediate updateSql using splitCollection(indexs).CUSTOMER_ID;
Needs to become:
updateSql:= 'select p.gis_x,p.gis_y from publish_'
||splitCollection(indexs).city_no ||'.t_customer p
where p.customer_id=:1 and p.gis_x is not null and p.gis_y is not null';
execute immediate updateSql using splitCollection(indexs).CUSTOMER_ID RETURNING into tmp_x,tmp_y;
The difference is the into clause, which, when used with execute immediate should go in the actual statement and not be part of the Select statement.
Cheers

ORACLE procedure, variables and handling with data

I would need some help, I am trying to build a procedure in ORACLE PL/SQL, that would get the list of unstable indexes from table dba_indexes and this select statement would also build up the statement to fix/alter the index.
So I have SQL statement that generates the alter statements.
SELECT 'alter index '||owner||'.'||index_name||' rebuild online; '
FROM dba_indexes
WHERE status = 'N/A';
And the output is:
alter index OWNER.INDEX_NAME rebuild online;
Which is perfect, and this is also the end result then I just have to get the procedure to work correctly and execute alter statements if there is any if not then just finish the procedure.
Now my question would be how can I save data from SELECT statement to 1 variable ? After that I will just check if variable is NULL or not, if null I finish procedure if not null the I would just execute this variable ?
Is that possible and how ?
Many thanks for help.
You don't necessarily need a variable; you can use a loop over your query:
begin
for s in (
SELECT 'alter index '||owner||'.'||index_name||' rebuild online' as statement
FROM dba_indexes
WHERE status = 'N/A'
)
loop
dbms_output.put_line(s.statement);
execute immediate s.statement;
end loop;
end;
This will simply do the job for every index in your query, or do nothing if the query returns no indexes.

Creating an Oracle User if it doesn't already exist

I am trying to create a script that will create users if they do not already exist.
CREATE USER "Kyle" PROFILE "DEFAULT" IDENTIFIED BY "password" ACCOUNT UNLOCK
WHERE NOT IN //Also tried 'WHERE NOT EXISTS'
(
SELECT username FROM all_users WHERE username = 'Kyle'
)
The following error is given:
SQL Error: ORA-00922: missing or invalid option
I was able to do this in SQL Server 2008 by using:
IF NOT EXISTS
(SELECT name FROM master.sys.server_principals
WHERE name = 'Kyle')
BEGIN
CREATE LOGIN Kyle WITH PASSWORD = 'temppassword' MUST_CHANGE, CHECK_EXPIRATION=ON, CHECK_POLICY=ON
END
Is there a similar way in Oracle to check if a user already exists before attempting to create a new user?
The IF NOT EXISTS syntax available in SQL Server, is not available in Oracle.
In general, Oracle scripts simply execute the CREATE statement, and if the object already exist, you'll get an error indicating that, which you can ignore. This is what all the standard Oracle deployment scripts do.
However, if you really want to check for existence, and only execute if object doesn't exist, thereby avoiding the error, you can code a PL/SQL block. Write a SQL that checks for user existence, and if it doesn't exist, use EXECUTE IMMEDIATE to do CREATE USER from the PL/SQL block.
An example of such a PL/SQL block might be:
declare
userexist integer;
begin
select count(*) into userexist from dba_users where username='SMITH';
if (userexist = 0) then
execute immediate 'create user smith identified by smith';
end if;
end;
/
You need to write a pl/sql block. See an example here
You can check if the user exists in the all_users table using some pl/sql code like:
SELECT count(*) INTO v_count_user
FROM all_users
WHERE username = 'Kyle'
and then use v_count_user in an IF condition to conditionally execute the create user statement.
From the previous answers, it is clear that if not exists is not supported in Oracle. To clarify which error(s) are thrown by Oracle when attempting to create an already existing user (and as a bonus, when attempting to drop a non existing user):
drop user foo;
ORA-01918: user 'foo' does not exist
create user existing_user IDENTIFIED BY existing_user;
ORA-01920: user name 'existing_user' conflicts with another user or role name
The statements above were executed on Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Another way to do it is to create the user in a PL/SQL block and catch the ORA-01920 error. That way, the block does not throw any errors when the user exists.
DECLARE
sqlStatement varchar2(512);
user_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(user_exists, -1920);
BEGIN
sqlStatement := 'CREATE USER "Kyle" ' ||
'IDENTIFIED BY "password" ' ||
'PROFILE "Default" ' ||
'ACCOUNT UNLOCK';
EXECUTE IMMEDIATE sqlStatement;
dbms_output.put_line(' OK: ' || sqlStatement);
EXCEPTION
WHEN user_exists THEN
dbms_output.put_line('WARN: ' || sqlStatement);
dbms_output.put_line('Already exists');
WHEN OTHERS THEN
dbms_output.put_line('FAIL: ' || sqlStatement);
RAISE;
END;
/

Writing a PL SQL change script (sql developer/oracle)

I have an assignment for uni now where I need to write a database change script and then a rollback script. I should also do some simple checks whether the changes has been done or not. I have spent enormous time by writing the scripts because I am not skilled in plsql. The prodcut is here:
-- the check could be more extensive, e.g. checking the type of the column
declare
titleExists number;
begin
select count(*) into titleExists
from user_tab_columns
where table_name = 'TITLE'
and column_name = 'TITLE';
if titleExists > 0 then
execute immediate 'alter table title rename column title to name';
end if;
end;
/
declare
typeExists number;
begin
select count(*) into typeExists
from user_tab_columns
where table_name = 'TITLE'
and column_name = 'TYPE'
and data_type = 'CHAR';
if typeExists > 0 then
execute immediate 'alter table title add (new_type varchar2(12) check (new_type in (''business'', ''mod_cook'', ''psychology'', ''popular_comp'', ''trad_cook'')))';
execute immediate 'update title set new_type = trim(type)';
execute immediate 'alter table title modify (new_type not null)';
execute immediate 'alter table title drop column type';
execute immediate 'alter table title rename column new_type to type';
end if;
end;
/
The first part renames a column and the second part changes columns type and adds a check, basically turns a char column into an enum.
I would really like to know whehter I need to put every alteration in execute immediate block. Is there a simpler way of writing this?
There are a number of issues with the script, but keeping myself limited to the question whether to use execute immediate:
There is not really a simpler way of writing this. The PL/SQL is modifying the database it runs on, so adding for instance the update title occur as a hard-coded section in the PL/SQL is not possible (it would not compile).
Also, sometimes it is possible to merge multiple statements executed through execute immediate in one big PL/SQL being send over. But PL/SQL itself does not support the DDL statements, so you need some way of dynamic SQL.
Note also that each DDL does an implicit commit, so your update is always executed and committed by the following alter table statement.
I think you have done great being this your first PL/SQL assignment.

Resources