Wednesday, July 8, 2015

Shell Script–Applying a command or a SQL script to all databases in the same host

Hello everyone,

This is my first post on the oracle blogs section!

I really hope the posts I will publish here help some of you to have a more dynamic and comfortable work.

This time I'm publishing a simple KSH script that can allow you to apply a single command or a SQL script to all running databases in a single host.

Now let's explain each part:

  • Functions
    • Utility Functions: refer to this post to check the utility functions I normally use.
      • You'll see debugPrint and msgPrint in most of my shell scripts. Those are used to give a nice format to the message I'm sending to the console/log.
    • Crawl
      • This is the main function that receives the command/sql file to be run in every instance.
  • Main Algorythm
    • for DBNAME in $(ps -ef | grep pmon | cut -d"_" -s -f3 | grep -v ASM)
      • Here, we cycle through all the running instances in the host. Looking for pmon processes and getting only the INSTANCE name from it.
    • crawl "@${2}"
      • In the SQL script mode see how the argument passed to the crawl function is appended to the @ sign, so that we don't have to check for it when running inside the function.
    • crawl "${2}"
      • In the command mode we pass the argument inside double quotes so that it is considered a single argument by the function while running.

With some light modifications, we can get this same schema to work with any of the command line utilities from Oracle. But that's a future project of mine. I will try to add support for RMAN in the next few months as my work allows me to.


Hope that some of you find this post useful.

Tuesday, July 7, 2015

Shell script–Utility functions

Hello fellow DBAs, Oracle and Linux enthusiasts.

Today I'm going to share the script I use to "source" utility functions to the shell scripts I commonly use for my daily DBA tasks.

Let's add some explanation:

  • continueQuestion
    • This function is a simple "Continue? (y/n)" question. As this is used a lot in interactive scripts, seemed like a good idea for me to add it to the utility functions script.
  • debugPrint
    • For debugging, it adds the timestamp prior to the message. Allows you to keep track of what, where and when something is happening.
  • msgPrint
    • Allows you to format in several ways the output that you want to send to console/log file.

I hope you find this useful and makes your job better!

Monday, July 6, 2015

Shell script–Quickly check Oracle listeners

Hello everyone,

Today I would like to share something with you. In my day to day work, I have encountered more than one place where DEV environments are spread through only some hosts. This normally leads to creating several databases on the same host and to create several listeners to be able to split communication through several ports.

Having this, after OS patches or any OS related activity that would require the database software to be shutdown, it's an usual case that people forget to start listeners.  For this I have this useful script:

Enjoy.

Hope this helps in making your job easier.

Friday, September 28, 2012

Configure Solaris 10 DNS client

Let’s get started!

I just got my brand new Solaris 10 server, but somehow although I have a DNS server in my domain, it cannot resolve any names given.  What’s wrong?

We have to first check the existence of some files and the status of the DNS client service.

So, first thing to do:  check the service

# svcs -a | grep dns
 
disabled     XX:XX:XX    svc:/network/dns/server:default
disabled     XX:XX:XX    svc:/network/dns/client:default



Then, let’s check the files.


Does the resolver configuration file exists? Try to see its contents with the following command.



# cat /etc/resolv.conf
 
cat: cannot open /etc/resolv.conf

Let’s then, create the file and edit it to add our name servers.



# touch /etc/resolv.conf
 
# vi /etc/resolv.conf

Add some lines with the following format



domain somedomain.com
nameserver 10.11.12.13
nameserver 14.15.16.17
nameserver 18.19.20.21
search firstdomain.com,seconddomain.com,thirddomain.com

where domain is the domain to look for as default, nameserver is the IP of each name server we have (up to three) and search is the domains that must be added as sufix to the name to look for if none was given.


That’s it with the resolver.  Now, we should go check the naming service switch file, /etc/nsswitch.conf


If you look for some nssswitch file in the /etc directory you’ll find that there are several templates already configured for your use. So backup your current file and replace it with the nsswitch.dns file provided.



# cp /etc/nsswitch.conf /etc/nsswitch.orig
 
# cp /etc/nsswitch.dns /etc/nsswitch.conf

Once you’re done.  Enable the dns client service and check again its status



# svcadm enable /network/dns/client
 
# svcs -a | grep dns
disabled    XX:XX:XX    svc:/network/dns/server:default
online      XX:XX:XX    svc:/network/dns/client:default
 

And welcome to the naming world, Solaris 10.


Hope this helps some of you.

Tuesday, January 24, 2012

Configuring EM DB Console manually

Welcome, for the DBAs out there, any tool that helps understand the  metrics of the DB performance faster is a helping hand in his daily job, the Enterprise Manager (EM) is no exception to this rule.

For the Oracle Database 10g and over, the EM can be automatically configured during the installation using the Oracle Universal Installer (OUI).  Depending on a variety of circumstances, this installation/configuration may fail, and thus we will have to create, or recreate the EM repository and configuration manually, by using the Enterprise Manager Configuration Assistant.

Contrary to what many of you would expect, the EMCA is not a GUI tool, but a command-line tool and thus, it can take us some time to read the entire list of options and clauses that can be added to the command in order to fulfill the task of configuring the EM and creating the necessary repository within the Oracle instance.

So, here you go.  The basic instructions you’ll need to create or recreate your repository and configure the EM are these:

emca –repos drop

emca –repos create

emca –deconfig dbcontrol db

emca –config dbcontrol db

But be sure to have the complete information of your instance because you will need it to fill the blanks when running these commands.  You’ll need: SID, port, password for sysdba and/or sysoper accounts.

If you are still having trouble, check your time zone settings.  For linux, the TZ environment variable should be set and it must match the line agentTZRegion within the EMD.properties file.

Hope this can help you out there.

All your comments are welcome.

Tuesday, September 13, 2011

Oracle SQL Developer UI language and some tips

Welcome to my tech-hut.

I live in Venezuela, so in most places where I work the OS are usually in spanish.  And here comes the thing.  I love SQL Developer.  I’ve been a fan since version 1.5 and some time ago (a lot of time ago, indeed) I switched to a newer version (2.xx) with the (not really good) surprise of the GUI language changed to my locale language.

Well, after some googling I stumbled upon an article where the solution was depicted, easy, beautiful…  Almost wonderful.

All you have to do is look for the configuration file withing $SQLDEV_HOME/IDE, it’s called IDE.conf and add one, or at most, 2 lines.  Mine looks like this:

# For the UI language setting
AddVMOption -Duser.language=en
AddVMOption -Duser.country=US

While connecting with the Oracle Database you can get this error.  If so, make sure to add the following line to the JVM configuration of the client you are using:

AddVMOption -Duser.timezone="-04:30"

That’s it, folks.  Give it a try and leave your comments if you need any further explanations.

Friday, June 3, 2011

Solaris 10 and the Linux Zone

For anyone out there trying to make an installation of Oracle with this configuration, I wish you the best of luck and I hope you don’t spend hours and hours researching, googling and figuring out the best way to make this work.

As we’re already half the way into 2011, past the first decade of the 21st century, you may be asking yourselves (as I did) why the hell a client wants to keep Oracle 9i if there’s 2 new versions out there.

Anyhow, the fact is that we’re stuck with UIX systems that aren’t meant to host Oracle under version 10g and we’re trying to get a dinosaur into a beauty spa.

Here’s what I did after numerous failed attempts a lot of research and an organization of tutorials, whitepapers and some common sense.

Users and Groups

I like to have access with a “support” account that has root privileges in order to be able to work remotely on the server. So, first step after installation, must be create groups and users.

Here’s how and soon I’ll post a full explanation about the commands for user and group management under Solaris 10

First the group: support

Group creation commands
groupadd -g 100 support
Group creation commands

Next the users: support

User creation commands
useradd -u 1000 -c "Support User" -m -d /export/home/support -e "" -g support -G root,adm -s /usr/bin/bash support
User creation commands

Linux Zone

In order to be able to install Oracle Database 9i inside the Solaris box you have to create a container, that is a box inside the box which will have the compatible linux libraries and files needed for the database to run smoothly.

So the next step is to create the Linux Zone.  For this purpose we will need an ISO or CD/DVD of a compatible linux image (RHEL or CentOS 3.5 to 3.8).  As for this point there’s a little problem: Up to 3.8, CentOS ISOs ARE ON HELL OF A GHOST!!!  It’s very HARD TO FIND.  I hope, truly, deeply that when the time comes and you’re reading this guide, you can still find it out there, or at least be able to tell your client to go jump off the roof or something.  Of course, the client could use the common sense (the least common of all senses) and UPGRADE THE GODDAMNED database system!

Once the flames have been thrown, let’s continue.  :)

Another way around is to give it a shot to another RHEL 3 free version, Whitebox Linux 3

Zone creation commands
mkdir -p /export/zones

mkdir -p /export/zones/linux

chmod 700 /export/zones/linux

zonecfg -z linux

linux: No such zone configured
Use 'create' to begin configuring a new zone.

zonecfg:linux> create -t SUNWlx
zonecfg:linux> add net
zonecfg:linux:net> set physical=e1000g0
zonecfg:linux:net> set address=10.10.10.13
zonecfg:linux:net> end
zonecfg:linux> add fs
zonecfg:linux:fs> set dir=/cdrom
zonecfg:linux:fs> set special=/cdrom
zonecfg:linux:fs> set type=lofs
zonecfg:linux:fs> add options=[ro,nodevices]
zonecfg:linux:fs> end
zonecfg:linux> set zonepath=/export/zones/linux
zonecfg:linux> set autoboot=true
zonecfg:linux> verify
zonecfg:linux> commit
zonecfg:linux> exit

zoneadm -z linux install
Zone creation commands

At this point you should have the linux image CD/DVD/ISO available in the selected media so that the installation of the zone goes as painless as it can go.

Once the DVD ISO was in place, I excecuted the command

FAILED Install attempt
# zoneadm -z linux install

Installing cluster 'desktop'

Would you like the system to eject the final install CD when
  installation of 'linux' is complete? ([y]/n)

The final install CD WILL be ejected.

You will need CDs 1 - 3 (or the equivalent DVD) to
install CentOS 3.8.
Installing miniroot for zone 'linux'.
Attempting to locate 30 packages...

Installing 30 miniroot packages...

Setting up the initial lx brand environment.
System configuration modifications complete.
Duplicating miniroot; this may take a few minutes...
Booting zone miniroot...
Miniroot zone setup complete.

Performing full install for zone 'linux'.
Attempting to locate 669 packages...

Installing zone 'linux' from
  CentOS 3.8, CD 1.


Installing 433 packages; this may take several minutes...

Please insert CentOS 3.8, CD 2, or a
  CentOS 3.8 DVD in the removable media
  drive and press <RETURN>.

Install of zone 'linux' from
  CentOS 3.8, CD 2 FAILED.

Attempt to eject '' failed.
Please eject the disc manually.

Installation of CentOS 3.8 to zone
  'linux' FAILED Fri Jun  3 12:10:16 VET 2011.

Cleaning up after failed install...

Installation of zone 'linux' FAILED.

See the log file:
  '/export/zones/linux/root/var/log/linux.install.3986.log'
for details.
 
FAILED Install attempt

Maybe you didn’t notice it, but the installation process doesn’t have any interactivity, so it would be only a matter of guessing when to switch the Images/CDs/DVDs.  But not al hope is lost.  The installation of the zone can be done with the –d <path_to_ISOs> option which will look in that path not for the files but for the ISO images.  It will mount the images as needed and copy all that needs to be copied.

So, after issuing a new  # zoneadm –z linux uninstall command to have the zone once more in the “configured” state, I issued the new and improved version of my installation command.

SUCCESSFUL Install attempt
# zoneadm -z linux install -d /export/CentOS
Installing distribution 'CentOS 3.8'...
Installing cluster 'desktop'
Installing miniroot for zone 'linux'.
Attempting to locate 30 packages...

Installing 30 miniroot packages...

Setting up the initial lx brand environment.
System configuration modifications complete.
Duplicating miniroot; this may take a few minutes...
Booting zone miniroot...
Miniroot zone setup complete.

Performing full install for zone 'linux'.
Attempting to locate 669 packages...

Installing zone 'linux' from
  ISO "/export/CentOS/CentOS-3.8-i386-bin1of3.iso".


Installing 433 packages; this may take several minutes...

Attempting to locate 236 packages...

Installing zone 'linux' from
  ISO "/export/CentOS/CentOS-3.8-i386-bin2of3.iso".


Installing 134 packages; this may take several minutes...

Attempting to locate 102 packages...

Installing zone 'linux' from
  ISO "/export/CentOS/CentOS-3.8-i386-bin3of3.iso".


Installing 102 packages; this may take several minutes...

Completing installation; this may take a few minutes.
Setting up the initial lx brand environment.
System configuration modifications complete.
Installation of CentOS 3.8 to zone
  'linux' completed Fri Jun  3 14:01:17 VET 2011.

Installation of zone 'linux' completed successfully.

Details saved to log file:
    "/export/zones/linux/root/var/log/linux.install.8971.log"
 
SUCCESSFUL Install attempt

After the installation is ready, the Zone will be at a ready state.  And we can proceed to boot it so it get running.

Soon, the Oracle Installation Process over the Linux (CentOS 3.8) Zone

Last words and Special Thanks

I hope that this guide made your experience installing Oracle 9i over a Solaris box one with a lot less pain that it normally is.

A word of advice for Oracle.  Guys, you really REALLY have to make these processes easier.  Or, at least, publish some good guides to help people throughout the whole thing.  We all understand that goes against your high paying support service, but it will give you a lot more customers and maybe bring some from MySQL and PostgreSQL.

Special thanks must go to First Alternative and their excellent tutorials and informative pages.

Tuesday, October 27, 2009

How to: Compare two database structures

A few days ago, I was asked for a task that can seem pretty simple in most cases.  Comparing 2 instances of “the same” database.  When your DB holds a few tables, this can be done manually, using some simple tools, using SQL commands like DESC or DEFINE as needed.  You can even use the Oracle Data Modeler an read the Data Dictionary from the 2 databases and compare them through it.
The problem comes when you so many tables that making an effective comparison would be a highly time-consuming effort.
For the effects of this post, I will state that what I need to compare is table names, column names and data types.  That was the challenge.  Of course, a procedure would pop-up to solve it.
Prerequisites for this procedure are:
  1. 1 schema must be local and be the current schema from which he procedure is called
  2. 1 schema must be reachable through a database link called “templink”
1: CREATE PROCEDURE COMPARE_DB_STRUCTURE IS
2:  ---------------
3:  -- VARIABLES --
4:  ---------------
5:   v_localtable user_tables.table_name%TYPE;
6:   v_localcol  user_tab_cols.column_name%TYPE;
7:   v_localtype  user_tab_cols.data_type%TYPE;
8:   v_localtabnum NUMBER;
9:   v_localcolnum NUMBER;
10:   
11:   v_remotetable user_tables.table_name%TYPE;
12:   v_remotecol  user_tab_cols.column_name%TYPE;
13:   v_remotetype user_tab_cols.data_type%TYPE;
14:   v_remotetabnum NUMBER;
15:   v_remotecolnum NUMBER;
16:  
17:  -------------
18:  -- CURSORS -- 
19:  -------------
20:   CURSOR c_localtable IS
21:   SELECT table_name
22:   FROM user_tables
23:   WHERE table_name NOT IN (
24:    SELECT table_name
25:    FROM user_tab_cols
26:    WHERE column_name = 'STATIC'
27:   )
28:   ORDER BY table_name;
29:  
30:   CURSOR c_remotetable IS
31:   SELECT table_name
32:   FROM user_tables@templink
33:   WHERE table_name NOT IN (
34:    SELECT table_name
35:    FROM user_tab_cols@templink
36:    WHERE column_name = 'STATIC'
37:   )
38:   ORDER BY table_name;
39: 
40:   CURSOR c_localcols IS
41:   SELECT column_name, data_type
42:   FROM user_tab_cols
43:   WHERE table_name = v_localtable
44:   ORDER BY column_name;
45:   
46:   CURSOR c_remotecols IS
47:   SELECT column_name, data_type
48:   FROM user_tab_cols@templink
49:   WHERE table_name = v_remotetable
50:   ORDER BY column_name;
51:  
52:  -------------------------
53:  -- MAIN PROCEDURE BODY --
54:  -------------------------
55:  BEGIN
56:  
57:      dbms_output.enable(1000000);
58:         dbms_output.put_line('Starting procedure: COMPARE DATABASE STRUCTURE...');
59:         dbms_output.put_line('');
60:   -- OPEN TABLE CURSORS
61:   
62:   SELECT COUNT(table_name) INTO v_localtabnum
63:   FROM user_tables
64:   WHERE table_name NOT IN (
65:    SELECT table_name
66:    FROM user_tab_cols
67:    WHERE column_name = 'STATIC'
68:   );
69:   
70:   SELECT COUNT(table_name) INTO v_remotetabnum
71:   FROM user_tables@templink
72:   WHERE table_name NOT IN (
73:    SELECT table_name
74:    FROM user_tab_cols@templink
75:    WHERE column_name = 'STATIC'
76:   );
77: 
78:         dbms_output.put_line('Number of static tables');
79:         dbms_output.put_line('-----------------------');
80:         dbms_output.put_line('');
81:      dbms_output.put_line('Local tables:  '|| v_localtabnum);
82:          dbms_output.put_line('Remote tables: '|| v_remotetabnum);
83:         dbms_output.put_line('');
84:   
85:   OPEN c_localtable;
86:   OPEN c_remotetable;
87:   -- GET TABLES
88:   FETCH c_localtable INTO v_localtable;
89:   FETCH c_remotetable INTO v_remotetable;
90:   
91:   WHILE c_localtable%FOUND LOOP
92:         IF v_localtable = v_remotetable THEN
93:           -- OPEN COLUMN CURSORS
94:           OPEN c_localcols;
95:           OPEN c_remotecols;
96:           
97:           WHILE c_localcols%FOUND LOOP
98:             
99:             IF v_localcol != v_remotecol THEN
100:               dbms_output.put_line('Found problem in table '|| v_localtable ||' - Inconsistency in column names.');
101:             ELSE
102:               IF v_localtype != v_remotetype THEN
103:                 dbms_output.put_line('Found problem in table '|| v_localtable ||' Column '|| v_localcol ||' data types are inconsistent');
104:               END IF; -- DATA TYPE
105:             END IF; -- COLUMN NAME
106:           END LOOP; -- COLUMN LOOP
107:           -- dbms_output.put_line('Finished local table '||v_localtable);
108:           -- dbms_output.put_line('Finished remote table '||v_remotetable);
109:           -- CLOSE COLUMN CURSORS
110:           CLOSE c_localcols;
111:           CLOSE c_remotecols;
112:           -- NEXT TABLE
113:           dbms_output.put_line('Table '|| v_localtable ||' checked');
114:           FETCH c_localtable INTO v_localtable;
115:           FETCH c_remotetable INTO v_remotetable;
116:         ELSE
117:           IF v_localtable > v_remotetable THEN
118:             dbms_output.put_line('Local table '|| v_localtable);
119:             dbms_output.put_line('Table '|| v_remotetable ||' not found in local DB');
120:             FETCH c_remotetable INTO v_remotetable;
121:           ELSE
122:             dbms_output.put_line('Remote table '|| v_remotetable);
123:             dbms_output.put_line('Table '|| v_localtable ||' not found in remote DB');
124:             FETCH c_localtable INTO v_localtable;
125:           END IF; -- TABLE COMPARE
126:         END IF;
127:  END LOOP; -- TABLES LOOP
128:   
129:  -- CLOSE TABLE CURSORS
130:  CLOSE c_localtable;
131:  CLOSE c_remotetable;
132:   
133:         dbms_output.put_line('');
134:         dbms_output.put_line('Process finished');
135:         dbms_output.put_line('----------------');
136:         dbms_output.put_line('');
137: END COMPARE_DB_STRUCTURE;


So, this procedure will alert of any difference found at table, column or data type level through the messages shown through the dbms_output.


This procedure can be easily edited and customized to work on 2 local DBs or even in 2 remote DBs.


Any comments, enhancement proposals and such are welcome.


Hope this can help any of you out there. Enjoy.

Monday, September 28, 2009

Execute Immediate and Dynamic SQL

When working with PL/SQL you may find that you would like to iteratively execute a query with different parameters.  Well, got you good news.  You can!

There’s an instruction that will make those ideas come true.

EXECUTE IMMEDIATE is an Oracle implementation that allows the developer to prepare a statement and then execute it from inside the PL/SQL block, defining also input and/or output parameters.

More so, this instruction will generate some tracing info that can be very useful when trying to model the business logic in the pl/sql program.

In its most basic form, it’s used like this:


EXECUTE IMMEDIATE <sql_query>;

This <sql_query> can be as simple or as complex as we want.  It can even be another pl/sql block.  So, you can use a variable to store the statement, let’s say v_smnt and then execute it with:


EXECUTE IMMEDIATE

v_smnt;

Even more, you can use bind variables or parameters with the simple addition of the USING and RETURNING INTO keywords followed by the pl/sql environment variables you want to use.

All these features make this command very powerful.  It also keep some tracking info in memory that you can access through some special variables, like SQL%ROWCOUNT.

Now that you know what would you be looking for, you can visit the next few links for more info on this command and its uses.

Oracle Docs - Command Diagram and elements explanation

Oracle Docs - Dynamic SQL explanation and some examples

And, of course, you can make questions here and I’ll answer as soon as I can.

Friday, September 11, 2009

How to: I need an empty Database – Part 2

In the first part of his post I showed the way to use IMP/EXP utilities from Oracle to get the DDL of a single schema or an entire database.  Some people don’t fully trust these utilities or just dislike the command line and prefer to work over procedures.  This post will give you another option to get a clean database using the information of the data dictionary on any DBMS or the user_<whatever> tables or the all_<whatever> tables from Oracle.
So, here we go.
Oracle
In particular, for the Oracle DBMS, I’ll use a package.  Let’s call it SCHEMA_CLEANER.  Now, we have to state what do we need?  For any database (that respects the relations between tables and data within those tables), the problems when you have to delete some data begin with the foreign keys being violated.
This indicates that the first thing to do is to DISABLE all those constraints so that we can delete all data in the schema without having to take core of the order we’re deleting that data.  But of course, we have later to take the time to enable all those constraints again, because we want to work with a consistent schema, and leaving the constraints disabled will lead almost for sure to inconsistent data.
Following, this we got 2 specifications for our package: DISABLE_FKS and ENABLE_FKS.
Finally, we need the procedure that will do the bulk delete.  So that’s one more specification for the package, let’s say: DELETE_DATA.
For his example I’ll be running the package procedures from the schema we want to clean.  Nevertheless, this code can be easily modified to be run as a DBA from the SYSTEM schema passing the schema to clean up as a parameter.
In this code, then I’ll use the user_<whatever> tables to get the info I need.  The mod would be to use the all_<whatever> tables and filter through the OWNER field in each table.
So, here is the code:
CREATE OR REPLACE PACKAGE SCHEMA_CLEANER AS

PROCEDURE DISABLE_FKS;
PROCEDURE ENABLE_FKS;
PROCEDURE DELETE_DATA;
PROCEDURE CLEAN_SCHEMA;

END SCHEMA_CLEANER;
/


CREATE OR REPLACE PACKAGE BODY SCHEMA_CLEANER AS

PROCEDURE DISABLE_FKS AS
ctable user_tables.table_name%TYPE;
fkey user_constraints.constraint_name%TYPE;

CURSOR tableList IS
SELECT table_name
FROM user_tables;

CURSOR FKList IS
SELECT constraint_name
FROM user_constraints
WHERE table_name = ctable
AND constraint_name LIKE '%FK%';

BEGIN
dbms_output.enable(1000000);
dbms_output.put_line('About to disable foreign keys...');
OPEN tableList;

FETCH tableList INTO ctable;

WHILE tableList%FOUND
LOOP
dbms_output.put_line('Table found: ' || ctable);
OPEN FKList;
FETCH FKList INTO fkey;
WHILE FKList%FOUND
LOOP
EXECUTE IMMEDIATE ('ALTER TABLE ' || ctable || ' DISABLE CONSTRAINT ' || fkey);
dbms_output.put_line('Foreign Key ' || fkey || ' disabled.');
FETCH FKList INTO fkey;
END LOOP;
CLOSE FKList;
FETCH tableList INTO ctable;
END LOOP;    
CLOSE tableList;
dbms_output.put_line('Foreign keys disabled.');
END DISABLE_FKS;

PROCEDURE ENABLE_FKS AS
ctable user_tables.table_name%TYPE;
fkey user_constraints.constraint_name%TYPE;

CURSOR tableList IS
SELECT table_name
FROM user_tables;

CURSOR FKList IS
SELECT constraint_name
FROM user_constraints
WHERE table_name = ctable
AND constraint_name LIKE '%FK%';

BEGIN
dbms_output.enable(1000000);
dbms_output.put_line('About to enable foreign keys...');
OPEN tableList;

FETCH tableList INTO ctable;

WHILE tableList%FOUND
LOOP
dbms_output.put_line('Table found: ' || ctable);
OPEN FKList;
FETCH FKList INTO fkey;
WHILE FKList%FOUND
LOOP
dbms_output.put_line('Foreign Key found: ' || fkey);
EXECUTE IMMEDIATE ('ALTER TABLE ' || ctable || ' ENABLE CONSTRAINT ' || fkey);
dbms_output.put_line('Foreign Key ' || fkey || ' enabled.');
FETCH FKList INTO fkey;
END LOOP;
CLOSE FKList;
FETCH tableList INTO ctable;
END LOOP;    
CLOSE tableList;
dbms_output.put_line('Foreign keys enabled.');
END ENABLE_FKS;

PROCEDURE DELETE_DATA AS
thetable user_tables.table_name%TYPE;

CURSOR ctable IS
SELECT table_name
FROM user_tables;

BEGIN
dbms_output.enable(1000000);
dbms_output.put_line('Starting data deletion process...');
OPEN ctable;
FETCH ctable INTO thetable;
dbms_output.put_line('Deleting, please wait...');
WHILE ctable%FOUND LOOP
dbms_output.put_line('Table found: '|| thetable);
dbms_output.put_line('Deleting data in '|| thetable);
EXECUTE IMMEDIATE ('DELETE FROM ' || thetable);
dbms_output.put_line(thetable || ' is empty.');
FETCH ctable INTO thetable;
END LOOP;
CLOSE ctable;
dbms_output.put_line('Data deletion process finished.');
END DELETE_DATA;

PROCEDURE CLEAN_SCHEMA AS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
SCHEMA_CLEANER.DISABLE_FKS;
SCHEMA_CLEANER.DROP_DYNAMIC_TABLES;
SCHEMA_CLEANER.DELETE_DATA;
SCHEMA_CLEANER.ENABLE_FKS;
END CLEAN_DB;

END SCHEMA_CLEANER;

/