Professional Documents
Culture Documents
DDL operations
Before you can use an index, you must create it. And then, later on, you might February 2003
decide to drop or alter the index. We need to provide callback methods
Volume 10, Number 2
corresponding to each of those DDL actions so that the domain index is
properly integrated. 1 Building Your Own
Extensible Indexes, Part 2
When creating an index Bryn Llewellyn
ORACLE issues this callback when the user issues a create index statement: and Steven Feuerstein
Like_Any_Support.
EXECUTE IMMEDIATE Purge_Cached_Parsed_Words (
'CREATE TABLE ' || v_index_name );
v_index_table_name ||
'( rid ROWID NOT NULL)';
When altering an index
Now we run a cursor over the table with the indexed ORACLE makes a callback to the following function in
column, selecting it and the ROWID. We evaluate response to a user’s alter index statement:
EXECUTE IMMEDIATE
'DROP INDEX ' || The arguments we’ve already met have the same
v_index_table_btree_name; meaning as already explained. i_old_field_value (not used in
EXECUTE IMMEDIATE the Like_Any example) is the value in the indexed column
'TRUNCATE TABLE ' ||
v_index_table_name;
for the row that’s been deleted. What do you code in the
body of this method? You need to update the index data
EXECUTE IMMEDIATE
'CREATE UNIQUE INDEX ' || structures so that they’ll never return the ROWID of the
v_index_table_btree_name || deleted row in response to a query. Depending on your
' ON ' ||
v_index_table_name || '( rid )'; algorithm, you may be able to do that without knowing
the value of the deleted field. You should provide an
DML operations overloading of this function for each of the column
An index won’t do us much good if its contents aren’t datatypes you support with your domain index (unless you
synchronized with that of the underlying database table. So don’t use i_old_field_value and all the column types you
a_cursor attribute of io_scan_ctx. Next we parse, set up the WHILE (v_ridcount < i_nrows AND NOT v_done)
LOOP
define binding, and execute a_cursor for 'select rid from< v_rowcount := DBMS_SQL.fetch_rows (a_cursor);
index-name>$R'. o_rid_list.EXTEND ();
v_ridcount := v_ridcount + 1;
CASE v_rowcount
io_scan_ctx := like_any_methods (DBMS_SQL.open_cursor); WHEN 0
THEN
DBMS_SQL.parse (io_scan_ctx.a_cursor,
v_done := TRUE;
'select rid from ' || v_index_table,
DBMS_SQL.native o_rid_list (v_ridcount) := NULL;
); EXIT;
ELSE
DBMS_SQL.define_column_rowid ( DBMS_SQL.column_value_rowid (a_cursor, 1, v_rid);
io_scan_ctx.a_cursor, 1, v_rid); o_rid_list (v_ridcount) := v_rid;
END CASE;
v_junk := DBMS_SQL.EXECUTE (io_scan_ctx.a_cursor); END LOOP;
Note: We can’t use native dynamic SQL here because Close the ROWIDs cache
we need to hold the cursor context across successive ORACLE calls the following program when all the results
www.oracleprofessionalnewsletter.com Oracle Professional February 2003 5
have been delivered. We can then perform whatever cleanup ODCIConst.CleanupCall to enable us to clean up whatever
is necessary. caching data structures we’ve used. Moreover, on the first
call the io_scan_ctx in/out argument is NULL, enabling us to
member function ODCIIndexClose ( detect that and to initialize our caching data structures as
i_env_not_used in sys.ODCIEnv )
return number purpose-designed attributes of the TYPE that implements
our interface. ORACLE looks after this context (that is, the
Again, as a member function, ODCIIndexClose has access reference to that object) for us between the calls.
to anything we decide is appropriate. We’ll see later that for We don’t take advantage of this mechanism in our
the Keyword INDEXTYPE, we free up our results buffer. For Like_Any example. (But we will in the Keyword
the Like_Any implementation we just close our cursor: INDEXTYPE.) On the first call, we instantiate io_scan_ctx as
an object of the “self ” class, with an arbitrary value for its
Dbms_Sql.Close_Cursor ( a_cursor );
a_cursor attribute. (This won’t be used per se. But it illustrates
the paradigm and is necessary for the control flow.) Then
The Functional Implementation
we invoke local procedure Evaluate_Satisfies_Like_Any to
ORACLE issues this callback when it chooses a query
calculate the return value.
execution plan driven by some other index(es), so that the
On the second and subsequent regular calls, we invoke
row with our domain-indexed field is accessed by ROWID. It
local procedure Evaluate_Satisfies_Like_Any to calculate the
then needs to know if the predicate expressed by the domain
return value.
index’s OPERATOR is satisfied by just this row. In this case,
And on the final cleanup call, we do nothing.
ORACLE will evaluate the comparison, so it just needs a
The local procedure Evaluate_Satisfies_Like_Any selects
value corresponding to the OPERATOR’s return datatype.
the requested ROWID value from the <index-name>$R table.
Listing 2 shows what it looks like.
On finding the row, it sets the return value to 1. On failing to
If we didn’t care about efficiency, we could implement
find the row, it sets the return value to 0:
this by performing explicit analysis of the supplied field
value on demand, reusing some of the code we’d written PROCEDURE evaluate_satisfies_like_any
for index population and maintenance. (This gets a bit IS
v_rid ROWID;
complicated. In rare cases, ORACLE might call our BEGIN
BEGIN
Functional_Implementation when the value doesn’t arise EXECUTE IMMEDIATE 'select rid from '
from a column. Then i_column is NOT NULL and i_index_ctx || v_index_table
|| ' where rid = :v'
of course has no information about the non-existent index. INTO v_rid
We won’t pursue that here.) In the common case, USING i_index_ctx.rid;
v_retval := 1;
Functional_Implementation is called when the value arises EXCEPTION
WHEN NO_DATA_FOUND
from a column. In that case i_column is NULL and i_index_ctx THEN
supplies the information that enables us to access the index. v_retval := 0;
END;
For many INDEXTYPEs, we’ll be able to compute the return END evaluate_satisfies_like_any;
value of Functional_Implementation more efficiently by
accessing the index data than by explicit analysis of the Listing 3 (on page 7) shows the executable section of
supplied field value. Functional_Implementation.
Note: When ORACLE calls the functional The procedure Like_Any_Support.Check_Index_Exists
implementation, it does not switch user to the domain index queries DBA_INDEXES to ensure that the given Like_Any
owner. So if, as we do for the Like_Any INDEXTYPE, you index exists and, if it doesn’t, raises an exception.
implement it as an invoker’s rights program (that is, compiled
with AUTHID CURRENT_USER), then you’ll have to make Supporting logic for Like_Any
sure that the querying user has SELECT privilege on the The Like_Any INDEXTYPE depends on just two very
index storage table(s). straightforward algorithms. We’ve implemented them in the
In general, we might expect our
Functional_Implementation to be called Listing 2. The Functional Implementation.
several times during the execution
of a given query, and we might well static function Functional_Implementation (
i_column in varchar2, /* operator_parameter_list */
be able to increase the efficiency i_param_2 in varchar2, /* " " " */
of the process by caching some data. i_param_3 in number, /* " " " */
... /* " " " */
Thus ORACLE calls us repeatedly /* i_param_2 through i_param_N correspond to the 2nd through Nth arguments
with i_cleanup_flag set to of the operator this implements.
The semantics of its return value exactly match those of that operator. */
ODCIConst.RegularCall. And then one i_index_ctx in sys.ODCIIndexCtx,
io_scan_ctx in out Like_Any_Methods,
last time (when the result we return is i_cleanup_flag in number )
immaterial) with i_cleanup_flag set to return number
Parsing parameter input for CREATE INDEX The local procedure Get_Next_Word advances along
This procedure accepts the end user’s parameter string the compacted parameter string and detects and stores the
(from the create index statement) and parses it into individual next word:
words, relying on the trivial syntax that one or several
spaces separate words. The words are cached in a package- FUNCTION get_next_word
RETURN BOOLEAN
global two-dimensional PL/SQL table whose first index IS
v_more_words BOOLEAN := TRUE;
(index-by-varchar2) is the domain index name and whose BEGIN
second index (index-by-pls_integer) runs over the set of words g_space_pos := INSTR (g_parms, c_one_space, g_start_pos);
g_word_idx := g_word_idx + 1;
for that domain index: CASE g_space_pos > 0
WHEN TRUE
IDX varchar2(61) THEN
-- to define the type of variable to hold g_words_for_index (i_index_name) (g_word_idx) :=
-- an index name SUBSTR (g_parms, g_start_pos,
; (g_space_pos - g_start_pos));
type Words_Tab_t is table of varchar2(64) g_start_pos := g_space_pos + 1;
index by pls_integer WHEN FALSE
-- the set of words parsed out of PARAMETERS THEN
-- for a given index g_words_for_index (i_index_name) (g_word_idx) :=
; SUBSTR (g_parms, g_start_pos,
type Words_For_Index_Tab_t is table of Words_Tab_t (1 + g_length - g_start_pos));
index by IDX%type v_more_words := FALSE;
-- multi-dim array: for all indexes, the PARAMETER words END CASE;
; RETURN v_more_words;
END get_next_word;
The appropriate code is in place in the function The executable section of Parse_Index_Parameter_String
Like_Any_Parsed_Word (see below) to populate this cache for can now be quite terse:
a given domain index the first time it’s mentioned in a
particular session. BEGIN
The local procedure Compact_Spaces transforms all g_parms :=
LTRIM (
occurrences of two or more adjacent spaces in the parameter RTRIM (i_parms, c_one_space),
c_one_space);
string to just one. (The g_ naming convention denotes
variables that are visible from the outer scope.) IF g_parms IS NULL
THEN
RAISE e_no_words;
PROCEDURE compact_spaces END IF;
IS
prev_length g_length%TYPE; compact_spaces ();
BEGIN
prev_length := -1; WHILE get_next_word ()
LOOP
LOOP NULL;
g_parms := END LOOP;
REPLACE ( END;
g_parms, c_two_spaces, c_one_space);
g_length := LENGTH (g_parms);
EXIT WHEN g_length = prev_length; Validate that a word is “like any”
prev_length := g_length;
END LOOP;
This takes the value of a field from the domain-indexed
END compact_spaces; column and the name of the domain index and returns
I
NFORMATION portals based upon the Oracle Portal now have the capability to serve as focal points for online
product hold tremendous promise for data publication. business data. Portals allow an organization to present
In developing portals, a common methodology for information—its own and that garnered from other
implementing the provider and portlet packages involves sources—in pages readable with the ubiquitous Web
creating separate PL/SQL packages for each individual browser, which has quickly become the most common
portlet. While that approach is ideal for publishing computer interface. For the typical company, this approach
individual portlets to the developer community, it creates can enhance the accessibility of all its Web-based
severe maintenance problems for organizations using Portal information, particularly for its dynamic data, in addition
for intranet development involving dozens, and perhaps to the traditional static data. Specifically, a company can
hundreds, of custom portlets. We’ll discuss a way to simplify more easily unify the interfaces to that information,
the structure of portlet libraries in Portal packages. standardize its format, and better control its security.
Oracle Portal is Oracle’s architecture for general portlet
The power of portals and portal development. It was formerly known as WebDB,
Throughout the history of corporate enterprise, particularly and included with the Oracle8i Database. It has since been
in our age of technology, the lifeblood of any organization enhanced and renamed, and is now bundled with Oracle9i
is truly information. The dissemination of it—to the right Application Server (Oracle9iAS). Oracle Portal gives users
people in the right form—is crucial to providing world-class the ability to create and administer information portals, and
service to valued clients, keeping employees informed about in turn, integrate internal as well as public information,
the company in a timely manner, and presenting compelling customize its look and feel, and more effectively deploy
product and service information to prospective customers. Web-based database applications. Every Portal Web page
Yet the underlying technology hasn’t always maximized consists of one or more portlets, each of which accepts and
this potential. delivers data using any browser-capable technology,
In the case of the Internet, the rapid adoption of the including of course the most common one, HTML.
Web provided a worldwide infrastructure for delivering an In order to jumpstart Portal development, Oracle offers
organization’s message. Initially, a manageable number of the Portal Development Kit (PDK), a framework integrated
HTML pages appeared sufficient for publishing limited with Oracle Portal, intended for the creation of portlets and
amounts of static information. But the inevitable growth services on any platform running an Oracle-based portal.
of Web pages on corporate sites—both public and later The PDK is designed with autonomous portlets in mind,
private—resulted in innumerable versioning and making it easier for corporate subscribers to pick and
presentation inconsistencies. These difficulties were choose which portlets to install. Thus, Oracle developers can
proportional to the sophistication and breadth of these create new types of portlets with a common distribution
Internet and intranet sites, and hence to the extent to mechanism, as set forth in the structure of the sample
which each individual firm took advantage of this new provider and portlet packages in the PDK. This article
medium. Recent extensions to HTML (such as CSS, XSL, presents a design for these packages that allows developers
and Dynamic HTML) have helped somewhat, but the to incorporate new portlets without incurring the
technologies aimed at dynamic publishing have invariably maintenance overhead implicit in a separate PL/SQL
resulted in even more moving parts, and a resultant increase package for each one.
in risk, configuration issues, and performance problems.
Furthermore, most of these technologies are file-centric, and The proliferation of portlets
the capabilities of the database, for data management and When considering the adoption of any new technology such
dynamic generation, have been under-utilized. as Oracle Portal, an organization should determine the likely
10 Oracle Professional February 2003 www.oracleprofessionalnewsletter.com
uses for which it can and realistically will be used. Oracle provider’s package body file, named provider_before.pkb in
Portal clearly can be leveraged by firms that derive a our example here.
majority of their business from their public Web sites. But
this is a minority of companies with any significant IT
Listing 1. Redundant code to call portlet functions.
infrastructure, or, for that matter, companies that make an
appreciable use of Oracle products. In fact, not many firms IF ( p_portlet_id = PORTLET_1 ) THEN
RETURN portlet_1_before.get_portlet_info(
worldwide generate their revenue primarily from e- p_provider_id => p_provider_id,
commerce. Moreover, with the dot-com meltdown, that p_language => p_language
);
segment of the corporate world is now even smaller. Thus, ELSIF ( p_portlet_id = PORTLET_2 ) THEN
for typical organizations to justify the costs of using Oracle RETURN portlet_2_before.get_portlet_info(
p_provider_id => p_provider_id,
Portal, they must be able to demonstrate its value aside from p_language => p_language
);
its use for their public sites. (These costs include the product ELSIF ( p_portlet_id = PORTLET_3 ) THEN
licensing expenses, hardware and network infrastructure RETURN portlet_3_before.get_portlet_info(
p_provider_id => p_provider_id,
costs, and the development dollars needed for creating and p_language => p_language
);
maintaining Portal-based sites.) In other words, the bulk of END IF;
the usage for this product will derive from its utility in
creating corporate intranet sites. In this code fragment, retrieving the needed information
As noted earlier, portals may prove to be the ideal about a particular portlet involves executing a separate—yet
tool for aggregating, standardizing, and controlling the almost identical—(ELS)IF clause, and then calling a separate
publication of online corporate data, especially internal-only and nearly identical function in its own portlet package. This
information. If Oracle Portal could be used consistently to duplication of code will result in a higher risk of introducing
gain these advantages, then the use of Oracle Portal can in new bugs into the framework, a greater number of execution
most cases be financially justified. In fact, such a portal paths during unit testing, and the likely repetition of effort
methodology could serve as more than just an architectural when modifications are needed in the future.
framework for Web content. With the proper use of Oracle Using the traditional PDK approach, each of the
databases, most if not all of that content could be centrally three portlets would need its own package, containing
controlled and dynamically created. Such an approach subprograms that register, de-register, and show the
would bypass the costs, complexity, and structuring issues particular portlet, return its properties, and determine
associated with relying upon a large number of directories whether it’s runnable. An example of this is the PDK
and HTML files—as well as Perl and Unix shell scripts, if sample portlet found in PDK\PLSQL\sample\param_
such are utilized. More important, the
content is dynamically generated; Listing 2. Portlet function get_portlet_info() duplicated for every portlet.
hence, the Web pages can easily
FUNCTION get_portlet_info(
display the new content as soon as it is p_provider_id IN INTEGER,
refreshed in the Oracle database. p_language IN VARCHAR2
)
From a source code perspective, RETURN wwpro_api_provider.portlet_record
IS
one advantage of Oracle Portal portlet wwpro_api_provider.portlet_record;
software is that the code can be BEGIN
portlet.id := provider_before.PORTLET_1;
organized in PL/SQL packages. It’s portlet.provider_id := p_provider_id;
portlet.name := 'Portlet_1';
critical to note that the default structure portlet.title := 'Portlet One';
of the current version of the PDK is to portlet.description := 'First portlet';
portlet.image_url := NULL;
create a unique package for every portlet.thumbnail_image_url := NULL;
portlet.help_url := NULL;
portlet. An example of this is the PDK portlet.timeout := NULL;
sample provider found in PDK\ portlet.timeout_msg := NULL;
portlet.implementation_style := NULL;
PLSQL\sample\sample_provider.pkb portlet.implementation_owner := NULL;
portlet.implementation_name := NULL;
(within the PDK installation archive portlet.content_type := wwpro_api_provider.CONTENT_TYPE_HTML;
file). Throughout this provider code, portlet.api_version := wwpro_api_provider.API_VERSION_1;
portlet.has_show_edit := FALSE;
every call to a portlet subprogram is portlet.has_show_edit_defaults := FALSE;
duplicated for each individual portlet. portlet.has_show_preview := FALSE;
portlet.call_is_runnable := NULL;
This is illustrated in the PL/SQL portlet.call_get_portlet := NULL;
portlet.accept_content_type := NULL;
fragment shown in Listing 1, which portlet.has_show_link_mode := NULL;
uses the same approach as the sample portlet.language := wwnls_api.AMERICAN;
portlet.preference_store_path := NULL;
PDK code. (To maximize readability, all portlet.created_on := SYSDATE;
portlet.created_by := wwctx_api.get_user;
of the code for this article was written portlet.last_updated_on := SYSDATE;
from scratch.) This code would be portlet.last_updated_by := wwctx_api.get_user;
RETURN portlet;
located in subprograms defined in the END get_portlet_info;
a shame to replace the proliferation of HTML files with the portlets(PORTLET_2).portlet_id := PORTLET_2;
portlets(PORTLET_2).portlet_name := 'Portlet_2';
equally costly proliferation of Portal packages. But this is portlets(PORTLET_2).portlet_title := 'Portlet Two';
precisely what happens with the default PDK approach. portlets(PORTLET_2).portlet_description := 'Second portlet';
portlets(PORTLET_3).portlet_id := PORTLET_3;
Restructuring the provider and portlet code portlets(PORTLET_3).portlet_name := 'Portlet_3';
portlets(PORTLET_3).portlet_title := 'Portlet Three';
To avoid the creation of excessive packages, we extract the portlets(PORTLET_3).portlet_description := 'Third portlet';
redundant code, thus allowing almost
all of the portlet subprograms to be free Listing 5. Generalized portlet function get_portlet_info().
of references to individual portlets.
FUNCTION get_portlet_info(
Instead, they can handle all of the p_provider_id IN INTEGER,
provider’s portlets generically. This p_portlet_id IN INTEGER,
p_language IN VARCHAR2
approach involves defining and using )
RETURN wwpro_api_provider.portlet_record
an index-by table to store all of the IS
portlet-specific information, thereby portlet wwpro_api_provider.portlet_record;
BEGIN
allowing the processing of multiple portlet.id := p_portlet_id;
portlet.provider_id := p_provider_id;
portlets in a single package. As before, portlet.name :=
the portlet IDs are defined as package provider_after.portlets(p_portlet_id).portlet_name;
portlet.title :=
constants. The code in Listing 3 shows provider_after.portlets(p_portlet_id).portlet_title;
portlet.description :=
the definition of such a portlet index- provider_after.portlets(p_portlet_id).portlet_description;
by table and the portlet IDs. This portlet.image_url := NULL;
portlet.thumbnail_image_url := NULL;
would be done in the provider’s portlet.help_url := NULL;
portlet.timeout := NULL;
package specification file, named, for portlet.timeout_msg := NULL;
instance, provider_after.pks. portlet.implementation_style := NULL;
portlet.implementation_owner := NULL;
The portlet index-by table is portlet.implementation_name := NULL;
portlet.content_type := wwpro_api_provider.CONTENT_TYPE_HTML;
initialized in the global section of the portlet.api_version := wwpro_api_provider.API_VERSION_1;
provider’s package body (in portlet.has_show_edit :=
provider_after.portlets(p_portlet_id).has_show_edit;
provider_after.pkb), as seen in the portlet.has_show_edit_defaults :=
provider_after.portlets(p_portlet_id).has_show_edit_defaults;
block of code shown in Listing 4. portlet.has_show_preview :=
The redesigned get_portlet_info() provider_after.portlets(p_portlet_id).has_show_preview;
portlet.call_is_runnable := NULL;
function is illustrated in Listing 5. It portlet.call_get_portlet := NULL;
portlet.accept_content_type := NULL;
would be in the package body file portlet.has_show_link_mode := NULL;
containing all the portlets, named, portlet.language := p_language;
portlet.preference_store_path := NULL;
in this example, portlets_after.pkb. portlet.created_on := SYSDATE;
portlet.created_by := wwctx_api.get_user;
Its function signature has one portlet.last_updated_on := SYSDATE;
modification: The portlet ID is now portlet.last_updated_by := wwctx_api.get_user;
RETURN portlet;
passed to the function, in addition to END get_portlet_info;
I
T’S quite possible that you’ll never need to use PLL required when calling them, and so on. From here on they
libraries. But, as your Forms development experience can be used pretty much as you would a regular built-in
continues and you start to develop more and more Forms function or procedure, as shown in the following
complex applications, it’s very likely that at some point the snippet that calls a PLL library procedure:
built-in Forms functions and procedures just won’t solve
the particular problem you happen to be working on. Or if BEGIN
…
they do, you might find that it involves greater complexity
/* pause for 5 seconds */
than you’d really like to implement. As a simple example, win_api_utility.sleep(5000);
consider a scenario where you want to suspend the running
…
of your Forms application for, say, five seconds before END;
continuing. At first sight you might turn to the PAUSE
built-in procedure, but that depends on the user pressing Can I edit PLL libraries?
a key in order to get things moving again. Another option You’ll find that the attached libraries are read-only and
would be to use a timer or an external program call, but therefore you won’t be able to edit them directly from within
that involves extra programming and complexity. Wouldn’t the Forms Builder application. However, there’s a way
it be great if someone had already written such a utility around this. Forms Builder has a file convert utility that
that you could simply link in with your application and call enables you to convert .PLL (library) files to their .PLD (text)
as required? That’s where PLL libraries come to the rescue. equivalents and vice versa. You’ll find this utility under the
In this particular example, the one we’re interested in is File | Administration | Convert… menu. Click on this and a
called D2KWUTIL.PLL. This library contains a whole host dialog box appears with three text fields to fill in. In the Type
of useful little Windows utilities, including one called text field, choose PL/SQL Library from the dropdown list. In
Win_Api_Utility.Sleep. This procedure takes as an argument the File text field, either type the name of or browse for the
the number of milliseconds for which the application is to PLL library file you wish to convert. In the Direction text
sleep—exactly what’s required. field, choose Binary To Text from the dropdown list. When
you now hit the Convert button, you should see that a .PLD
How do I use PLL libraries? file appears in the relevant directory. This file contains the
Before you can use the procedures and functions contained source code for the library and can be opened by a regular
in a PLL library, you have to attach it to your Forms module. text editor such as Notepad. If desired, you can now change
First, open a Forms module in the Object Navigator window, the functionality of the existing modules within the library
and then click on the Attached Libraries node and select the or even add new ones or delete old ones. I’d urge you,
Navigator | Create menu item. Next, you specify the name however, not to edit or change the original .PLL library files
and procedures code into this file and convert it to a .PLL BEGIN
library using the File | Administration | Convert… menu. ret_val :=
f50Write.WRITE_BLOCK(:system.current_block,
Now you can attach your newly created PLL library to your 'output.csv','w',TRUE,',','ALL',FALSE);
If ret_val < 0
Forms applications and make use of the procedures and Then
functions contained within it. Message('Write block failed,
please contact support');
End if;
A real-life example END;
To pull all the threads of this topic together, I’m going to take
you through the steps of a real-life example that I worked on I then incorporated this code as part of a menu
not long after I first started using Forms as a development icon, which made it accessible by every Forms module,
environment. The basic requirement was to allow the users meaning that it could be used to output any database
of the system the ability to dump the contents of multi- block in the entire application to a file. I think that’s pretty
record database blocks into a text file. When I looked into powerful stuff.
this I discovered that there were many ways to achieve this. Last but not least, you should be aware of a couple of
I could have used an ordinary SQL*Plus script, PL/SQL extra points. First, a useful side effect of being able to specify
with the TEXT_IO package, or created an external program the separator character is that if you make this a comma, you
such as embedded C. However, I happened across PLL instantly have a comma-separated file, which is great if you
libraries, and when I looked into them further I came across want to subsequently read the file into a spreadsheet like
a function in the F50UTIL library (I was using Forms version Excel. Second, the write_block function works for both
5 at the time) called f50write.write_block. This did exactly single-record and multi-record blocks, but only for database
what I required—namely, output a block of data to a file. blocks. In my own particular circumstance I needed it to
The function write_block is defined in the library thus: work for both data and control blocks; therefore, I had to
modify the code and use this as a model for my own block-
function write_block to-file function. I did this by first converting the F50Write
(
block_name in varchar2, PLL library to its PLD equivalent using the process I
output_file in varchar2 := 'output.lis',
output_mode in varchar2 := 'W', described previously. Next, I copied and pasted the
column_align in boolean := TRUE, write_block function into my own function and renamed it
sep_char in varchar2 := ' ',
rec_option in varchar2 := 'ALL', block_to_file. I then changed my new block_to_file function
displayed_only in boolean := FALSE
) return number;
as required and incorporated it into my Form module by
creating a new Program Unit and defining my block_to_file
where: function in the normal way.
• block_name is the name of the block to write. PLL library code compatibility
• output_file is the name of the output file. The default file The code shown in this article should work unchanged
name is ‘output.lis’. under all versions of Oracle 7 and 8 using Developer 2000/
• output_mode determines how the file is written to. The Forms 5. My understanding is that Forms6i was the last
valid values are ‘w’ for write and ‘a’ for append. version of Forms with which PLL libraries were fully
• column_align is a Boolean indicating whether the output integrated. Forms6i developers need to amend the code
should be column-aligned. snippets shown here to change the references F50UTIL and
• sep_char is the character(s) to use as column separators. f50write.write_block to F60UTIL and f60write.write_block,
The default value is a space character. respectively. Certain PLL libraries such as D2KWUTIL,
• rec_option specifies the records to write from the block. which are client/server-based, are unsupported in
Valid values are ‘ALL’, ‘VIEWED’, and ‘VISIBLE’. Developer9i, which is a totally Web-centric environment
• displayed_only is a Boolean indicating whether to write and may not work as expected, if at all.
only the displayed items or all of the items whether
they’re visible or not. The default value is FALSE, Summary
indicating that all items should be written. In this article I’ve shown you how you can enhance the
Oracle Portal Packages... easier to maintain and extend in the future. The benefits of
this approach are most evident when a new portlet needs to
Continued from page 13 be added to the portal. ▲
Conclusion
Michael J. Ross is a database software developer in San Diego. He’s also
Before development teams begin writing Oracle Portal code
the communications director and Webmaster of the San Diego Oracle
for their companies’ private and public Web sites, it’s critical
Users Group. www.ross.ws.
that they select in advance—or at least evolve toward—a
methodology that will avoid the unnecessary creation of Tom Scott is the owner of Scott Consulting, Inc., a San Diego-based
portlet packages. The design outlined in this article will consulting firm that specializes in Oracle design and implementation for
avoid individual packages for each portlet and minimize a variety of industries. He’s also the president of the San Diego Oracle
code overhead. In turn, the resulting Portal software will be Users Group. tom-s@pacbell.net.
For access to all current and archive content and source code, log in at
www.oracleprofessionalnewsletter.com with your unique subscriber user name and password. User name cement
For access to this issue’s Downloads only, click on the “Source Code” button, select the file(s)
you want from this issue, and enter the User name and Password at right when prompted. Password denim
Advertising: HowardF@Ragan.com Oracle, Oracle 8i, Oracle 9i, PL/SQL, and SQL*Plus are trademarks or registered trademarks of
Oracle Corporation. Other brand and product names are trademarks or registered trademarks
Pinnacle Web Site: www.pinnaclepublishing.com of their respective holders. Oracle Professional is an independent publication not affiliated
with Oracle Corporation. Oracle Corporation is not responsible in any way for the editorial
policy or other contents of the publication.
Subscription rates
This publication is intended as a general guide. It covers a highly technical and complex
subject and should not be used for making decisions concerning specific products or
United States: One year (12 issues): $229; two years (24 issues): $389 applications. This publication is sold as is, without warranty of any kind, either express or
Canada:* One year: $249; two years: $423 implied, respecting the contents of this publication, including but not limited to implied
warranties for the publication, performance, quality, merchantability, or fitness for any particular
Other:* One year: $254; two years: $432 purpose. Lawrence Ragan Communications, Inc., shall not be liable to the purchaser or any
other person or entity with respect to any liability, loss, or damage caused or alleged to be
Single issue rate: caused directly or indirectly by this publication. Articles published in Oracle Professional
$27.50 ($30 in Canada; $32.50 outside North America)* reflect the views of their authors; they may or may not reflect the view of Lawrence Ragan
Communications, Inc. Inclusion of advertising inserts does not constitute an endorsement by
* Funds must be in U.S. currency. Lawrence Ragan Communications, Inc., or Oracle Professional.