< previous page page_224 next page > Page 224 The SHOW_SECURITY.SQL script The following script shows you the grants that have been made on any table you own. You can also see grants made on tables owned by other users, so long as those grants apply to you. The two queries you saw previously have been unioned together, and the columns have been concatenated together to produce readable output. The table name and grantee are shown in the header, which changes each time the grantee changes. DESCRIPTION This script displays information about security on a table. USAGE @SHOW_SECURITY [owner.] table_name SET ECHO OFF SET VERIFY OFF SET FEEDBACK OFF SET PAGESIZE 9999 SET HEADING OFF Dissect the input argument, and get the owner name and table name into two separate substitution variables. The owner name defaults to the current user. SET TERMOUT OFF DEFINE s_owner_name = DEFINE s_synonym_name = COLUMN owner_name NOPRINT NEW_VALUE s_owner_name COLUMN table_name NOPRINT NEW_VALUE s_table_name SELECT DECODE(INSTR(&&1, .), 0,USER, /*Default to current user.*/ UPPER(SUBSTR(&&1,1,INSTR(&&1,.)-1))) owner_name, DECODE (INSTR( &&1,.), UPPER(&&1), /*Only the table name was passed in.*/ UPPER(SUBSTR(&&1,INSTR(&&1,.)+1))) table_name FROM dual; SET TERMOUT ON COLUMN grantee NOPRINT NEW_VALUE s_grantee BREAK ON grantee SKIP PAGE TTITLE LEFT PRIVILEGES GRANTED TO s_grantee - ON s_owner_name . s_table_name Execute a query to show privileges granted at the table level. SELECT grantee, ¦¦ privilege ¦¦ DECODE(grantable, YES, with grant option, ) privilege FROM all_tab_privs WHERE table_schema = &&s_owner_name AND table_name = &&s_table_name UNION SELECT grantee, < previous page page_224 next page > < previous page page_225 next page > Page 225 ¦¦ privilege ¦¦ of column ¦¦ column_name ¦¦ DECODE(grantable, YES, with grant option, ) privilege FROM all_col_privs WHERE table_schema = &&s_owner_name AND table_name = &&s_table_name ORDER BY grantee, privilege; Reset everything back to its default. CLEAR COLUMNS CLEAR BREAK UNDEFINE s_owner_name UNDEFINE s_table_name SET VERIFY ON SET FEEDBACK ON SET HEADING ON SET PAGESIZE 24 Running the SHOW_SECURITY script The following example shows the results of running SHOW_SECURITY against the EMPLOYEE table, after first granting some access to other users: SQL> @show_security employee PRIVILEGES GRANTED TO PUBLIC ON JEFF.EMPLOYEE SELECT UPDATE of column EMPLOYEE_TERMINATION_DATE PRIVILEGES GRANTED TO USER_A ON JEFF.EMPLOYEE DELETE INSERT of column EMPLOYEE_BILLING_RATE with grant option INSERT of column EMPLOYEE_HIRE_DATE with grant option INSERT of column EMPLOYEE_ID with grant option INSERT of column EMPLOYEE_NAME with grant option SELECT PRIVILEGES GRANTED TO USER_B ON JEFF.EMPLOYEE UPDATE of column EMPLOYEE_NAME UPDATE of column EMPLOYEE_TERMINATION_DATE PRIVILEGES GRANTED TO USER_C ON JEFF.EMPLOYEE INSERT INSERT of column EMPLOYEE_ID SELECT UPDATE Finding More Information It's well worth your time to learn more about Oracle's data dictionary views. Anything you want to know about the structure of your database and the objects within it can be found by querying the appropriate view. The definitive reference for these views is the Oracle8 Server Reference manual. However, if you don't < previous page page_225 next page > < previous page page_226 next page > Page 226 have that manual close by, you can get a quick list of useful views by executing the following query: SELECT * FROM dictionary ORDER By table_name; Use the DESCRIBE command on any views of interest. The column names tend to be reasonably self-explanatory. < previous page page_226 next page > < previous page page_227 next page > Page 227 7 Advanced Scripting In this chapter Bind Variables Branching in SQL*Plus Looping in SQL*Plus Validating and Parsing User Input Error Handling SQL*Plus was not designed to be a tool used for writing complex scripts. Its capabilities cannot compare to those of your typical Unix shell, such as the Kom shell or the Bourne shell. Nor does it have anywhere near the capabilities of an advanced scripting tool such as Perl. Most noticeably, SQL*Plus suffers from the following limitations: It lacks an IF statement. There are no looping constructs. It has very limited error handling. There is only marginal support for validating user input. Because of these limitations, SQL*Plus is best suited to executing top-down scripts that don't require any branching, looping, or error handling. Most of the scripts you have seen so far in this book fall into this category. Many are reports that simply set up some column and page formatting, then execute a query. If something goes wrong, you either don't see any data in the report or may see some SQL or SQL*Plus error messages. This limited scripting support is fine when it comes to producing a report. After all, if a report fails, you can simply fix the problem and return the report. But what if you are performing a more complex and critical task? What if you are summarizing some data, posting the summary results to a summary table, and then deleting the underlying detail? In that case, you certainly wouldn't want to delete the data if the summarization failed. You would need some sort of error- handling mechanism. < previous page page_227 next page > < previous page page_228 next page > Page 228 If you need to write scripts of any significant complexity, I strongly encourage you to investigate the use of PL/SQL in your script. PL/SQL is a powerful programming language in its own right, and includes support for error handling, branching, and loopingthe very items that SQL*Plus lacks. Steven Feuerstein's and Bill Pribyl's book, Oracle PL/SQL Programming, 2nd edition (O'Reilly, 1997), is an excellent resource. This chapter will show you some specific ways to work around these limitations of SQL*Plus. Believe it or not, it is possible, using just SQL*Plus, to implement branching and to validate user input. There are even ways to deal with repetitive tasks without resorting to a loop. You will learn about bind variables and see how they better enable you to mix PL/SQL code into your SQL*Plus scripts. You will also see how bind variables can make the job of developing queries for application programs a little bit easier. Bind Variables Back in Chapter 4, Writing SQL*Plus Scripts, you learned about substitution variables. In addition to substitution variables, SQL*Plus supports another type of variable called a bind variable. Unlike substitution variables, bind variables are real variables, having both a datatype and a size. Bind variables were created to support the use of PL/SQL in a SQL*Plus script. They provide a mechanism for returning data from a PL/SQL block back to SQLlus, where it can be used in subsequent queries or by other PL/SQL blocks. Here's a simple example showing how a bind variable can be used: Bind variables can be declared in your SQL*Plus script. VARIABLE s_table_name varchar2(30) Preface a bind variable with a colon to reference it in a PL/SQL block. BEGIN :s_table_name := EMPLOYEE; END; / Bind variables can even be referenced by SQL queries. SELECT index_name FROM user_indexes WHERE table_name = :s_table_name; Bind variables persist until you exit SQL*Plus, so they can be referenced by more than one PL/SQL block. SET SERVEROUTPUT ON < previous page page_228 next page > < previous page page_229 next page > Page 229 BEGIN DBMS_OUTPUT. PUT_LIKE(: s_table_name) ; END; / The scope of a bind variable is the SQL*Plus session in which it was defined. Variables defined within a PL/SQL block, on the other hand, cease to exist once that block has finished executing. Bind variables are defined one level higher (at the SQL*Plus level), so they can be referenced by many PL/SQL blocks and queries. Declaring Bind Variables The SQLlus VARIABLE command is used to declare bind variables. The syntax looks like this: VAR[IABLE] var_name data_type where: VAR[IABLE] Is the command, which can be abbreviated to VAR. var_name Is whatever name you want to give the variable. A variable name must start with a letter, but after that, the name may contain any combination of letters, digits, underscores, pound signs, and dollar signs. 30 characters is the maximum length for a variable name. data_type Is the datatype of the variable. The following datatypes are allowed: NUMBER Results in a floating-point number, and is the same as a NUMBER variable in PL/SQL or a NUMBER column in a table. Unlike PL/SQL, SQL*Plus does not let you specify a length or a precision, so a declaration like NUMBER (9,2) would not be allowed. CHAR[(length)] Results in a fixed-length character string. Length is optional. If it's omitted, you get a one-character string. NCHAR[(length)] Results in a fixed-length character string in the national character set. Length is optional. If it's omitted, you get a one-character string. VARCHAR2(length) Results in a variable-length character string. < previous page page_229 next page > < previous page page_230 next page > Page 230 NVARCHAR2(length) Results in a variable-length character string using the national language character set. CLOB Results in a character large object variable. NCLOB Results in a character large object variable using the national language character set. REFCURSOR Gives you a cursor variable that you can use to return the results of a SQL query from PL/SQL to SQL*Plus. In addition to declaring variables, you can also use the VARIABLE command to list all the variables you have defined. To do that, simply issue the command VARIABLE, with no arguments, as shown in the following example: SQL>VARIABLE variable jenny datatype NUMBER variable jeff datatype CHAR variable sharon datatype VARCHAR2(30) If you are interested in one specific variable, you can specify that variable's name as an argument to the VARIABLE command, for example: SQL> VARIABLE SHARON variable sharon datatype VARCHAR2(30) There is no way to get rid of a variable once you've defined it. Using Bind Variables and Substitution Variables Together Bind variables and substitution variables don't mesh together too well in SQL*Plus. Each was created for a different purpose, and the two types cannot be used interchangeably. For example, bind variables cannot be used with the ACCEPT command, while substitution variables can. Substitution variables can be used with the TTITLE and BTITLE commands that set up page headers and footers, while bind variables cannot. Bind variables are true variables, and can be passed as argu- < previous page page_230 next page > < previous page page_231 next page > Page 231 ments to PL/SQL functions and procedures, while substitution variables cannot. Table 7-1 summarizes the best uses and capabilities of each type of variable. Table 7-1. Bind Variables versus Substitution Variables Task Bind Variable Substitution Variable Comments Display information to the userthe PROMPT command. X Accept input from the userthe ACCEPT command. X Place information from a query into page headers and footersthe TITTLE and BTITLE commands. X Run a query with user-specified criteria in the WHERE clause. X X User input must come through a substitution variable, but you can store the resulting value in a bind variable. Pass values to a PL/SQL function or procedure. X X Substitution variables may be used to pass input arguments as literals. Return information back from a PL/ SQL function or procedure. X Bind variables must be used for OUT and IN OUT arguments. As you can see, each variable type pretty much exists in its own world, completely separate from the other. In fact, you cannot even directly assign values from a bind variable to a substitution variable, or vice versa. The following lines of script, though appearing perfectly reasonable on the surface, simply will not work: DEFINE my_sub_var = VARIABLE my_bind_var VARCHAR2(30) EXECUTE :my_bind_var := Donna Gennick my_sub_var = my_bind_var This lack of interoperability between variable types can be a real frustration when writing scripts. As Table 7-1 shows, there are some tasks for which you can only use a bind variable, and others for which you can only use a substitution variable. Yet SQL*Plus does not let you easily move values back and forth between the two types. Fortunately, there are some relatively straightforward incantations that let you work around this problem. From substitution to bind Putting the value of a substitution variable into a bind variable is actually the easier of the two tasks. Remember that as SQL*Plus executes your script, any substitu- < previous page page_231 next page > < previous page page_232 next page > Page 232 tion variables are simply replaced by their contents as each line of code is executed. You can easily take advantage of this in order to place a value into a bind variable. Take a look at the following short script: DEFINE my_sub_var = Raymond VARIABLE my_bind_var VARCHAR2(30) EXECUTE :my_bind_var := &my_sub_var; EXECUTE is a command that executes one line of PL/SQL code. When SQL*Plus encounters the EXECUTE command, it replaces the reference to the substitution variable with the value of that variable. The command after substitution, the one that is actually executed, looks like this: EXECUTE :my_bind_var := Raymond; Since the assignment involves a character string, the substitution variable must be contained in quotes; otherwise, you would not have a valid string. If you are working with numeric values, you shouldn't quote them. The following example declares a variable of type NUMBER and assigns a value to it: DEFINE imy_sub_num = 9 VARIABLE my_bind_num NUMBER EXECUTE :my_bind_num := &&my_sub_num; So quote your strings, don't quote your numbers, and remember that substitution is occurring. From bind to substitution Taking a value from a bind variable and placing it into a substitution variable is a more difficult task. What you need to do is take advantage of SQL*Plus's ability to store the results of a SELECT statement into a substitution variable. Let's say you have the following in your script: DEFINE my_sub_var = VARIABLE my_bind_var VARCHAR2(30) EXECUTE :my_bind_var := This is a test.; In order to get the value of the bind variable into the substitution variable, you need to go through the following steps: 1. Think up a column name. 2. Execute a COLUMN command for the column name you thought up. Use the NEW_VALUE clause and specify the substitution variable as the target. 3. Turn off terminal output by executing a SET TERMOUT OFF command. This is optional. 4. Issue a SELECT statement that selects the bind variable from Oracle's DUAL table. Use the column name you thought up in step 1 as the column alias. 5. Turn terminal output back on. < previous page page_232 next page > < previous page page_233 next page > Page 233 The SELECT statement will, of course, only return one value, but that value will be a new value for the column in question. The COLUMN command, with its NEW_VALUE clause, causes this value to be stored in the specified substitution variable. It's a roundabout solution to the problem, but when it's all over the substitution variable will contain the value from the bind variable. The important thing is to be sure that the column alias matches the column name used in the COLUMN command. Here's a code sample that demonstrates this technique: Declare one bind variable and one substitution variable. Initialize the bind variable to a value. DEFINE my_sub_var = VARIABLE my_bind_var VARCHAR2(30) EXECUTE :my_bind_var := This is a test.; Store the new value of the my_alias column in my_sub_var. COLUMN my_alias NEW_VALUE my_sub_var SELECT the value of the bind variable. SQL*Plus will store that value in my_sub_var because of the previous COLUMN command. SET TERMOUT OFF SELECT :my_bind_var my_alias FROM dual; SET TERMOUT ON Notice that a column alias is used in the SELECT statement to give the column a name. This same name must be used in the COLUMN command issued prior to the SELECT. If these two don't match, then the assignment won't be made, and my_sub_var will still be blank. Strictly speaking, it's not necessary to turn the terminal output off for the SELECT statement. The variable assignment will still be made, even with the output on. However, if you are writing a script, you probably won't want the results of this SELECT to clutter up the display. Displaying the Contents of a Bind Variable There are two ways to display the contents of a bind variable to a user. You can use the PRINT command, or you can list the variable in a SELECT statement. The PRINT command The format for the PRINT command looks like this: PRI[NT] [bind_variable_name] where: PRI[NT] Is the command, which can be abbreviated to PRI. < previous page page_233 next page > . support the use of PL /SQL in a SQL* Plus script. They provide a mechanism for returning data from a PL /SQL block back to SQLlus, where it can be used in subsequent queries or by other PL /SQL blocks of PL /SQL code. When SQL* Plus encounters the EXECUTE command, it replaces the reference to the substitution variable with the value of that variable. The command after substitution, the one. higher (at the SQL* Plus level), so they can be referenced by many PL /SQL blocks and queries. Declaring Bind Variables The SQLlus VARIABLE command is used to declare bind variables. The syntax