September/October 2015
Oracle Database 12c brings a variety of useful, powerful enhancements to the PL/SQL language. Your DBAs may not have upgraded to Oracle Database 12.1, but it’s bound to happen soon. And when it does, wouldn’t you like to be ready to take advantage of the new features? Use Oracle Magazine quizzes (powered by the PL/SQL Challenge platform at plsqlchallenge.oracle.com) to “learn through play” and actively test and deepen your PL/SQL knowledge.
I create two packages:
CREATE OR REPLACE PACKAGE private_pkg IS PROCEDURE do_this; PROCEDURE do_that; END; / CREATE OR REPLACE PACKAGE BODY private_pkg IS PROCEDURE do_this IS BEGIN DBMS_OUTPUT.put_line ('THIS'); END; PROCEDURE do_that IS BEGIN DBMS_OUTPUT.put_line ('THAT'); END; END; / CREATE OR REPLACE PACKAGE public_pkg IS PROCEDURE do_only_this; END; / CREATE OR REPLACE PACKAGE BODY public_pkg IS PROCEDURE do_only_this IS BEGIN private_pkg.do_this; private_pkg.do_that; END; END; /
Which of the choices, when run before the following blocks, will result in “Done” being displayed once and only once (possibly along with some other text) after the blocks are executed?
BEGIN public_pkg.do_only_this; DBMS_OUTPUT.put_line ('Done'); END; / BEGIN private_pkg.do_this; DBMS_OUTPUT.put_line ('Done'); END; /
a.
CREATE OR REPLACE PACKAGE BODY private_pkg IS PROCEDURE do_this IS l_unit VARCHAR2 (30) := $$PLSQL_UNIT; BEGIN IF l_unit <> 'PUBLIC_PKG' THEN RAISE PROGRAM_ERROR; END IF; DBMS_OUTPUT.put_line ('THIS'); END; PROCEDURE do_that IS l_unit VARCHAR2 (30) := $$PLSQL_UNIT; BEGIN IF l_unit <> 'PUBLIC_PKG' THEN RAISE PROGRAM_ERROR; END IF; DBMS_OUTPUT.put_line ('THAT'); END; END; /
b.
CREATE OR REPLACE PACKAGE private_pkg ACCESSIBLE BY (public_pkg) IS PROCEDURE do_this; PROCEDURE do_that; END; /
c.
CREATE OR REPLACE PACKAGE BODY private_pkg IS PROCEDURE do_this IS BEGIN DBMS_OUTPUT.put_line ('THIS'); END; PROCEDURE do_that IS BEGIN DBMS_OUTPUT.put_line ('THAT'); END; BEGIN DECLARE l_stack VARCHAR2 (32767) := DBMS_UTILITY.format_call_stack; BEGIN IF INSTR (l_stack, 'PUBLIC_PKG') = 0 THEN RAISE PROGRAM_ERROR; END IF; END; END; /
d.
CREATE OR REPLACE PACKAGE BODY private_pkg IS PROCEDURE check_stack IS l_stack VARCHAR2 (32767) := DBMS_UTILITY.format_call_stack; BEGIN IF INSTR (l_stack, 'PUBLIC_PKG') = 0 THEN RAISE PROGRAM_ERROR; END IF; END; PROCEDURE do_this IS BEGIN check_stack; DBMS_OUTPUT.put_line ('THIS'); END; PROCEDURE do_that IS BEGIN check_stack; DBMS_OUTPUT.put_line ('THAT'); END; END; /
e.
CREATE OR REPLACE PACKAGE private_pkg ACCESSIBLE BY (public_pkg.do_only_this) IS PROCEDURE do_this; PROCEDURE do_that; END; /
Quiz Summary
Use the ACCESSIBLE BY clause to restrict which program units can execute a subprogram, even when all code is defined within the same schema.
I want to maximize the availability of the PL/SQL Challenge website, so I sent a proposal for an n-node Oracle Real Application Clusters (Oracle RAC) configuration to my boss.
I created a sequence and a table to store the infrastructure details that would be required for the Oracle RAC setup. The table contains the configuration details of each node in the cluster. Here are simplified versions of the sequence and the table:
CREATE SEQUENCE plch_seq START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE / CREATE TABLE plch_nodes ( node_name VARCHAR2(30), node_pub_ip VARCHAR2(15) UNIQUE, CONSTRAINT plch_nodes_pk PRIMARY KEY(node_name) ) /
Which of the following choices will successfully insert two rows into the PLCH_NODES table?
a.
CREATE OR REPLACE TRIGGER plch_node_name BEFORE INSERT ON plch_nodes FOR EACH ROW DECLARE v_node_name plch_nodes.node_name%TYPE := 'PLCH_RAC'; BEGIN :new.node_name := v_node_name || plch_seq.NEXTVAL; END plch_node_name; / BEGIN INSERT INTO plch_nodes (node_pub_ip) VALUES ('192.168.2.101'); INSERT INTO plch_nodes (node_pub_ip) VALUES ('192.168.2.102'); COMMIT; END; /
b.
DROP SEQUENCE plch_seq / DECLARE v_node_name plch_nodes.node_name%TYPE := 'PLCH_RAC'; v_plch_seq INTEGER := 0; BEGIN SELECT NVL ( MAX (SUBSTR (node_name, LENGTH (v_node_name) + 1)), 1) INTO v_plch_seq FROM plch_nodes; INSERT INTO plch_nodes VALUES (v_node_name || v_plch_seq, '192.168.2.101'); v_plch_seq := v_plch_seq + 1; INSERT INTO plch_nodes VALUES (v_node_name || v_plch_seq, '192.168.2.102'); COMMIT; END; /
c.
DECLARE v_node_name plch_nodes.node_name%TYPE; BEGIN v_node_name := 'PLCH_RAC' || plch_seq.NEXTVAL; INSERT INTO plch_nodes VALUES (v_node_name, '192.168.2.101'); v_node_name := 'PLCH_RAC' || plch_seq.NEXTVAL; INSERT INTO plch_nodes VALUES (v_node_name, '192.168.2.102'); COMMIT; END; /
d.
DROP TABLE plch_nodes / CREATE TABLE plch_nodes ( node_name VARCHAR2(30) DEFAULT 'PLCH_RAC'|| plch_seq.NEXTVAL, node_pub_ip VARCHAR2(15) UNIQUE, CONSTRAINT plch_nodes_pk PRIMARY KEY(node_name) ) / BEGIN INSERT INTO plch_nodes (node_pub_ip) VALUES ('192.168.2.101'); INSERT INTO plch_nodes (node_pub_ip) VALUES ('192.168.2.102'); COMMIT; END; /
e.
BEGIN INSERT INTO plch_nodes (node_name, node_pub_ip) VALUES ('PLCH_RAC' || plch_seq.NEXTVAL , '192.168.2.101'); INSERT INTO plch_nodes (node_name, node_pub_ip) VALUES ('PLCH_RAC' || plch_seq.NEXTVAL , '192.168.2.102'); COMMIT; END; /
Oracle Database offers multiple ways to generate unique values for table columns. One of the most commonly used methods for doing this is to use a sequence, and in Oracle Database 12c, you can now call the NEXTVAL function to get that next unique value right within the default value for the column.
Read a discussion of the question and answers—and look for more challenges—at the PL/SQL Challenge.
I create this package:
CREATE OR REPLACE PACKAGE plch_rec_pkg AS TYPE rec_t IS RECORD ( n1 NUMBER, n2 NUMBER ); PROCEDURE set_rec (n1_in IN NUMBER, n2_in IN NUMBER, rec_out OUT rec_t); PROCEDURE set_values (n1_in IN NUMBER, n2_in IN NUMBER, n1_out OUT NUMBER, n2_out OUT NUMBER); END plch_rec_pkg; / CREATE OR REPLACE PACKAGE BODY plch_rec_pkg AS PROCEDURE set_rec (n1_in IN NUMBER, n2_in IN NUMBER, rec_out OUT rec_t) AS BEGIN rec_out.n1 := n1_in - n2_in; rec_out.n2 := n2_in - n1_in; END set_rec; PROCEDURE set_values (n1_in IN NUMBER, n2_in IN NUMBER, n1_out OUT NUMBER, n2_out OUT NUMBER) IS l_record rec_t; BEGIN set_rec (n1_in, n2_in, l_record); n1_out := l_record.n1; n2_out := l_record.n2; END; END plch_rec_pkg; /
Which of the choices displays the following text after execution?
N1 = -10 N2 = 10
a.
DECLARE l_record plch_rec_pkg.rec_t; BEGIN EXECUTE IMMEDIATE 'BEGIN plch_rec_pkg.set_rec (10, 20, :rec); END;' USING OUT l_record; DBMS_OUTPUT.put_line ('N1 = ' || l_record.n1); DBMS_OUTPUT.put_line ('N2 = ' || l_record.n2); END; /
b.
DECLARE l_record plch_rec_pkg.rec_t; BEGIN plch_rec_pkg.set_rec (10, 20, l_record); DBMS_OUTPUT.put_line ('N1 = ' || l_record.n1); DBMS_OUTPUT.put_line ('N2 = ' || l_record.n2); END; /
c.
DECLARE l_record plch_rec_pkg.rec_t; BEGIN EXECUTE IMMEDIATE 'BEGIN plch_rec_pkg.set_values (10, 20, :n1, :n2); END;' USING OUT l_record.n1, out l_record.n2; DBMS_OUTPUT.put_line ('N1 = ' || l_record.n1); DBMS_OUTPUT.put_line ('N2 = ' || l_record.n2); END; /
As of Oracle Database 12c, you can now bind user-defined types (such as record types and collection types) defined in PL/SQL packages into dynamic SQL statements.
Read a discussion of the question and answers—and look for more challenges—at the PL/SQL Challenge.
Next Steps
READ more Feuerstein
|
DISCLAIMER: We've captured these popular historical magazine articles for your reference. Links etc contained within these article possibly won't work. These articles are provided as-is with no guarantee that the information within them is the best advice for current versions of the Oracle Database.