Data Management

Clone database objects with dynamic SQL

It's possible to take a copy of one table and make a copy of it in another schema. However, if you want to do the same thing with any other database object, such as packages or functions, there is no simple command. Scott Stephens creates a package that allows you to clone most database objects with dynamic SQL.

Warning: The following article can create serious security holes if used carelessly. Please understand the ramifications thoroughly before using it in a non-test environment.

If you want to take a copy of one table and make a copy of it in another schema (under another username), you can use the SQL*Plus COPY command, or a simple <code>"create table foo as select * from other.foo;"</code>, as long as you have select privileges on the other user's table.

However, if you want to do the same thing with any other database object, such as packages, procedure, functions, or views, there is no simple command. You need to manually find the code and run it against the new instance. It would be nice if you could simply say "clone object" and have another user's object under your own instance. This would be particularly useful for creating a test schema for development. In this example, I create a package that allows you to do this for most objects (with some limitations).

The main problem to tackle is getting the source code. The object owner has access to source code through the view USER_SOURCE. Other users can only see selected source code through the view ALL_SOURCE if the object has been granted EXECUTE privileges to that user. We can encapsulate the request "give me the source for your object" through a single procedure. View Listing A. Notice that I didn't say "UPPER(name)." This means you must match the case of the procedure. (Java stored procedures use a lot of mixed case.)

If this procedure is owned by the object owner, then the source code for that object will be exported through a REF CURSOR variable. If this procedure is granted EXECUTE privilege to another user, then that user will be able to call this procedure and view the source code for any database object—even to objects that aren't granted to them and to objects that don't show up in ALL_SOURCE, such as TYPE declarations. To show that this works, try the code in Listing B from SQL*Plus.

With the source code from the object owner, the caller can create the object in their own schema. We need Dynamic SQL to build the object from text strings. Another problem to deal with is that the source for some database objects can exceed 32,767 characters, the maximum limit for VARCHAR2 strings. That rules out using a simple VARCHAR2 string to hold the SQL. There is a little-used variant of DBMS_SQL.PARSE that allows you to store source code as a table of VARCHAR2 lines. It can be used to store SQL much larger than the 32,767 limit. (In a real application, you would also need to wrap any lines that exceed 256 characters since USER_SOURCE stores up to 4,000 character lines.) Listing C contains the code that you would use if you were going to implement this as a SQL*Plus script with command-line arguments.

As an example, say a schema wants to clone a function called "FOO" in SCOTT's schema. SCOTT would own a copy of CLONER and grant execute privilege to users allowed to clone SCOTT's objects. The other user could issue the SQL*Plus command:

SQL> connect ANOTHER USER
SQL> @clone SCOTT FUNCTION FOO

This works but it still requires SQL*Plus sessions and scripts. I want everything in SQL so any application can perform this function. To do that, we need to take the previous SQL*Plus script and wrap it in another dynamic SQL statement, in which we can concatenate the owner name and bind in the owner and type parameters. You can do this with the code in Listing D.

Notice that I have to add AUTHID CURRENT_USER so the procedure will have enough privileges to create database objects. Now you can call this procedure from any product capable of calling Oracle stored procedures. Here is the same example as above but in SQL*Plus:

SQL> @clone_obj
SQL> exec clone_obj('SCOTT','FUNCTION','FOO');

There is some security in place but not much. Only users who have been granted execute to GET_SOURCE can read the source code for objects that they can't normally view. In an ideal situation, you might create a user that only contains "GET_SOURCE" and a set of template objects.

The program isn't complete, but it's an example of what is possible. Besides needing to wrap the 4,000 character source into the 256 character destination lines, it could also be extended to scan for the object name and insert an owner name so a DBA owner could clone objects from one user to another.

TechRepublic's Oracle newsletter covers automating Oracle utilities, generating database alerts, solving directed graph problems, and more. Automatically subscribe today!

Editor's Picks

Free Newsletters, In your Inbox