\section{A Complete DBPL Programming Example}

DBPL inherits from Modula-2 linguistic support for the modular
development of complex software systems that meet todays engineering
demands. This section demonstrates the use of DBPL for the
implementation of a larger application and illustrates a systematic
approach to database application modularization that turns out to be
advantageous also for a wider range of database programs.

The module concept of DBPL is also a key to the type-safe integration of
external databases and external libraries developed using other
programming languages into DBPL applications, two features that are also
demonstrated by examples in this section.

\subsection{DBPLXref: A Database for Module, Name and Type Dependency
Management}

The task of the example application \kw{DBPLXref} is to maintain an
online module cross reference database that stores information about
  \begin{itemize}
  \item compiled DBPL module definitions, module implementations and
        application programs;
  \item identifiers declared in module definitions (transactions, procedures,
        types, database variables, etc.);
  \item module import dependencies and
  \item identifier dependencies (e.g., variables depend on their
        types, procedures depend on their parameter types).
  \end{itemize}

This task is accomplished by a small extension to the DBPL compiler to
store all identifier information extracted during declaration and body
analysis in a DBPL database. The raw data stored in this system-wide
database is made available to interactive users by a form-based
interface that supports associative and navigational access. Finally,
there is a small utility program to remove all identifier information
from the database.

The following screen dumps give an idea of the externally visible
functionality of the interactive part of the application.

After the compiled and linked DBPL application \kw{dbxref} has been
started from a shell, an initial empty entry form for identifier descriptors
appears on the screen (see Fig.~\ref{ScrDump1}). Question marks denote
``don't care'' values and all other field descriptors describe a
conjunctive query (e.g., ``display all type identifiers in file
\kw{DBXRefType.def}''). If the user confirms his input, a scrollable list
of all matching identifiers appears in a new overlapping window (see
leftmost window in Fig.~\ref{ScrDump2}).

\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{entry.ps}
\caption{Associative access to identifiers based on their attributes}
\label{ScrDump1}
\end{center}
\end{figure}

The user can then browse through the list, return to the initial form to
re-specify the query, or pick an identifier. If an identifier is
selected, additional information is displayed in a newly created form
and a pop-up scroll-menu appears on the screen (see Fig.~\ref{ScrDump2}).
In the example given, details of a type identifier named \kw{IdentRelT},
declared on line 49 in module \kw{DBXRefType} are displayed.  The
options of the scroll-menu depend on the kind of an identifier. The
possible options for a type identifier include the display of all type
identifers on which it depend, the display of all its occurences in
definition or implementation modules, and the display of the source text
of its declaration.

\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{selected.ps}
\caption{Displaying details about an identifier chosen from a list}
\label{ScrDump2}
\end{center}
\end{figure}

\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{information.ps}
\caption{Listing the occurences of a type identifier in implementation modules}
\label{ScrDump3}
\end{center}
\end{figure}

Fig.~\ref{ScrDump3} shows the output generated for the selection of the
menu item ``Used in Implementation Module''. Again, the user can scroll
through the list of occurences and select a particular item. The
selction of an occurrence opens a new window that displays details of
the occurence.  Another scroll-menu allows the user, for example, to
display the context of the occurence or to issue a new associative query
in a separate form.

The general idea of the program is therefore to provide easy
navigational aids to analyse call, type, and binding dependencies in
large DBPL programs composed of a multitude of modules. A particulary
useful feature is the stack of past query results displayed as a stack
of scroll lists that can be re-activated by an ``exit'' option available
in every menu.

Some care has to be taken in the implementation of the \kw{DBPLXref}
application to uniquely identify modules in the file systems of multiple
workstations (avoiding name clashes between identical module names) and
to control the interaction between concurrent readers (interactive
users) and writers (compilation processes).


\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{DBXRef-Architecture.ps}
\caption{The module structure of the DBPL example application}
\label{DBPL-Arch}
\end{center}
\end{figure}

Fig.~\ref{DBPL-Arch} depicts the final architecture of the DBPL
application program and all its import relationships.
The appplication is divided into seven components that define
  \begin{itemize}
   \item a global DBPL database: \kw{DBXRef}, \kw{DBXRefType}
   \item a procedural interface to the DBPL compiler: \kw{DBXRefO}
   \item a menu-driven text window interface for browsing: \kw{DBXRefMenu}
   \item two stand-alone main programs: \kw{dbplxref}, \kw{dbplxrefclean}
   \item the DBPL compiler itself: \kw{dbplc} plus many additional
   modules 
  \end{itemize}
Shaded boxes in Fig.~\ref{DBPL-Arch} correspond to implementation
modules and main programs whereas the module interfaces (called
definition modules in DBPL) are represented as white boxes.

The interested reader can find the full source code of the DBPL
application outlined in the following sections as part of the Sun and
VAX DBPL distribution.


\subsection{Global Type Declarations}

Type and constant declarations shared between all parts of the
application are factored-out into the module \kw{DBXRefType} that has
an empty implementation. 
{\small\begin{verbatim}
DEFINITION MODULE DBXRefType;

CONST 
  Maxstring     = 20;
  LongMaxstring = 256;
  UnknownID     = 0;  (* reserved ID value *)
  NotFoundID    = 1;  (* reserved ID value *)

  
TYPE 
  ID            = CARDINAL;
  TypeClass     = (basesc, setsc, pointersc, relationsc, procsc, enumsc,
                   subrangesc, arraysc, recordsc, predicatesc, dummyc);
  IdKind        = (modulesk, procsk, typesk, constsk, varsk, fieldsk, 
                   predicatesk, dummyk);  
  ModuleKind    = (defM, implM, mainM, standardM); 
  DeclKind      = (inmodule, inrecord, inenum);
  ShortString   = ARRAY[0..Maxstring-1] OF CHAR;
  LongString    = ARRAY[0..LongMaxstring-1] OF CHAR;

  Identifier    = RECORD
    ID          : ID;          (* system-generated unique key *)
    Name        : ShortString; (* identifier declared in a program or 'Anonymous' *)
    Anonymus    : BOOLEAN;     (* is this an anonymous identifier? *)
    Kind        : IdKind;      (* kind of this identifier *)
    Type        : TypeClass;   (* type if kind = typesk else dummyc *)
    DeclLine    : CARDINAL;    (* line number where identifier is declared *)
    DeclModule  : ID;          (* compilation unit where identifier is declared *)
    DeclKind    : DeclKind;    (* the kind of scope for this identifier *) 
    DeclName    : ID;          (* the name of the scope for this identifier *)
  END;
  IdentRelT     = RELATION ID OF Identifier;
     (* One entry per identifier [module, variable, type, constant, type, 
        selector ... record field] declared in a definition module. *)

  Module        = RECORD
    ModuleID    : ID;          
    Filename    : ShortString;  
    MKind       : ModuleKind;
  END;
  ModuleRelT    = RELATION ModuleID OF Module;
     (* One entry per separate compilation unit. *)

  DependsOn     = RECORD
    IdentID     : ID;
    DependsOnID : ID;
  END;
  DependsOnRelT = RELATION IdentID, DependsOnID OF DependsOn;
     (* Direct, i.e. non-transitive dependency: 
        - A module depends on the modules it imports
        - A base type declaration depends on its base type
        - A procedure, transaction, selector or constructor depends on 
          its parameter and result types
        - A variable depends on its type 
        - ... *)
 
   Occurence    = RECORD
     ID         : ID;       (* referenced non-module identifier *)
     ModuleID   : ID;       (* used in this module *)
     Line       : CARDINAL; (* line number in ModuleID where ID is used *)
   END;
   OccurRelT    = RELATION ID, ModuleID, Line OF Occurence;
     (* One entry per utilization(!) of a non-module identifier in a 
        definition module. The declaration point is only stored 
        in IdentifierRel. *)
END DBXRefType.
\end{verbatim}}

This example demonstrates the preciseness achievable for database domain
definitions in DBPL by the heavy use of enumeration and subrange types.
The module essentially exports four relation types (\kw{IdentifierRelT},
\kw{ModuleRelT}, \kw{DependsOnRelT}, \kw{OccurenceRelT}) with their
associated element types that are used by clients of this module for
record constructors and argument type definitions.

The type \kw{ID} denotes internal, system-generated unique identifiers
associated with every identifier declaration that is inserted by the
compiler into the database. For example, there may be many declarations
of a variable \kw{i}, but each occurence of a variable \kw{i} is
assigned the unique \kw{ID} value of its matching declaration. Modules
are also uniquely identified by the \kw{ID} of their module identifier.
Given the absolute file name of a module or the \kw{ID} of an enclosing
scope (module, record, enumeration type) it is possible to determine the
\kw{ID} of any DBPL identifier.

\subsection{Persistent Variable Declarations}

The declaration of the database variables is localized in a single
module, \kw{DBXRef}, that exports relation variables and a single
\kw{CARDINAL} variable that holds the last identifier value issued.
Furthermore, there is a transaction \kw{InitDB} that is automatically
called during the initialization of the persistent module but that can
be also used to reset the database state at arbitrary points in time.
{\small\begin{verbatim}
DATABASE DEFINITION MODULE DBXRef;
IMPORT DBXRefType;

VAR  (* persistent database variables *)
  IdentRel        : DBXRefType.IdentRelT;
  ModuleRel       : DBXRefType.ModuleRelT;
  DependsOnRel    : DBXRefType.DependsOnRelT;
  DefModOccurRel, 
  ImpModOccurRel  : DBXRefType.OccurRelT;
  IdentCt         : DBXRefType.ID;   (* highest ID key issued up to now. *)

  TRANSACTION InitDB;

END DBXRef.
\end{verbatim}}
In general, it is a bad idea to allow access to database variables by
arbitrary clients since they could violate the database integrity. Most of
the integrity constraints of the example can be expressed by quantified
DBPL expressions:
{\small\begin{verbatim}
  ALL i IN IdentRel 
     (SOME m IN ModuleRel (i.DeclModule = m.ModuleID) AND 
      SOME n IN IdentRel (i.DeclName = n.ID))
  
  ALL m IN ModuleRel
     (SOME i IN IdentRel (m.ModuleID = i.ID) AND (i.kind = modulesk))

  ALL d IN DependsOnRel
     (SOME id IN IdentRel (d.IdentID = id.ID) AND
     (SOME dep IN IdentRel (d.DependsOnID = dep.ID))

  ALL o IN ImpModOccurRel
     (SOME id IN IdentRel ((o.ID = id.ID) AND 
                           NOT SOME m IN ModuleRel (id.ID = m.ModuleID)) AND
     (SOME mod IN ModuleRel (o.ModuleID = mod.ModuleID))
     
  ALL o IN DefModOccurRel
     (SOME id IN IdentRel ((o.ID = id.ID) AND 
                           NOT SOME m IN ModuleRel (id.ID = m.ModuleID)) AND
     (SOME mod IN ModuleRel (o.ModuleID = mod.ModuleID))

  ALL i IN IdentRel (i.ID <= IdentCt)
\end{verbatim}}
As discussed in \cite{ScMa91a,ScMa90a}, it is possible in DBPL to only
export restricted \e{views} on database variables that automatically
enforce such predicatively specified constraints. Alternatively, one can
export read-only views and perform all database updates through verified
transactions \cite{SWBM89}. 


\subsection{Encapsulation via Transactions and Procedures}

Modifications to the database variables declared in the previous section
are limited to the body of seven procedures exported by the module
\kw{DBXRefO}.  This module and the type module \kw{DBXRefType} are the
only parts of the \kw{DBPLXRef} application visible to the compiler:
{\small\begin{verbatim}
DEFINITION MODULE DBXRefO;
IMPORT DBXRefType;

PROCEDURE InsertIdent(VAR ident      : DBXRefType.Identifier;     (* out *)
                          name       : ARRAY OF CHAR;
                          declModule : DBXRefType.ID;
                          typeClass  : DBXRefType.TypeClass;
                          anonymus   : BOOLEAN;
                          kind       : DBXRefType.IdKind;
                          declLine   : CARDINAL;
                          declName   : DBXRefType.ID;
                          declKind   : DBXRefType.DeclKind);

PROCEDURE InsertModule(VAR module    : DBXRefType.Module;         (* out *)
                           modName   : ARRAY OF CHAR;
                           mKind     : DBXRefType.ModuleKind;
                           filename  : ARRAY OF CHAR);

PROCEDURE InsertDependsOn(VAR dependsOn   : DBXRefType.DependsOn; (* out *)
                              identID     : DBXRefType.ID;
                              dependsOnID : DBXRefType.ID); 

PROCEDURE InsertDefOccurence(VAR occurence: DBXRefType.Occurence; (* out *)
                                 identId  : DBXRefType.ID;
                                 moduleId : DBXRefType.ID;
                                 line     : CARDINAL);

PROCEDURE InsertImpOccurence(VAR occurence: DBXRefType.Occurence; (* out *)
                                 identId  : DBXRefType.ID;
                                 moduleId : DBXRefType.ID;
                                 line     : CARDINAL);

PROCEDURE DeleteDefModules(moduleId: DBXRefType.ID);
(* Delete all information about objects declared in this definition module and
   all (definition or implementation) modules transitively importing
   this module. *)

PROCEDURE DeleteImpModule(moduleId: DBXRefType.ID);
(* Delete all information about occurences and import dependencies of this
   implementation module. 
*)
END DBXRefO.
\end{verbatim}}
The insert transactions provide the necessary object attributes as value
parameters and return a newly created identifier via their first
variable parameter. This value may then used by the compiler to
establish further bindings to an object.

The deletion operations automatically preserve the integrity of the
database by removing all transitively dependent declarations. Their
implementations make heavy use of the declarative high-level access
abstractions provided by DBPL:
{\small\begin{verbatim}
PROCEDURE DeleteModule(moduleId: DBXRefType.ID);
  VAR lrelident: DBXRefType.IdentRelT;   (* a local relation variable *)
BEGIN
    (* Delete module from module and identifier relation: *)
    ModuleRel:- DBXRefType.ModuleRelT{ ModuleRel[moduleId] };
    IdentRel:- DBXRefType.IdentRelT{ IdentRel[moduleId] }; 

    (* Delete all dependencies from moduleId to imported objects: *)
    DependsOnRel:- DBXRefType.DependsOnRelT{
                     EACH d IN DependsOnRel: d.IdentID = moduleId};

    (* Determine the set of all locally declared identifiers: *)
    lrelident:= DBXRefType.IdentRelT{EACH i IN IdentRel: i.DeclModule = moduleId}; 
    (* Remove them: *)
    IdentRel:- lrelident;

    (* Remove dependencies referencing the locally declared identifiers: *)
    DependsOnRel:- DBXRefType.DependsOnRelT{EACH d IN DependsOnRel: 
          SOME l IN lrelident((d.IdentID = l.ID) OR (d.DependsOnID = l.ID))};

    (* Remove all occurencies within this module: *)
    DefModOccurRel:- DBXRefType.OccurRelT{
          EACH o IN DefModOccurRel : o.ModuleID = moduleId};
    ImpModOccurRel:- DBXRefType.OccurRelT{
          EACH o IN ImpModOccurRel : o.ModuleID = moduleId};
END DeleteModule; 

PROCEDURE DeleteDefModules(moduleId : DBXRefType.ID);
(* Delete all information about objects declared in this module and
   all modules transitively importing this module. *)
TYPE
  IdRelType = RELATION OF DBXRefType.ID;
VAR
  successors: IdRelType;
 
  CONSTRUCTOR Successors WITH (root : DBXRefType.ID) : IdRelType;
  BEGIN
    root,
    d.IdentID OF EACH d IN DependsOnRel:
            SOME succ IN IdRelType{Successors(root)} (d.DependsOnID = succ)
  END Successors;

BEGIN (* DeleteDefModules *) 
  (* Determine transitive successors of moduleId (incl. moduleId itself) *)
   successors:= IdRelType{Successors(moduleId)}; 
   FOR EACH succ IN successors : TRUE DO
     DeleteModule(succ);
   END;
END DeleteDefModules;

PROCEDURE DeleteImpModule(moduleId : DBXRefType.ID);
BEGIN
  DeleteModule(moduleId);
END DeleteImpModule;
\end{verbatim}}
Within the function \kw{DeleteDefModules}, the set of identifiers of
modules depending on a definition module is computed by a locally
declared recursive constructor \kw{Successors} that is parameterized by
the module identifier of the root module.

Since all of these procedures access persistent database variables, they
have to be called during a transaction execution (otherwise a runtime
error is raised). The compilation of a module \kw{X} initiates a
transaction that first deletes all information transitively referring to
a possibly existing ``old'' version of \kw{X} and then inserts
information about identifiers declared within \kw{X}. The enclosing
transaction guarantees that these operations appear (also for
transactions executing in parallel) to be executed as a single atomic
action.


\subsection{Building Form-Based User Interfaces}

A substantial amount of the programming effort in typical interactive
database applications is concerned with the management of user-friendly
interfaces to support easy, problem-oriented data retrieval.  To
alleviate this task, the DBPL standard library exports several modules
(\kw{Selector, Form, SetForm}) to handle repeating tasks like
menu-driven command input, form management and generic display routines
for set-structured data.

The following example shows how the menu of Fig.~\ref{ScrDump2} was created:
{\small\begin{verbatim}
ChooseText := "Depends on|Used in Def.Module|Used in Imp.Module|
              Show File|Respecify|Restart Transaction|Statistics";
s := Selector.Select(level+8,level+41, ChooseText);
CASE s OF
  | 0:  (* Exit key, return to where we came from *)
        status := back;
            EXIT;
  | 1: ... (* Print ``Depends on'' information *)
END;
\end{verbatim}}

An example for the use of the Form interface is the following procedure
that generates the form of Fig.~\ref{ScrDump1} by successive calls to
\kw{Form.xxxField} calls, handles user input (\kw{Form.Edit}), and
returns the typed input values via variable parameters to the caller.
The procedure \kw{Form.EnumField} uses the display string supplied as
its third parameter to translate between the internal and external data
representation.
{\small\begin{verbatim}
  PROCEDURE Inquire(VAR i,j,r : ShortString; VAR kind : IdKind;
                        x, y  : CARDINAL;    VAR f    : Form.T) : BOOLEAN;
  VAR z: CARDINAL;
  BEGIN
    Form.Message("Fill in what you know about the object of your interest!", FALSE); 
    i:="?";
    j:="?";
    kind := dummyk;
    r:= "?";
    f := Form.Create(x,y,"Inquired_Object");
    Form.ConstField(1, 1," IdName: ");
    Form.TextField(1, 14, i);
    Form.ConstField(2, 1," FileName: ");
    Form.TextField(2, 14, j);
    Form.ConstField(3, 1," Kind: ");
    Form.EnumField(3, 14, "module|proc|type|const|var|field|predicate|?",kind);
    Form.ConstField(4, 1," Record: ");
    Form.TextField(4, 14, r);
    Form.Display(f);
    RETURN (Form.Edit(f));
  END Inquire;
\end{verbatim}}

Because of the availability of generic set types in DBPL, the management
of scroll lists, repeating groups etc.\ is as simple as the handling of
forms for atomic data values. For example, the window of
Fig.~\ref{ScrDump3} is created as follows:
{\small\begin{verbatim}
PROCEDURE CreateModIdRelForm(VAR modIdRel : ModIdRel;
                             VAR modIdRec : ModIdRec;
                                 line,col : CARDINAL): SetForm.T;
(* Return Setform identifier for future display *) 
VAR header,body : Form.T;
BEGIN
  header := Form.Create(line,col,"Identifier in Module");
  Form.ConstField(0,0,"Name");
  Form.ConstField(0,20,"Module");
  Form.ConstField(0,40,"Kind");
  body := Form.Create(line,col,"");
  Form.TextField(0,0,modIdRec.Identname);
  Form.TextField(0,20,modIdRec.Modulename);
  Form.EnumField(0,40, KindText,modIdRec.Kind);
  RETURN SetForm.Create(header,body,modIdRec,modIdRel,15);
END CreateModIdRelForm;
\end{verbatim}}
The variable \kw{modIdRec} is used as a buffer to make the result of a
user selection immediately available for further processing.

The interaction between DBPL and so-called foreign language libraries is
exemplified by the module \kw{Subprocess} that is implemented using the
Unix library calls \kw{fork} and \kw{wait}. In the example application,
this module is used to spawn new editor subprocesses to display 
DBPL source code.
{\small\begin{verbatim}
DEFINITION MODULE Subprocess;
TYPE 
  ProcessId = INTEGER;
  String = ARRAY[0..79] OF CHAR;

PROCEDURE UnixCall(program: ARRAY OF CHAR; 
                   VAR arguments: ARRAY OF String): ProcessId;
(* Call program with these arguments. Each argument and the program
   has to end with a 0C. The program runs as a subprocess. 
*)

PROCEDURE Wait(VAR result : INTEGER) : ProcessId;
(* Wait for any subprocess to terminate. Return its process id and 
   completion status.
*)
END Subprocess.
\end{verbatim}}


\subsection{Database Access Optimization}

The following example gives an idea of the kind of queries that arise in
typical DBPL applications. The following query computes the result
displayed in Fig.~\ref{ScrDump2}, namely the name, source file name,
kind and scope identifier for each identifier declared in a file
specified by the value of a (local) variable \kw{File}, by taking the
the union of a two-way and a three-way join over the global database
relations:
{\small\begin{verbatim}
  GlobalRelT{{i.Name, m.Filename, i.Kind, empty, i.ID} OF
                EACH i IN IdentRel, EACH m IN ModuleRel:
                    (i.DeclModule = m.ModuleID) AND (m.Filename = File) 
                    AND (i.Kind <> fieldsk),

            {i.Name, m.Filename, i.Kind, decl.Name, i.ID} OF
                EACH i IN IdentRel, EACH m IN ModuleRel, EACH decl IN IdentRel:
                    (i.DeclModule = m.ModuleID) AND (m.Filename = File)
                    AND (i.Kind = fieldsk) AND (decl.ID = i.DeclName) };
\end{verbatim}}
The current version of the DBPL query optimizer will use indexed access
to the relations \kw{ModuleRel} and \kw{IdentRel} via their primary keys
and evaluate filters (like \kw{i.Kind \verb$<>$ fieldsk}) as early as possible.

Query optimization becomes even more crucial in the presence of
recursive queries, e.g.\ the following fixed-point computation of the set of
identifiers on which a given identifier \kw{root} transitively depends
on:
{\small\begin{verbatim}
CONSTRUCTOR NextModule WITH (root : ID) : IdRelT;
BEGIN
  root,
  d.IdentID OF EACH d IN DependsOnRel:
    SOME s IN IdRelT{NextModule(root)} (d.DependsOnID = s)
END NextModule;

modules := IdRelT{NextModule(Id)};
\end{verbatim}}

\subsection{Transparent Access to Commercial SQL Servers}

The current implementations of VAX DBPL and Sun DBPL provide gateways to
external Ingres databases (see also Sec.~\ref{Ingres}). The following
steps have to be taken to store identifier information (i.e.\ the
relation \kw{DBXRef.IdentRel}) in an Ingres database and not in a DBPL
database. First, an Ingres table has to be created that provides
attribute and domain declarations as required by the DBPL record type
\kw{DBXRefType.IdentRec}:
{\small\begin{verbatim}
create table identrel(
        id i4 not null not default,
        name c20 not null not default,
        anonymus i1 not null not default,
        kind i1 not null not default,
        type i1 not null not default,
        declline i4 not null not default,
        declmodule i4 not null not default,
        declkind i1 not null not default,
        declname c20 not null not default /* should be i4 */)
with noduplicates,location = (ii_database);
\end{verbatim}}
Next, the primary key constraint (as required by the type
\kw{DBXRefType.IdentRelT}) needs to be enforced in Ingres, for example,
{\small\begin{verbatim}
modify identrel to btree unique on
  id with nonleaffill = 80, leaffill = 70, fillfactor = 80;
\end{verbatim}}
The binding between the table \kw{identrel} in a SQL database \kw{xref}
and the variable \kw{IdentRel} in the module \kw{DBXRef} is established
by a form-based DBPL binding tool shown in Fig.~\ref{dbplbind}. This
tool also verifies the compatibility between the SQL table structure and
the DBPL type structure. Errors (e.g.\ the mismatch between the string
type for the SQL attribute \kw{declname} and the integer type for the
DBPL record field \kw{DeeclName}) are reported in a subwindow (see
Fig.~\ref{dbplbind}) and have to be removed before a binding can be
successfully established. 

Further access to the variable \kw{DBXRef.IdentRel} is fully
transparently handled by the Ingres database server. Since the coupling
between DBPL and Ingres is done at the level of set-oriented queries,
effective optimizations can be performed on the server side.
Furthermore, DBPL and Ingres objects can be freely mixed in DBPL
expressions (see also Sec.~\ref{Ingres}). In particular, parameterized
and recursive views can be defined on external SQL relations.

\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{bind.ps}
\caption{Binding of a DBPL database variable to an external SQL relation}
\label{dbplbind}
\end{center}
\end{figure}

Finally, Fig.~\ref{sqltable} shows that external DBPL relations can be
accessed using standard SQL statements, e.g.\ to grant access rights, to
define secondary indices and to define parameters that determine the
physical database schema.

\begin{figure}[htbf]
\begin{center}\leavevmode
\epsfxsize=\figwidth
\epsffile{sqltable.ps}
\caption{Browsing external DBPL databases using SQL ad-hoc queries}
\label{sqltable}
\end{center}
\end{figure}
