A PTF can accept an input table and selectively include or exclude its columns in the output. This is controlled via a parameter passed to the implementation package's describe() function. Before we learn how to use this control let's first look at its datatype, dbms_tf.table_t. This datatype, and its releated types, are defined as follows.
TYPE TABLE_T IS RECORD( column TABLE_COLUMNS_T, schema_name DBMS_id, package_name DBMS_id, ptf_name DBMS_id ) ; TYPE TABLE_COLUMNS_T IS TABLE OF COLUMN_T ; TYPE column_t IS RECORD ( description COLUMN_METADATA_T, pass_through BOOLEAN, for_read BOOLEAN); TYPE COLUMN_METADATA_T IS RECORD ( type PLS_INTEGER, max_len PLS_integer DEFAULT -1, name VARCHAR2(32767), name_len PLS_INTEGER, precision PLS_INTEGER, scale PLS_INTEGER, charsetid PLS_INTEGER, charsetform PLS_INTEGER, collation PLS_INTEGER );
We can visualize the data type like this.
table_t.schema_name table_t.package_name table_t.ptf_name table_t.column table_t.column(i).pass_through table_t.column(i).for_read table_t.column(i).description table_t.column(i).description.type table_t.column(i).description.max_len table_t.column(i).description.name table_t.column(i).description.name_len table_t.column(i).description.precision table_t.column(i).description.scale table_t.column(i).description.charsetid table_t.column(i).description.charsetform table_t.column(i).description.collation
Note that table_t.column is a nested table. The examples in the DBMS_TF documentation use a FOR loop to iterate through such data types. This is o.k. when the data is densly packed but, as we'll soon see, this might not always be the case. To stay on the safe side the solutions in this tutorial will always iterate through nested tables using a WHILE loop, which works for both dense and sparse data.
Now let's see how to write a describe() function that hides certain columns, e.g. all columns appearing after the first two columns in the input table.
create or replace package ptf_demo as function describe( p_input_table_metadata in out dbms_tf.table_t ) return dbms_tf.describe_t ; function trim_columns( p_table table ) return table pipelined row polymorphic using ptf_demo ; end; / create or replace package body ptf_demo as function describe( p_input_table_metadata in out dbms_tf.table_t ) return dbms_tf.describe_t as i pls_integer := p_input_table_metadata.column.first ; begin while i is not null loop if i > 2 then p_input_table_metadata.column(i).pass_through := false ; -- hides the column end if; i := p_input_table_metadata.column.next(i) ; end loop ; return null ; end describe ; end ptf_demo ; /
Let's test it.
select * from ptf_demo.trim_columns( scott.dept ) ;
DEPTNO DNAME ---------- ---------- 10 ACCOUNTING 20 RESEARCH 30 SALES 40 OPERATIONS
select * from ptf_demo.trim_columns( scott.emp ) ;
EMPNO ENAME ---------- ---------- 7839 KING 7934 MILLER 7782 CLARK 7369 SMITH 7566 JONES 7876 ADAMS 7902 FORD 7788 SCOTT 7499 ALLEN 7521 WARD 7654 MARTIN 7698 BLAKE 7844 TURNER 7900 JAMES
Now let's see how p_table_metadata might become sparsely populated.
rem rem we use the same package spec created above, rem only the body changes in this example rem create or replace package body ptf_demo as function describe( p_input_table_metadata in out dbms_tf.table_t ) return dbms_tf.describe_t as i pls_integer ; begin -- delete columns numbered 2 and 4 p_input_table_metadata.column.delete(2) ; p_input_table_metadata.column.delete(4) ; -- now the nested table is sparsely populated return null ; end describe ; end ptf_demo ; /
For a baseline, let's take a look at the entire EMP table.
select * from scott.emp ;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 7839 KING PRESIDENT 1981-11-17 5000 10 7934 MILLER CLERK 7782 1982-01-23 1300 10 7782 CLARK MANAGER 7839 1981-06-09 2450 10 7369 SMITH CLERK 7902 1980-12-17 800 20 7566 JONES MANAGER 7839 1981-04-02 2975 20 7876 ADAMS CLERK 7788 1987-07-13 1100 20 7902 FORD ANALYST 7566 1981-12-03 3000 20 7788 SCOTT ANALYST 7566 1987-07-13 3000 20 7499 ALLEN SALESMAN 7698 1981-02-22 1600 300 30 7521 WARD SALESMAN 7698 1981-02-22 1250 500 30 7654 MARTIN SALESMAN 7698 1981-09-28 1250 1400 30 7698 BLAKE MANAGER 7839 1981-05-01 2850 30 7844 TURNER SALESMAN 7698 1981-09-08 1500 0 30 7900 JAMES CLERK 7698 1981-12-03 950 30
Now let's compare this with the output of the PTF to confirm that columns 2 and 4 are hidden.
select * from ptf_demo.trim_columns( scott.emp ) ;
EMPNO JOB HIREDATE SAL COMM DEPTNO ---------- ---------- ---------- ---------- ---------- ---------- 7839 PRESIDENT 1981-11-17 5000 10 7934 CLERK 1982-01-23 1300 10 7782 MANAGER 1981-06-09 2450 10 7369 CLERK 1980-12-17 800 20 7566 MANAGER 1981-04-02 2975 20 7876 CLERK 1987-07-13 1100 20 7902 ANALYST 1981-12-03 3000 20 7788 ANALYST 1987-07-13 3000 20 7499 SALESMAN 1981-02-22 1600 300 30 7521 SALESMAN 1981-02-22 1250 500 30 7654 SALESMAN 1981-09-28 1250 1400 30 7698 MANAGER 1981-05-01 2850 30 7844 SALESMAN 1981-09-08 1500 0 30 7900 CLERK 1981-12-03 950 30
So far the package works as expected. Let's see what happens if we try to iterate through the collection with a FOR loop now.
create or replace package body ptf_demo as function describe( p_input_table_metadata in out dbms_tf.table_t ) return dbms_tf.describe_t as begin -- delete column numbers 2 and 4 p_input_table_metadata.column.delete(2) ; p_input_table_metadata.column.delete(4) ; for i in 1 .. p_input_table_metadata.column.count loop if p_input_table_metadata.column(i).description.name = '"SAL"' then p_input_table_metadata.column(i).pass_through := false ; end if; end loop ; return null ; end describe ; end ptf_demo ; /
select * from ptf_demo.trim_columns( scott.emp ) ;
Error starting at line : 1 in command - select * from ptf_demo.trim_columns( scott.emp ) Error at Command Line : 2 Column : 8 Error report - SQL Error: ORA-62565: The Describe method failed with error(s). ORA-01403: no data found ORA-06512: at "JFUDA.PTF_DEMO", line 15 ORA-06512: at line 22
We encountered an ORA-01403: no data found exception. If we re-write the package to use a WHILE loop we avoid the exception.
create or replace package body ptf_demo as function describe( p_input_table_metadata in out dbms_tf.table_t ) return dbms_tf.describe_t as i pls_integer ; begin -- delete column numbers 2 and 4 p_input_table_metadata.column.delete(2) ; p_input_table_metadata.column.delete(4) ; i := p_input_table_metadata.column.first ; while i is not null loop if p_input_table_metadata.column(i).description.name = '"SAL"' then p_input_table_metadata.column(i).pass_through := false ; end if; i := p_input_table_metadata.column.next(i) ; end loop ; return null ; end describe ; end ptf_demo ; /
This time the query works without error.
select * from ptf_demo.trim_columns( scott.emp ) ;
EMPNO JOB HIREDATE COMM DEPTNO ---------- ---------- ---------- ---------- ---------- 7839 PRESIDENT 1981-11-17 10 7934 CLERK 1982-01-23 10 7782 MANAGER 1981-06-09 10 7369 CLERK 1980-12-17 20 7566 MANAGER 1981-04-02 20 7876 CLERK 1987-07-13 20 7902 ANALYST 1981-12-03 20 7788 ANALYST 1987-07-13 20 7499 SALESMAN 1981-02-22 300 30 7521 SALESMAN 1981-02-22 500 30 7654 SALESMAN 1981-09-28 1400 30 7698 MANAGER 1981-05-01 30 7844 SALESMAN 1981-09-08 0 30 7900 CLERK 1981-12-03 30