Stop SQL statements without schema qualifiers - oracle

How can I stop a SQL statement from running if it's missing a schema qualifier? Most of these issues are caught by a development process, but is there a way to stop the ones that slip through the cracks?
For example, this statement should work:
create table jheller.test_table(a number);
This statement should fail:
create table test_table(a number);
Most of these problems are easily caught during development. Usually a lack of privileges will cause an error like ORA-00942: table or view does not exist. Or if the statement runs successfully on the wrong schema it will cause an obvious error that will be caught during testing.
But inevitably some bad statements still make it into deployments that are promoted to the upper environments. This leads to broken deployments and invalid objects created in schemas like SYS. (We shouldn't be running so many deployments as SYS but that's beyond our control.)
It's not necessary to catch 100% of these issues. But catching 99.9% instead of 99% would make a significant difference.

SQL statements without schema qualifiers can be prevented by:
Creating a fake, empty schema on all databases.
Creating a database trigger to prevent creating objects in that schema.
Setting the session variable CURRENT_SCHEMA to that schema at the beginning of deployment scripts.
Installation - run once per database.
--Create a user. It won't be used so lock it and don't grant it any privileges.
create user schema_qualifier_required identified by "[SOME RANDOM PASSWORD HERE]";
alter user schema_qualifier_required account lock;
--Create trigger to prevent any other user from creating objects on it.
create or replace trigger schema_qualifier_required.no_objects_on_schema_qualifier
before ddl on database
/*
Purpose: SCHEMA_QUALIFIER_REQUIRED exists only to help prevent statements
without schema qualifiers. This trigger ensures no objects can be created in
the schema.
Run this command in a session to help ensure schema qualifiers are used:
alter session set current_schema=schema_qualifier_required;
To drop or modify the schema this trigger must be dropped like this:
alter system set "_system_trig_enabled"=false;
drop trigger schema_qualifier_required.no_objects_on_schema_qualifier
alter system set "_system_trig_enabled"=true;
*/
begin
if ora_dict_obj_owner = 'SCHEMA_QUALIFIER_REQUIRED' then
raise_application_error(-20000, 'You cannot create objects in this schema. '||
'Did you forget to use a schema qualifier in your statement?');
end if;
end;
/
Non-qualified statements initially work.
SQL> create table test1(a number);
Table created.
SQL> select * from test1;
no rows selected
ALTER SESSION to prevent future non-qualified statements from running.
SQL> alter session set current_schema=schema_qualifier_required;
Session altered.
Non-qualified statements no longer work.
SQL> create table test2(a number);
create table test2(a number)
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: You cannot create objects in this schema. Did you forget to use a
schema qualifier in your statement?
ORA-06512: at line 3
SQL> select * from test1;
select * from test1
*
ERROR at line 1:
ORA-00942: table or view does not exist
I have not used this method in production yet. If anyone sees problems with this approach or knows of a better way please edit, comment, or add another answer.

Related

When I Create New Table from Oracle Sql Developer. I get this error

Error Messages:
ORA-00604: error occurred at recursive SQL level 1
ORA-00942: table or view does not exist
You're living a dangerous life.
Never, ever create anything in SYS (nor SYSTEM) schema.
Who knows what might be wrong ... maybe you dropped some SYS-owned object. Maybe there's a trigger which does "something" when you create a table.
There's nothing obvious in what you posted. Table, as is, creates normally:
SQL> show user
USER is "SCOTT"
SQL> create table table1
2 (id varchar2(20) not null,
3 ivoice varchar2(20) not null
4 );
Table created.
SQL>

ORA-00942: Table or View not exist connecting with another user

in Oracle SQL developer I got error ORA-00942: Table or View not exist connecting with another user when I do the following:
CREATE USER marta IDENTIFIED BY 'marta';
GRANT SELECT, INSERT ON myTable TO marta;
so then, executing:
CONNECT marta/marta;
INSERT INTO myTable VALUES ('1', 'foo', bar');
got the ORA-00942...
Obviusly, If I use system user I can insert row with no issues.
I searched other answers but I couldnt solve this... what is wrong
Obviusly, If I use system user I can insert row with no issues.
Uh-oh. There's nothing obvious about that. The SYSTEM user should not own a table called MY_TABLE (or whatever application table that is actually named). The SYSTEM user is part of the Oracle database, its schema is governed by Oracle and using it for our own application objects is really bad practice.
But it seems you have created a table in that schema and user MARTA can't see it. That's standard. By default users can only see their own objects. They can only see objects in other schemas if the object's owner (or a power user) grants privileges on that object to the other user.
So, as SYSTEM
grant select on my_table to marta;
Then, as MARTA
select * from system.my_table;
To avoid prefixing the owning schema MARTA can create a synonym:
create or replace synonym my_table for system.my_table;
select * from my_table;
But really, you need to stop using SYSTEM for your own tables.

Oracle schema user cannot create table in procedure

I'm trying to create a temporary table in a procedure:
PROCEDURE pr_create_tmp_bp_table(fp_id NUMBER) IS
tbl_name CONSTANT VARCHAR2(20) := 'BP_TO_DELETE';
BEGIN
-- sanity checks removed for readablity
EXECUTE IMMEDIATE
'CREATE GLOBAL TEMPORARY TABLE ' || tbl_name || ' ' ||
'ON COMMIT PRESERVE ROWS AS ' ||
'SELECT * FROM infop_stammdaten.bp';
END;
If I copy the BEGIN.._END block to a SQL worksheet everything works fine. So I think the user has the right to create a temporary table. If I execute the procedure from the same SQL worksheet I get
Fehlerbericht -
ORA-01031: Nicht ausreichende Berechtigungen
ORA-06512: in "INFOP_STAMMDATEN.PA_DELETE_FP", Zeile 16
ORA-06512: in Zeile 6
01031. 00000 - "insufficient privileges"
*Cause: An attempt was made to perform a database operation without
the necessary privileges.
*Action: Ask your database administrator or designated security
administrator to grant you the necessary privileges
Line 16 (Zeile 16) point to the EXECUTE IMMEDIATE statment that creates the temporary table.
To me this look like the user does not have the same rights in the sql work sheet and when it executes a procedure, also the procedure is in it's own schema.
The answer to your immediate question is that you get ORA-01031: insufficient privileges because your user has the CREATE TABLE privilege granted through a role: the Oracle security model enforces a rule that we can't use privileges granted through roles in PL/SQL. So you need your DBA to grant the CREATE TABLE privilege to your user directly.
Or do you?
Because what you are trying to do does not make sense in Oracle. In Oracle global temporary tables are permanent structures; it's just the data in them which is temporary. So, the correct solution is to build the table once with a normal DDL script, like any other database object. Then you can just insert into the global temporary table as you need to.
You are not the first person on this site to make this mistake (read this pertinent thread). Often it's because people are coming from another database such as SQL Server which has a construct called "temporary table" which is actually different from Oracle's global temporary tables. If that's your scenario then you will be interested in an Oracle 18c new feature called Private Temporary Tables. These are exactly analogous to SQL Server temporary tables. Find out more.

Oracle - Unable to drop tables

This question is related to the one I posted yesterday but with further implications.
The situation is: I'm unable to drop ANY table. Here's an example:
SQL> CREATE TABLE FOO (BAR NUMBER) TABLESPACE SYSTEM
/
Table created.
SQL> SELECT COUNT(1) FROM FOO;
COUNT(1)
----------
0
SQL> DROP TABLE FOO;
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-00942: table or view does not exist
ORA-06512: at line 19
So, the table seems to exist but I'm not capable of dropping it.
Notice the error ORA-00604: error occurred at recursive SQL level 1. If I try to drop a non existing table, this error does not appear:
SQL> DROP TABLE NON_EXISTING_TABLE
ERROR at line 1:
ORA-00942: table or view does not exist
Somehow, the system is unable to find the table at dropping time.
The oracle installation and the DB itself is new (one day old).
EDIT - I retried this test using another tablespace and user (I just created ones) and I got a slightly different behaviour: using SYS, after I got the DROP error I can still SELECT from the table. However, using this new user, after I got the DROP error, I no longer can SELECT from the table.
Solution
We found the problem: the MDSYS.SDO_GEOR_SYSDATA_TABLE was missing, preventing the drop operation.The solution is to restore that table. Here is the complete solution, by Gaurav Soni (by the way, many thanks).
Run the script catmd.sql (located in $ORACLE_HOME/md/admin dir).
The catmd.sql script is the script that loads all objects needed by Oracle spatial in the database. Then drop the user.
you can also refer to oracle metalinks
Steps for Manual Installation of Oracle Spatial Data Option
Dropping user results in ORA-942 against SDO_GEOM_METADATA_TABLE
I'd suggest that you activate SQL tracing (ALTER SESSION SET SQL_TRACE=TRUE;) and try the drop again. This will generate a trace file on the server (in the udump directory) that will show all the SQL the session executed, including recursive statements. This should show you the recursive SQL statement that is failing.
I think the problem is that you created the table on system tablespace. You should create it on the user tablespace or create one to store your data.

Global temporary tables getting data from different session in Oracle

We have a stored procedure in Oracle that uses global temporary tables. In most of our other stored procedures, first thing we do is delete data from global temporary tables. However, in few of the stored procedures we do not have the delete's.
Are there any other options other than adding the delete statements? Can something be done on the Server side to forcefully delete data from those temporary tables when that SP is ran?
the GTT's are defined with ON COMMIT PRESERVE ROWS;
I think your title is misleading: the problem is not "getting data from different session", it is re-using the same session. Terminating a session always flushes a temporary table:
SQL> conn apc
Enter password:
Connected.
SQL> create global temporary table tmp_23 (username varchar2(30))
2 on commit preserve rows
3 /
Table created.
SQL> insert into tmp_23 values (user)
2 /
1 row created.
SQL> commit
2 /
Commit complete.
SQL> select * from tmp_23
2 /
USERNAME
------------------------------
APC
SQL> conn apc
Enter password:
Connected.
SQL> select * from tmp_23
2 /
no rows selected
SQL>
From within a session there is no way to flush a temporary table which has PRESERVE ROWS except by deletion of truncation. There is no way to annotate a stored procedure in the manner you suggest. So I'm afraid that if you are experiencing the problem as you describe it you will have to bite the bullet and add the DELETE (or TRUNCATE) calls to your procedures. Or define the tables with DELETE ROWS; but that probably won't suit your processing.
Incidentally, it seems like you are using temporary tables quite heavily. This is unusual in Oracle systems, because temporary tables are relatively expensive objects (all those writes to disk) and there is normally a more performant way approaching things: e.g. caching data in PL/SQL collections or just using SQL. It is common for developers coming from a non-Oracle background - especially SQL Server - to overuse temporary tables because they are used to that way of working.

Resources